当前位置:首页>编程日记>正文

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

本站寻求有缘人接手,详细了解请联系站长QQ1493399855

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

        在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 );

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

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


http://www.coolblog.cn/news/5dc483a422364b12.html

相关文章:

  • asp多表查询并显示_SpringBoot系列(五):SpringBoot整合Mybatis实现多表关联查询
  • s7day2学习记录
  • 【求锤得锤的故事】Redis锁从面试连环炮聊到神仙打架。
  • 矿Spring入门Demo
  • 拼音怎么写_老师:不会写的字用圈代替,看到孩子试卷,网友:人才
  • Linux 实时流量监测(iptraf中文图解)
  • Win10 + Python + GPU版MXNet + VS2015 + RTools + R配置
  • 美颜
  • shell访问php文件夹,Shell获取某目录下所有文件夹的名称
  • 如何优雅的实现 Spring Boot 接口参数加密解密?
  • LeCun亲授的深度学习入门课:从飞行器的发明到卷积神经网络
  • 法拉利虚拟学院2010 服务器,法拉利虚拟学院2010
  • Mac原生Terminal快速登录ssh
  • 支撑微博千亿调用的轻量级RPC框架:Motan
  • mysql commit 机制_1024MySQL事物提交机制
  • java受保护的数据与_Javascript类定义语法,私有成员、受保护成员、静态成员等介绍...
  • 2019-9
  • jquery 使用小技巧
  • vscode pylint 错误_将实际未错误的py库添加到pylint白名单
  • 科学计算工具NumPy(3):ndarray的元素处理
  • 工程师在工作电脑存 64G 不雅文件,被公司开除后索赔 41 万,结果…
  • linux批量创建用户和密码
  • js常用阻止冒泡事件
  • 气泡图在开源监控工具中的应用效果
  • newinsets用法java_Java XYPlot.setInsets方法代碼示例
  • 各类型土地利用图例_划重点!国土空间总体规划——土地利用
  • php 启动服务器监听
  • dubbo简单示例
  • Ubuntu13.10:[3]如何开启SSH SERVER服务
  • [iptables]Redhat 7.2下使用iptables实现NAT
  • Django View(视图系统)
  • 【设计模式】 模式PK:策略模式VS状态模式
  • CSS小技巧——CSS滚动条美化
  • JS实现-页面数据无限加载
  • 最新DOS大全
  • 阿里巴巴分布式服务框架 Dubbo
  • 阿里大鱼.net core 发送短信
  • Sorenson Capital:值得投资的 5 种 AI 技术
  • 程序员入错行怎么办?
  • Arm芯片的新革命在缓缓上演
  • 两张超级大表join优化
  • 第九天函数
  • HDU 5988 最小费用流
  • Linux软件安装-----apache安装
  • 通过Spark进行ALS离线和Stream实时推荐
  • 《看透springmvc源码分析与实践》读书笔记一
  • nagios自写插件—check_file
  • python3 错误 Max retries exceeded with url 解决方法
  • 正式开课!如何学习相机模型与标定?(单目+双目+鱼眼+深度相机)
  • 行为模式之Template Method模式