易语言EIP注入

认识EIP

EIP是寄存器的一种,它和EBP、ESP都是为“栈”而生,用来存储CPU要读取指令的地址,CPU通过EIP寄存器读取即将要执行的指令。每次CPU执行完相应的汇编指令之后,EIP寄存器的值就会增加。EIP存储着下一条指令的地址,每执行一条指令,该寄存器变化一次。

CPU通过读取EIP寄存器执行汇编指令的大致过程如下:

1. 首先PE loader装载我们的PE文件,读取PE文件的基地址和入口RVA地址(相对于基地址的偏移),并且读取相应级表结构的值,然后将程序映射到内存;
2. 映射到内存以后,PE loader把返回的程序入口点给EIP寄存器赋值,然后通知CPU:我映射完了,你可以执行了。然后CPU通过将EIP的值传送到输入输出电路,并送入相应的地址总线上,再通过EIP寄存器读取EIP偏移地址中的二进制数据(通常为汇编指令),并传送到数据总线上,最后传送到指令缓冲区。
3. 传送到指令缓冲区后,EIP会自动的增加其读入指令的数量,以便往下执行。最后CPU执行控制器中地址指令缓冲区的指令并且往下执行。

CPU每次执行控制器读取完,相应的就再通过EIP寄存器去进行下一次的读取指令工作。每次CPU读取指令到指令缓冲区,相应的EIP寄存器的值增加,增加大小的就是读取指令的字节大小。

EIP注入原理

挂起目标进程,停止目标进程EIP的变换,在目标进程开启空间,然后把相关的指令机器码和数据拷贝到里面去,然后修改目标进程EIP使其强行跳转到我们拷贝进去的相关机器码位置,执行相关,然后跳转回来。下面的例子是实现DLL注入,
但是和平时说的远程代码注入在注入的逻辑上不同,但是同时都是用到了一个重要的结论就是:很多系统dll的导出函数地址在不同进程中,是一样的。

/* 思路
修改EID实现代码注入的汇编思路如下
SuspendThread();
get eip
push ad
push fd
push AddressDllFilePath
call LoadLibrary
pop fd
pop ad
jmp eip //这个是为了让程序执行完我们的代码之后自己跳转回去继续执行
ResumeThread();
*/

/*
注意两个问题
1.call 如果直接是个地址的话要这么计算
call RVA - call指令的下一条地址 RVA - (nowaddress + 5) //+5是因为 call dword 是长度5 1+4
2.jmp 直接跳转地址也是同理
jmp RVA - jmp指令的销一条地址 RVA - (nowaddress + 5) //+5是因为 jmp dword 长度是5 1+4
Tip
还有就是要知道,Kernel32.LoadLibraryW的地址不同进程是一样的,这样就可以直接得到相关RVA
*/

  1. #include "stdafx.h"
  2. #include <string>
  3. #include <windows.h>
  4. #include "AnalyzeAndRun.h"
  5. using namespace std;
  6.  
  7.  
  8. WCHAR pDllPath[] = L"C:\\TestDllMexxBoxX32.dll";              //被注入dll的路径(32位)
  9. VOID Test(){
  10. 	HWND hWnd=::FindWindow(NULL,L"AAA");                      //注入的线程对应窗体的title AAA,
  11. 	                                                          //主要就是为了获得tid 和 pid 这个地方可以对应修改,通过别的姿势获取。
  12. 	if(hWnd==NULL){
  13. 		MessageBox(NULL ,L"未获取窗口句柄!",L"失败",MB_OK);
  14. 		return;
  15. 	}
  16. 	DWORD pid,tid;
  17. 	tid=GetWindowThreadProcessId(hWnd,&pid);
  18. 	if(tid<=0){
  19. 		MessageBox(NULL ,L"未获取线程ID",L"失败" ,MB_OK);
  20. 		return;
  21. 	}
  22. 	if(pid<=0){
  23. 		MessageBox(NULL ,L"未获取进程ID",L"失败" ,MB_OK);
  24. 		return;
  25. 	}
  26. 	HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pid);
  27. 	if(hProcess <= 0){
  28. 		MessageBox(NULL ,L"未获取进程句柄",L"失败" ,MB_OK);
  29. 		return;
  30. 	}
  31. 	HANDLE hThread=OpenThread(THREAD_ALL_ACCESS,FALSE,tid);
  32. 	if(hThread <= 0){
  33. 		MessageBox(NULL ,L"未获取线程ID",L"失败" ,MB_OK);
  34. 		return;
  35. 	}
  36. 	SuspendThread(hThread);                         //挂起线程                     
  37.  
  38.  
  39. 	CONTEXT ct={0};
  40. 	ct.ContextFlags = CONTEXT_CONTROL;
  41. 	GetThreadContext(hThread,&ct);                 //获取,保存线程寄存器相关
  42.  
  43.  
  44. 	DWORD dwSize = sizeof(WCHAR)*1024;             //0-0x100 写代码 之后写数据
  45. 	BYTE *pProcessMem = (BYTE *)::VirtualAllocEx(hProcess,NULL,dwSize,MEM_COMMIT,PAGE_EXECUTE_READWRITE);
  46. 	DWORD dwWrited = 0;
  47. 	::WriteProcessMemory(hProcess, (pProcessMem + 0x100), pDllPath,   //先把路径(数据)写到内存里,从0x100开始
  48. 		(wcslen(pDllPath) + 1) * sizeof(WCHAR), &dwWrited);
  49.  
  50.  
  51.  
  52. 	FARPROC pLoadLibraryW = (FARPROC)::GetProcAddress(::GetModuleHandle(L"Kernel32"), "LoadLibraryW");
  53. 	BYTE ShellCode[32] = { 0 };
  54. 	DWORD *pdwAddr = NULL;
  55.  
  56.  
  57. 	ShellCode[0] = 0x60; // pushad
  58. 	ShellCode[1] = 0x9c; // pushfd
  59. 	ShellCode[2] = 0x68; // push
  60. 	pdwAddr = (DWORD *)&ShellCode[3]; // ShellCode[3/4/5/6]
  61. 	*pdwAddr = (DWORD)(pProcessMem + 0x100);
  62. 	ShellCode[7] = 0xe8;//call
  63. 	pdwAddr = (DWORD *)&ShellCode[8]; // ShellCode[8/9/10/11]
  64. 	*pdwAddr = (DWORD)pLoadLibraryW - ((DWORD)(pProcessMem + 7) + 5 );  // 因为直接call地址了,所以对应机器码需要转换,计算VA
  65. 	ShellCode[12] = 0x9d; // popfd
  66. 	ShellCode[13] = 0x61; // popad
  67. 	ShellCode[14] = 0xe9; // jmp
  68.  
  69.  
  70. 	pdwAddr = (DWORD *)&ShellCode[15]; // ShellCode[15/16/17/18]
  71. 	*pdwAddr = ct.Eip - ((DWORD)(pProcessMem + 14) + 5);  //因为直接jmp地址了,所以对应机器码需要转换,计算VA
  72. 	::WriteProcessMemory(hProcess, pProcessMem, ShellCode, sizeof(ShellCode), &dwWrited);
  73. 	ct.Eip = (DWORD)pProcessMem;
  74. 	::SetThreadContext(hThread, &ct);
  75.  
  76.  
  77. 	::ResumeThread(hThread);
  78. 	::CloseHandle(hProcess);
  79. 	::CloseHandle(hThread);
  80.  
  81.  
  82. }
  83. int _tmain(int argc, _TCHAR* argv[]){ 
  84. 	Test();
  85. 	return 0;
  86. }

易语言实现流程

我们把上述C++转换成易语言代码来实现,严格的说,我们是采用暂停线程来实现的

我们知道进程仅仅作为容器,真正执行代码的是线程,我们可以通过修改线程的执行,来让线程执行我们加载DLL的代码
设置线程的上下文我们可以通过Get/SetThreadContext实现.

总结:线程EIP注入的步骤
1.获取要注入的线程ID,进程ID
2.打开线程和进程,获得线程和进程句柄
3.把DLL路径写入游戏内存中
4.获取LoadLibary的地址
5.挂起线程
6.获取线程EIP
7.计算LoadCode
8.把LoadCode写入目标进程的内存中
9.设置线程EIP为LoadCode的地址
10.恢复线程运行
11.等待执行结束
12.释放内存并且关闭句柄

用到的自定义数据结构类型

线程信息

.版本 2

.数据类型 THREADENTRY32, 公开, 线程信息结构体
.成员 dwSize, 整数型, , , 结构大小,注意需要先初始化 28
.成员 cntUsage, 整数型, , , 引用线程数,不在使用,一般为0
.成员 th32ThreadID, 整数型, , , 线程ID
.成员 th32OwnerProcessID, 整数型, , , 父进程ID
.成员 tpBasePri, 整数型, , , 优先级
.成员 tpDeltaPri, 整数型, , , 更改的优先级,不在使用,一般为0
.成员 dwFlags, 整数型, , , 保留,不在使用,一般为0

线程环境

.版本 2

.数据类型 CONTEXT, 公开, CONTEXT结构,204 716字节
.成员 ContextFlags, 整数型, , , 环境标志
.成员 Dr0, 整数型, , , 调试寄存器
.成员 Dr1, 整数型, , , 调试寄存器
.成员 Dr2, 整数型, , , 调试寄存器
.成员 Dr3, 整数型, , , 调试寄存器
.成员 Dr6, 整数型, , , 调试寄存器
.成员 Dr7, 整数型, , , 调试寄存器
.成员 FloatSave, FLOATING_SAVE_AREA, , , 1C 28 CONTEXT_FLOATING_POINT=65544
.成员 GS, 整数型, , , 8C 140 SegGs CONTEXT_SEGMENTS=65540 GS,FS,ES,DS
.成员 FS, 整数型, , , 90 144 SegFs
.成员 ES, 整数型, , , 94 148 SegEs
.成员 DS, 整数型, , , 98 152 SegDs
.成员 EDI, 整数型, , , 9C 156 CONTEXT_INTEGER=65538 EDI,ESI,EBX,EDX,ECX,EAX
.成员 ESI, 整数型, , , A0 160
.成员 EBX, 整数型, , , A4 164
.成员 EDX, 整数型, , , A8 168
.成员 ECX, 整数型, , , AC 172
.成员 EAX, 整数型, , , B0 176
.成员 EBP, 整数型, , , B4 180 CONTEXT_CONTROL=65537 EBP,EIP,CS,标志位,ESP,SS
.成员 EIP, 整数型, , , B8 184
.成员 CS, 整数型, , , BC 188 SegCs=0 标志位=0
.成员 EFlags, 整数型, , , C0 192 EFlags CF=1,PF=3,AF=5,ZF=7,SF=8,TF=9,IF=10,DF=11,OF=12, 取位 BT_(标志,位数), 反位 BTC_(标志,位数), 置0 BTR_(), 置1 BTS_(标志,位数)
.成员 ESP, 整数型, , , C4 196
.成员 SS, 整数型, , , C8 200 SegSs
.成员 保留, 整数型, , , CC 208 禁止使用 扩展寄存器 整数型[128] CONTEXT_EXTENDED_REGISTERS=65568

浮点寄存器

.版本 2

.数据类型 FLOATING_SAVE_AREA, , 浮点寄存器结构
.成员 ControlWord, 整数型, , , 1C 28 控制代码
.成员 StatusWord, 整数型, , , 20 32 状态代码
.成员 TagWord, 整数型, , , 24 36 标记代码
.成员 ErrorOffset, 整数型, , , 28 40 错误偏移
.成员 ErrorSelector, 整数型, , , 2C 44 错误选择器
.成员 DataOffset, 整数型, , , 30 48 数据偏移
.成员 DataSelector, 整数型, , , 34 52 数据选择器
.成员 RegisterArea, 字节型, , "80"
.成员 Cr0NpxState, 整数型, , , 属于浮点数寄存器里的同步状态

模块信息结构体

.版本 2

.数据类型 MODULEENTRY32, 公开, 模块信息结构体
.成员 dwSize, 整数型, , , 结构大小
.成员 th32ModuleID, 整数型, , , 模块ID
.成员 th32ProcessID, 整数型, , , 进程ID
.成员 GlblcntUsage, 整数型, , , 没意义,模块使用计数1
.成员 ProccntUsage, 整数型, , , 没意义,模块使用计数2
.成员 modBaseAddr, 整数型, , , 模块基址
.成员 modBaseSize, 整数型, , , 模块大小
.成员 hModule, 整数型, , , 模块句柄
.成员 szModule, 字节型, , "256", 模块名
.成员 szExePath, 字节型, , "256", 模块路径


发布日期:

所属分类: 易语言 标签: