前两节中介绍了通过远线程进行注入的方法。现在换一种方法——修改进程入口点。(转载请指明出处)

在PE文件中,其中有个字段标识程序入口点位置。我们通过这个字段,到达程序入口点。PE文件的结构我这儿不讨论(我会在之后写关于PE文件的介绍和研究),我只列出一些和程序入口点有关的数据结构

typedef struct _IMAGE_NT_HEADERS {DWORD Signature;IMAGE_FILE_HEADER FileHeader;IMAGE_OPTIONAL_HEADER32 OptionalHeader;
} IMAGE_NT_HEADERS32, *PIMAGE_NT_HEADERS32;
typedef struct _IMAGE_OPTIONAL_HEADER {//// Standard fields.//WORD    Magic;BYTE    MajorLinkerVersion;BYTE    MinorLinkerVersion;DWORD   SizeOfCode;DWORD   SizeOfInitializedData;DWORD   SizeOfUninitializedData;DWORD   AddressOfEntryPoint;DWORD   BaseOfCode;DWORD   BaseOfData;//// NT additional fields.//DWORD   ImageBase;……
}

其中ImageBase是程序加载的基址,AddressOfEntryPoint是代码执行的入口偏移。于是我们的程序入口点是

PIMAGE_DOS_HEADER lpstDosHeader = (PIMAGE_DOS_HEADER)(LPSTR)lpMapFile;
PIMAGE_NT_HEADERS lpstNtHeaders = (PIMAGE_NT_HEADERS)((LPSTR)lpMapFile + lpstDosHeader->e_lfanew );
dwPEEntry = lpstNtHeaders->OptionalHeader.AddressOfEntryPoint + lpstNtHeaders->OptionalHeader.ImageBase;

我们将从程序入口点开始搜索Call这个指令。这个地方存在一个经验,就是一般(如果没动手脚)我们代码第一个Call指令跟随的是一个函数偏移地址。我们用ollydbg打开mspaint.exe这个我们要注入的进程文件

01034BD7 > $  6A 70         push    70
01034BD9   .  68 00740001   push    01007400
01034BDE   .  E8 09040000   call    01034FEC

用IDA打开之,可以看到

public _wWinMainCRTStartup
.text:01034BD7 _wWinMainCRTStartup proc near
……
.text:01034BD7                 push    70h
.text:01034BD9                 push    offset stru_1007400
.text:01034BDE                 call    __SEH_prolog

这个特性很重要,我们在找到被注入进程的第一个call指令后,将之后的偏移量记下来,并计算出真实函数的起始地址。我们程序结束后再jmp到这个真实地址。当然不否认这个方案存在很大风险,比如第一call的不是偏移地址,而是一个寄存器中保存的地址,那么我们这个方案就挂了!
        我们得到第一个Call指令位置和Call的地址后,我们就可以考虑将我们的代码注入到傀儡中。因为我们这次要在代码中动态地修改注入的代码,于是我们需要使用ShellCode,毕竟汇编和01之间还是隔一层的。ShellCode也很好得到,我们写完汇编后,查看该处的16进制码即可。

/*$ ==>    >  60              pushad$+1      >  9C              pushfd$+2      >  68 11111111     push    11111111  //加载的dll名称$+7      >  E8 444288A5     call    LoadLibraryW  //LoadLibraryW地址$+C      >  50              push    eax$+D      >  E8 444288A5     call    FreeLibrary  //FreeLibrary地址$+12     >  9D              popfd$+13     >  61              popad$+14     >- E9 495399B6     jmp     33333333  //跳转到第一个call函数开始*/BYTE lpShellCode[] = {0x60,0x9c,0x68,0xcc,0xcc,0xcc,0xcc,0xe8,0xcc,0xcc,0xcc,0xcc,0x50,0xe8,0xcc,0xcc,0xcc,0xcc,0x9d,0x61,0xe9,0xcc,0xcc,0xcc,0xcc};

OK,此处我们预留了4个地址,分别是LoadLibrary加载的DLL的路径的地址,LoadLibrary 和FreeLibrary的地址,以及真实Call函数地址的在ShellCode中的偏移量。下步我们填充这些数据。

            DWORD dwCallAddrOffset = 0;if( FALSE == ReadProcessMemory( hProcess, lpFirstCallAddr, &dwCallAddrOffset, 4, NULL ) ) {break;}// 计算call的目标函数地址LPSTR lpRealFuncAddr = lpFirstCallAddr + 4 + dwCallAddrOffset;DWORD dwDllPathSize =  ( (DWORD) wcslen( lpDllPath ) ) * sizeof(WCHAR);LPVOID lpDllPathAddr = VirtualAllocEx( hProcess, NULL, dwDllPathSize, MEM_COMMIT, PAGE_READWRITE );if( NULL == lpDllPathAddr ) {break;}if( FALSE == WriteProcessMemory( hProcess, lpDllPathAddr, lpDllPath, dwDllPathSize, NULL) ) {break;}LPLoadLibrary lpFuncLoadLibraryAddr = LoadLibraryW;LPFreeLibrary lpFuncFreeLibraryAddr = FreeLibrary;if ( NULL == lpFuncLoadLibraryAddr || NULL == FreeLibrary ) {break;}size_t ShellCodeSize = strlen( (char*)lpShellCode ) + 1;LPVOID lpShellCodeAddr = VirtualAllocEx( hProcess, NULL, ShellCodeSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE );if ( NULL == lpShellCodeAddr  ) {break;}// 修改shellcode// 填充DLL路径memcpy( lpShellCode + 0x03, &lpDllPathAddr, 4 );// 填充LoadLibrary偏移DWORD dwCallLoadLibraryOffset = (*(LPDWORD)&lpFuncLoadLibraryAddr) -( (*(LPDWORD)&lpShellCodeAddr) + 0x0C );memcpy( lpShellCode + 0x08, &dwCallLoadLibraryOffset, 4 );// 填充FreeLibrary偏移DWORD dwCallFreeLibraryOffset = (*(LPDWORD)&lpFuncFreeLibraryAddr) - ( (*(LPDWORD)&lpShellCodeAddr) + 0x12 );memcpy( lpShellCode + 0x0E, &dwCallFreeLibraryOffset, 4 );// 填充返回地址偏移,即原来的Call地址DWORD dwRealFuncOffset = (*(LPDWORD)&lpRealFuncAddr) - ( (*(LPDWORD)&lpShellCodeAddr) + 0x19 );memcpy( lpShellCode + 0x15, &dwRealFuncOffset, 4  );// 写入shellcodeif( FALSE == WriteProcessMemory( hProcess, lpShellCodeAddr, lpShellCode, ShellCodeSize, NULL ) ) {break;}

在我们写入ShellCode后,我们可以拿到ShellCode的地址,然后我们算出它在第一个Call指令的偏移

            //计算call的新地址DWORD dwNewCallAddrOffset = (*(LPDWORD)&lpShellCodeAddr) - ((*(LPDWORD)&lpFirstCallAddr) + 4 );

之后就要做个非常重要的事情——将新Call地址写入已经载入内存中的傀儡代码中。因为一般PE文件的代码段的内存页是EXECUTE|READ,但是不具备WRITE属性。于是我们要将第一个Call指令所在的内存页的页属性改为PAGE_EXECUTE_READWRITE,我们写入新Call地址后,再将原来的页属性设置回去,善始善终。

            MEMORY_BASIC_INFORMATION stMemBasicInfor = {0};if ( FALSE == VirtualQueryEx( hProcess, lpFirstCallAddr, &stMemBasicInfor, sizeof(MEMORY_BASIC_INFORMATION) ) ){break;}DWORD dwOldProtect = 0;if ( FALSE == VirtualProtectEx( hProcess, stMemBasicInfor.BaseAddress, stMemBasicInfor.RegionSize, PAGE_EXECUTE_READWRITE, &dwOldProtect ) ){break;}if ( FALSE == WriteProcessMemory( hProcess, lpFirstCallAddr, &dwNewCallAddrOffset, 4, NULL ) ){break;}VirtualProtectEx( hProcess, stMemBasicInfor.BaseAddress, stMemBasicInfor.RegionSize, dwOldProtect, NULL );

最简单的修改程序入口点进行注入的方法就是如此。

方案我整理出的工程。
       (转载请指明出处)

VC下提前注入进程的一些方法3——修改程序入口点相关推荐

  1. VC下提前注入进程的一些方法2——远线程带参数

    在前一节中介绍了通过远线程不带参数的方式提前注入进程,现在介绍种远线程携带参数的方法.(转载请指明出处) 1.2 执行注入的进程需要传信息给被注入进程 因为同样采用的是远线程注入,所以大致的思路是一样 ...

  2. VC下提前注入进程的一些方法1——远线程不带参数

    前些天一直在研究Ring3层的提前注入问题.所谓提前注入,就是在程序代码逻辑还没执行前就注入,这样做一般用于Hook API.(转载请指明出处)自己写了个demo,在此记下. 我的demo使用了两种注 ...

  3. VC提前注入.net软件的方法

    在之前几节介绍了各种注入方法,但是这些方法存在一些缺陷--对.net程序注入无效.(转载请指明出处) 这个可以理解,.net程序的代码不是汇编,而是微软自定义的IL中间语言..net CLR如同虚拟机 ...

  4. 脱壳:OEP(即程序入口点)查找 --- 基本思路和常见方法

    OEP:程序的入口点,软件加壳就是隐藏了OEP(或者用了假的OEP), 只要我们找到程序真正的OEP,就可以立刻脱壳. PUSHAD (压栈) 代表程序的入口点, POPAD (出栈) 代表程序的出口 ...

  5. vc下禁止按钮连续点击的方法

    最近接手的项目需要(其实就是一个桌面小软件),需要通过MFC上的上一页和下一页按钮,控制CHTMLCTRL的显示.但CHTMLCTRL显示是有延迟的,一旦按钮点击过快,该控件会无法及时响应.所以,需要 ...

  6. 编写测试:VC下获取文件大小的4种方法

    代码参考自lailx的博客:获取文件大小的4种方法(http://www.cnblogs.com/lailx/archive/2011/11/20/2256550.html) 1 // TestGet ...

  7. 一种在注入进程中使用WTL创建无焦点不在任务栏出现“吸附”窗口的方法和思路

    最近一直在做沙箱项目,在项目快接近结尾的时候,我想给在我们沙箱中运行的程序界面打上一个标记--标识其在我们沙箱中运行的.我大致想法是:在被注入程序的顶层窗口上方显示一个"标题性"窗 ...

  8. 在linux下python爬虫进程发生异常时自动重启直至正常结束的方法

    在linux下python爬虫进程发生异常时自动重启直至正常结束的方法 参考文章: (1)在linux下python爬虫进程发生异常时自动重启直至正常结束的方法 (2)https://www.cnbl ...

  9. kill掉多个进程linux中的sudo,linux下批量kill进程的方法

    --kill某个用户下的所有进程(用户为test) --pkill # pkill -u test --killall # killall -u test --ps # ps -ef | grep t ...

最新文章

  1. hdu 4460 friend chains spfa 最短路里面的最长路
  2. VS2010属性表的建立与灵活运用
  3. 'gbk' codec can't decode byte 0x80 in position的一个解决办法
  4. 推荐几本对创业者很有用的书籍
  5. android拦截短信获取短信内容,《英雄联盟手游》先锋测试招募说明:仅安卓用户...
  6. 作者:魏凯(1981-),男,中国信息通信研究院移动互联网与大数据部高级工程师、副主任。...
  7. MQTT 连接服务端失败,报错客户机未连接(32104)
  8. 语音识别之——mfcc什么是汉明窗,为什么加汉明窗
  9. 2017最新xcode打包APP详细图文
  10. 基于云效Codeup一键恢复删库保护数据资源,程序员删库跑路不复存在
  11. 代码:批量图片格式转换(jpg-png)
  12. 【Android】解决aab上传Google Play后下载语言文件缺失的问题
  13. ggplot2 theme主题参数详解
  14. 如何领先 90% 的程序员?
  15. 怎样在VS2005中添加Flash控件
  16. 使用贝叶斯进行新闻分类
  17. Python爬虫-openlaw登陆JS加密破解
  18. 世界历史上10位征服过最广大土地的人
  19. iOS技术框架构和更新版本的技术特性
  20. springboot集成CAS单点登录客户端

热门文章

  1. 使用Python,OpenCV实现简单的场景边界/拍摄转换检测器
  2. 获取系统时间精确到毫秒级C++代码实现
  3. C/C++指向指针的指针
  4. Node.js实现本地客户端上传单个或者多个文件Excel文件(xls格式、xlsx格式文件)到服务器端,并且解析对应的Excel内容反馈到请求报文中
  5. 在CentOS 6.9 x86_64的OpenResty 1.13.6.1上使用LuaRocks示例
  6. 3D广告建模-C4D Octane渲染视频教程
  7. Linux负载均衡实现
  8. HTML学习笔记之基本介绍
  9. PIXI 下落文字消除(3)
  10. CSS常见布局解决方案