//
//编辑时间:2011-8-22
//整理人:紫色溟渊
//说明:仅供学习技术交流,欢迎转载,转载请注明出处。
//编辑目的:
// 1.加深自己对远线程注入的理解,
// 2.对VC内嵌汇编代码进一步了解
// 3.DEBUG版的函数会有堆栈检测,RELEASE版的函数没有堆栈检测代码
// 4.理解一下汇编代码对应的机器码
// 5.自己打算做一个易语言汇编模块
//
//汇编代码
PUBLIC ?AsmInject@@YAXXZ ; AsmInject
EXTRN __chkesp:NEAR
; COMDAT ?AsmInject@@YAXXZ
_TEXT SEGMENT
_dwAddr$ = -4
?AsmInject@@YAXXZ PROC NEAR ; AsmInject, COMDAT
; 8 : {
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 83 ec 44 sub esp, 68 ; 00000044H
00006 53 push ebx
00007 56 push esi
00008 57 push edi
00009 8d 7d bc lea edi, DWORD PTR [ebp-68]
0000c b9 11 00 00 00 mov ecx, 17 ; 00000011H
00011 b8 cc cc cc cc mov eax, -858993460 ; ccccccccH
00016 f3 ab rep stosd
; 9 : DWORD dwAddr =0x0060E8F0 ;
00018 c7 45 fc f0 e8
60 00 mov DWORD PTR _dwAddr$[ebp], 6351088 ; 0060e8f0H
; 10 : __asm
; 11 : {
; 12 : pushad
0001f 60 pushad
; 13 : mov ecx,0x80000003
00020 b9 03 00 00 80 mov ecx, -2147483645 ; 80000003H
; 14 : mov edx,[0x0AF8450]
00025 ba 50 84 af 00 mov edx, 11502672 ; 00af8450H
; 15 : push ecx
0002a 51 push ecx
; 16 : mov ecx,[edx +0x20]
0002b 8b 4a 20 mov ecx, DWORD PTR [edx+32]
; 17 : add ecx,0x0d4
0002e 81 c1 d4 00 00
00 add ecx, 212 ; 000000d4H
; 18 : call dwAddr
00034 ff 55 fc call DWORD PTR _dwAddr$[ebp]
; 19 : popad
00037 61 popad
; 20 : }
; 21 : }
00038 5f pop edi
00039 5e pop esi
0003a 5b pop ebx
0003b 83 c4 44 add esp, 68 ; 00000044H
0003e 3b ec cmp ebp, esp
00040 e8 00 00 00 00 call __chkesp
00045 8b e5 mov esp, ebp
00047 5d pop ebp
00048 c3 ret 0
?AsmInject@@YAXXZ ENDP ; AsmInject
_TEXT ENDS
END
//
#include "StdAfx.h"
#include "publicfunction.h"
//
//汇编代码注入
void AsmInject()
{
DWORD dwAddr =0x0060E8F0 ;
__asm
{
pushad
mov ecx,0x80000003
mov edx,[0x0AF8450]
push ecx
mov ecx,[edx +0x20]
add ecx,0x0d4
call dwAddr
popad
}
}
//
第一段代码是VC6.0生成的汇编代码和机器代码
第二段代码是我编写的C代码,内嵌汇编
场景介绍:我尝试这将这个函数AsmInject() 通过远线程注入到 TARGET.EXE(被攻击进程)
发现问题:为什么远线程注入到TARGET.EXE进程会导致TARGET.EXE出现错误?
解决办法:
void AsmInject()
{
DWORD dwAddr =0x0060E8F0 ;
__asm
{
pushad
mov ecx,0x80000003
mov edx,[0x0AF8450]
push ecx
mov ecx,[edx +0x20]
add ecx,0x0d4
call dwAddr
popad
}
}
在以上这个函数中 红色标记的代码 通过观察汇编代码之后和我们想象的并不一样,
; 14 : mov edx,[0x0AF8450]
00025 ba 50 84 af 00 mov edx, 11502672 ; 00af8450H
事实上它这里并不是把0x0AF8450里面的内容存放到edx,而是直接将0x0AF8450存放到edx中,和我们事先想象的好像不一样。
//
下面我做了一些改进
PUBLIC ?AsmInject_New@@YAXXZ ; AsmInject_New
; COMDAT ?AsmInject_New@@YAXXZ
_TEXT SEGMENT
_dwAddr$ = -4
?AsmInject_New@@YAXXZ PROC NEAR ; AsmInject_New, COMDAT
; 26 : {
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 83 ec 44 sub esp, 68 ; 00000044H
00006 53 push ebx
00007 56 push esi
00008 57 push edi
00009 8d 7d bc lea edi, DWORD PTR [ebp-68]
0000c b9 11 00 00 00 mov ecx, 17 ; 00000011H
00011 b8 cc cc cc cc mov eax, -858993460 ; ccccccccH
00016 f3 ab rep stosd
; 27 : DWORD dwAddr =0x0060E8F0 ;
00018 c7 45 fc f0 e8
60 00 mov DWORD PTR _dwAddr$[ebp], 6351088 ; 0060e8f0H
; 28 : __asm
; 29 : {
; 30 : pushad
0001f 60 pushad
; 31 : mov ecx,0x80000003
00020 b9 03 00 00 80 mov ecx, -2147483645 ; 80000003H
; 32 : mov edx,0x0AF8450
00025 ba 50 84 af 00 mov edx, 11502672 ; 00af8450H
; 33 : mov edx,[edx]
0002a 8b 12 mov edx, DWORD PTR [edx]
; 34 : push ecx
0002c 51 push ecx
; 35 : mov ecx,[edx +0x20]
0002d 8b 4a 20 mov ecx, DWORD PTR [edx+32]
; 36 : add ecx,0x0d4
00030 81 c1 d4 00 00
00 add ecx, 212 ; 000000d4H
; 37 : call dwAddr
00036 ff 55 fc call DWORD PTR _dwAddr$[ebp]
; 38 : popad
00039 61 popad
; 39 : }
; 40 : }
0003a 5f pop edi
0003b 5e pop esi
0003c 5b pop ebx
0003d 83 c4 44 add esp, 68 ; 00000044H
00040 3b ec cmp ebp, esp
00042 e8 00 00 00 00 call __chkesp
00047 8b e5 mov esp, ebp
00049 5d pop ebp
0004a c3 ret 0
?AsmInject_New@@YAXXZ ENDP ; AsmInject_New
_TEXT ENDS
END
//
//汇编代码注入 改进版
void AsmInject_New()
{
DWORD dwAddr =0x0060E8F0 ;
__asm
{
pushad
mov ecx,0x80000003
mov edx,0x0AF8450
mov edx,[edx]
push ecx
mov ecx,[edx +0x20]
add ecx,0x0d4
call dwAddr
popad
}
}
远线程注入改进版的汇编代码注入之后 ,可以看到TARGET.EXE进程确实存在效果,但是TARGET.EXE任然会报错,这又是为什么呢???
0003d 83 c4 44 add esp, 68 ; 00000044H
00040 3b ec cmp ebp, esp
00042 e8 00 00 00 00 call __chkesp
据说是DEBUG版本的堆栈检测机制,只有在DEBUG版本里面才会有,那么我们使用RELEASE版本编译试试看。
PUBLIC ?AsmInject_New@@YAXXZ ; AsmInject_New
; COMDAT ?AsmInject_New@@YAXXZ
_TEXT SEGMENT
_dwAddr$ = -4
?AsmInject_New@@YAXXZ PROC NEAR ; AsmInject_New, COMDAT
; 26 : {
00000 55 push ebp
00001 8b ec mov ebp, esp
00003 51 push ecx
00004 53 push ebx
00005 56 push esi
00006 57 push edi
; 27 : DWORD dwAddr =0x0060E8F0 ;
00007 c7 45 fc f0 e8
60 00 mov DWORD PTR _dwAddr$[ebp], 6351088 ; 0060e8f0H
; 28 : __asm
; 29 : {
; 30 : pushad
0000e 60 pushad
; 31 : mov ecx,0x80000003
0000f b9 03 00 00 80 mov ecx, -2147483645 ; 80000003H
; 32 : mov edx,0x0AF8450
00014 ba 50 84 af 00 mov edx, 11502672 ; 00af8450H
; 33 : mov edx,[edx]
00019 8b 12 mov edx, DWORD PTR [edx]
; 34 : push ecx
0001b 51 push ecx
; 35 : mov ecx,[edx +0x20]
0001c 8b 4a 20 mov ecx, DWORD PTR [edx+32]
; 36 : add ecx,0x0d4
0001f 81 c1 d4 00 00
00 add ecx, 212 ; 000000d4H
; 37 : call dwAddr
00025 ff 55 fc call DWORD PTR _dwAddr$[ebp]
; 38 : popad
00028 61 popad
; 39 : }
; 40 : }
00029 5f pop edi
0002a 5e pop esi
0002b 5b pop ebx
0002c 8b e5 mov esp, ebp
0002e 5d pop ebp
0002f c3 ret 0
?AsmInject_New@@YAXXZ ENDP ; AsmInject_New
_TEXT ENDS
END
//
我们远线程注入这段代码
//
//汇编代码注入
void CASMCodeInjectDlg::OnButtonAsmInject()
{
// TODO: Add your control notification handler code here
DWORD procid;
HWND hwnd ;
hwnd =::FindWindow(NULL ,"Element Client") ;
if (hwnd ==NULL)
{
MessageBox("请先运行游戏") ;
return ;
}
::GetWindowThreadProcessId(hwnd,&procid);//获得进程ID
HANDLE prochandle=::OpenProcess(PROCESS_ALL_ACCESS,false,procid);//打开进程
LPVOID baseaddr=VirtualAllocEx(prochandle,NULL,1024*4,MEM_COMMIT,PAGE_EXECUTE_READWRITE);//分配空间
if(baseaddr==NULL)
{
MessageBox("分配空间出错");
return;
}
if(!::WriteProcessMemory(prochandle,baseaddr,&AsmInject_New,1024*4,NULL))
{
MessageBox("将函数写内存出错");
return ;
}
HANDLE tid;
tid=CreateRemoteThread(prochandle,NULL,0,(LPTHREAD_START_ROUTINE)baseaddr,0,0,NULL);//远程调用函数
WaitForSingleObject(tid,INFINITE);//等待线程结束
VirtualFreeEx(prochandle,baseaddr,1024*4,MEM_RELEASE);
::CloseHandle(prochandle);
}
确实没有堆栈检测机制,然后我们再次注入这段代码,很好,TARGET.EXE并没有出现错误,一切都是如此的正常,如此的和谐。
//
如果我们直接向TARGET.EXE写入机器指令该怎么做呢?
//
//机器码函数注入
void CASMCodeInjectDlg::OnButtonMachinecodeInject()
{
// TODO: Add your control notification handler code here
DWORD procid;
HWND hwnd ;
hwnd =::FindWindow(NULL ,"Element Client") ;
if (hwnd ==NULL)
{
MessageBox("请先运行游戏") ;
return ;
}
::GetWindowThreadProcessId(hwnd,&procid);//获得进程ID
HANDLE prochandle=::OpenProcess(PROCESS_ALL_ACCESS,false,procid);//打开进程
LPVOID baseaddr=VirtualAllocEx(prochandle,NULL,1024*4,MEM_COMMIT,PAGE_EXECUTE_READWRITE);//分配空间
if(baseaddr==NULL)
{
MessageBox("分配空间出错");
return;
}
//
//待注入的机器指令
char cMachineCode[] ={
0x55,
0x8b, 0xec,
0x51,
0x53,
0x56,
0x57,
0xc7, 0x45, 0xfc, 0xf0, 0xe8, 0x60, 0x00,
0x60,
0xb9, 0x03, 0x00, 0x00, 0x80,
0xba, 0x50, 0x84, 0xaf, 0x00,
0x8b, 0x12,
0x51,
0x8b, 0x4a, 0x20,
0x81, 0xc1, 0xd4, 0x00, 0x00, 0x00,
0xff, 0x55, 0xfc,
0x61,
0x5f,
0x5e,
0x5b,
0x8b, 0xe5,
0x5d,
0xc3
} ;
if(!::WriteProcessMemory(prochandle,baseaddr,cMachineCode,1024*4,NULL))
{
MessageBox("将函数写内存出错");
return ;
}
HANDLE tid;
tid=CreateRemoteThread(prochandle,NULL,0,(LPTHREAD_START_ROUTINE)baseaddr,0,0,NULL);//远程调用函数
WaitForSingleObject(tid,INFINITE);//等待线程结束
VirtualFreeEx(prochandle,baseaddr,1024*4,MEM_RELEASE);
::CloseHandle(prochandle);
}
事实上,上面的机器码是直接copy AsmInject_New这个函数的机器码,没有做任何修改,把这些机器码存放到cMachineCode 缓冲区中。进行远线程注入依然可以行的,如此的和谐,TARGET.EXE也不会给我一个 READ MEMORY ERROR !
以上说了下远线程注入的细节问题。