EXE加载模拟器直接在内存中加载运行EXE不通过API创建进程运行

nouveau

发布日期: 2019-01-02 09:47:41 浏览量: 2083
评分:
star star star star star star star star star star
*转载请注明来自write-bug.com

背景

在网上搜索了很多病毒木马的分析报告,看了一段时间后,发现还是有很多病毒木马都能够模拟PE加载器,把DLL或者是EXE等PE文件,直接从内存中直接加载到自己的内存中执行,不需要通过API函数去操作,以此躲过一些杀软的检测。

在看到这些技术的描述后,虽然没有详细的实现思路,但是凭借自己的知识积累,我也大概知道是怎么做了。后来,就自己动手写了这么一个程序,实现了从内存中直接加载并运行EXE,不需要通过API函数创建另一个进程启动该EXE。暂时还没有想清楚这种技术有什么积极的一面,不管了,既然都把程序写出来了,那就当作是对PE结构以及编程水平的一次锻炼吧

现在,把实现的思路和实现过程,写成文档,分享给大家。

程序实现原理

要想完全理解透彻这个程序的技术,需要对PE文件格式有比较详细的了解才行,起码要了解PE格式的导入表、导出表以及重定位表的具体操作过程。

这个程序和 “DLL加载模拟器直接在内存中加载DLL不通过API加载” 这篇文章原理是一样的,只是一个是EXE,一个是DLL,本质上都是PE文件加载模拟器。

EXE加载到内存的过程并运行实现原理

  • 首先,在EXE文件中,根据PE结构格式获取其加载映像的大小SizeOfImage,并根据SizeOfImage在自己的程序中申请一块可读、可写、可执行的内存,那么这块内存的首地址就是EXE程序的加载基址

  • 然后,根据EXE中的PE结构格式获取其映像对齐大小SectionAlignment,然后把EXE文件数据按照SectionAlignment对齐大小拷贝到上述申请的可读、可写、可执行的内存中

  • 接着,根据PE结构的重定位表,重新对重定位表进行修正

  • 接着,根据PE结构的导入表,加载所需的DLL,并获取导入表导入函数的地址并写入导入表中

  • 接着,修改EXE的加载基址ImageBase

  • 最后,根据PE结构获取EXE的入口地址AddressOfEntryPoint,然后跳转到入口地址处继续执行

这样,EXE就成功加载到程序中并运行起来了。要注意的一个问题就是,并不是所有的EXE都有重定位表,对于没有重定位表的EXE程序,那就不适用于本文介绍的方法。

编码实现

  1. // 模拟PE加载器加载内存EXE文件到进程中
  2. // lpData: 内存EXE文件数据的基址
  3. // dwSize: 内存EXE文件的内存大小
  4. // 返回值: 内存EXE加载到进程的加载基址
  5. LPVOID MmRunExe(LPVOID lpData, DWORD dwSize)
  6. {
  7. LPVOID lpBaseAddress = NULL;
  8. // 获取镜像大小
  9. DWORD dwSizeOfImage = GetSizeOfImage(lpData);
  10. // 在进程中开辟一个可读、可写、可执行的内存块
  11. lpBaseAddress = ::VirtualAlloc(NULL, dwSizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
  12. if (NULL == lpBaseAddress)
  13. {
  14. ShowError("VirtualAlloc");
  15. return NULL;
  16. }
  17. ::RtlZeroMemory(lpBaseAddress, dwSizeOfImage);
  18. // 将内存PE数据按SectionAlignment大小对齐映射到进程内存中
  19. if (FALSE == MmMapFile(lpData, lpBaseAddress))
  20. {
  21. ShowError("MmMapFile");
  22. return NULL;
  23. }
  24. // 修改PE文件重定位表信息
  25. if (FALSE == DoRelocationTable(lpBaseAddress))
  26. {
  27. ShowError("DoRelocationTable");
  28. return NULL;
  29. }
  30. // 填写PE文件导入表信息
  31. if (FALSE == DoImportTable(lpBaseAddress))
  32. {
  33. ShowError("DoImportTable");
  34. return NULL;
  35. }
  36. //修改页属性。应该根据每个页的属性单独设置其对应内存页的属性。
  37. //统一设置成一个属性PAGE_EXECUTE_READWRITE
  38. DWORD dwOldProtect = 0;
  39. if (FALSE == ::VirtualProtect(lpBaseAddress, dwSizeOfImage, PAGE_EXECUTE_READWRITE, &dwOldProtect))
  40. {
  41. ShowError("VirtualProtect");
  42. return NULL;
  43. }
  44. // 修改PE文件加载基址IMAGE_NT_HEADERS.OptionalHeader.ImageBase
  45. if (FALSE == SetImageBase(lpBaseAddress))
  46. {
  47. ShowError("SetImageBase");
  48. return NULL;
  49. }
  50. // 跳转到PE的入口点处执行, 函数地址即为PE文件的入口点IMAGE_NT_HEADERS.OptionalHeader.AddressOfEntryPoint
  51. if (FALSE == CallExeEntry(lpBaseAddress))
  52. {
  53. ShowError("CallExeEntry");
  54. return NULL;
  55. }
  56. return lpBaseAddress;
  57. }

程序测试

在 main 函数中调用上述封装好的函数,加载EXE程序进行测试。main 函数为:

  1. int _tmain(int argc, _TCHAR* argv[])
  2. {
  3. char szFileName[] = "KuaiZip_Setup_2.8.28.8.exe";
  4. // 打开EXE文件并获取EXE文件大小
  5. HANDLE hFile = CreateFile(szFileName, GENERIC_READ | GENERIC_WRITE,
  6. FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
  7. FILE_ATTRIBUTE_ARCHIVE, NULL);
  8. if (INVALID_HANDLE_VALUE == hFile)
  9. {
  10. ShowError("CreateFile");
  11. return 1;
  12. }
  13. DWORD dwFileSize = GetFileSize(hFile, NULL);
  14. // 申请动态内存并读取DLL到内存中
  15. BYTE *pData = new BYTE[dwFileSize];
  16. if (NULL == pData)
  17. {
  18. ShowError("new");
  19. return 2;
  20. }
  21. DWORD dwRet = 0;
  22. ReadFile(hFile, pData, dwFileSize, &dwRet, NULL);
  23. CloseHandle(hFile);
  24. // 判断有无重定位表
  25. if (FALSE == IsExistRelocationTable(pData))
  26. {
  27. printf("[FALSE] IsExistRelocationTable\n");
  28. system("pause");
  29. return 0;
  30. }
  31. // 将内存DLL加载到程序中
  32. LPVOID lpBaseAddress = MmRunExe(pData, dwFileSize);
  33. if (NULL == lpBaseAddress)
  34. {
  35. ShowError("MmRunExe");
  36. return 3;
  37. }
  38. system("pause");
  39. return 0;
  40. }

测试结果:

运行程序后,成功显示“快压”安装程序的对话框界面,而且还显示了“快压”安装程序正在向 “i.kpzip.com” 使用HTTP发送“GET”数据请求:

所以,程序测试成功。

总结

这个程序你只要熟悉PE格式结构的话,这个程序理解起来会比较容易。其中,需要特别注意的一点是:并不是所有的EXE都是用于本文介绍的方法。因为,对于那些没有重定位表的EXE程序来说,它们加载运行时的加载基址只能是固定的,因为没有重定位表的缘故,所以无法重定位数据,势必不能成功运行程序。同时,对一些MFC程序也不支持,目前正在改进当中。如果遇到不成功的,那就多换几个有重定位表的EXE试试就好。

参考

参考自《Windows黑客编程技术详解》一书

上传的附件 cloud_download RunExeInMem_Test.7z ( 148.13kb, 78次下载 )

keyboard_arrow_left上一篇 : 摄像头图像采集及拼接程序的实现 Minifilter驱动程序与用户层程序通信 : 下一篇keyboard_arrow_right



nouveau
2019-01-02 09:47:54
直接在内存中加载运行EXE不通过API创建进程运行
电子管
2019-01-10 08:29:11
直接在内存中加载运行EXE
流浪灬猫
2019-02-18 17:50:54
在VS中F5执行没有问题,但是直接双击生成的Debug\RunExeInMem_Test.exe会报错。
流浪灬猫
2019-02-18 17:58:40
应该是跟我这个测试用的exe有关系
Tommorow
2019-02-19 18:18:50
厉害啦
325
2019-03-07 17:16:19
弹出查找memcpy.asm怎么办 win10 vs2013
肯特
2019-03-16 23:59:37
不支持64位啊
o0ojin
2019-05-10 14:42:19
不错啊

发送私信

花花世界,终归空寂

8
文章数
17
评论数
最近文章
eject