VC 不同版本代码注入的改进

码农UP2U

共 6756字,需浏览 14分钟

 ·

2023-07-08 15:28

早期文章


在上篇文章中 《VC 不同版本代码注入的区别》 ,我们想要对目标进程进行代码的注入,由于 Debug 版编译生成的代码和 Release 版编译生成的代码有些不同(Debug 版编译后,调用函数时会有一条 jmp 指令,而 Release 没有),因此, 通过 #if def 这样的宏来区别   VC 是 以 Debug 版方式编译,还是 通过 Release 版方式编译,从而 编译不同的代码来针对不同的版本进行了处理。代码如下:

      
        
          #ifdef DEBUG
        
      
      
            DWORD dwAddr = (DWORD)Inject;
      
      
            DWORD dwOffset = *(DWORD *)((PBYTE)dwAddr + 1);
      
      
            dwInjectAddr = dwAddr + 5 + dwOffset;
      
      
        
          #else
        
      
      
            dwInjectAddr = (DWORD)Inject;
      
      
        
          #endif
        
      
    

这样虽然 解决了问题,但是实际中还是有些不够完善的地方。

我们向目标进程注入代码的时候,我这里给了一个固定的注入代码的长度,代码如下:

      
        LPVOID lpBase = VirtualAllocEx(hProcess, NULL, 0x4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
      
      
        WriteProcessMemory(hProcess, lpBase, (LPCVOID)Inject, 0x4096, NULL);
      
    

从上面的代码中可以看出,我给的长度是固定的 0x4096,而实际上真正注入的代码也就几十个字节。

那么实际我们想要计算一下代码的长度后再进行计算,那也由此想到,我们把要注入的代码放到完成注入功能的代码的后面就可以了。这样有两个好处,方便计算注入代码的长度,而且也不用区分是 Debug 版和 Release 版的差异了。

实现的具体代码如下:

      
        void RemoteCall()
      
      
        {
      
      
            HWND hWnd = ::FindWindow(NULL, L"XXXXXX");
      
      
        
          
DWORD dwPid = 0; ::GetWindowThreadProcessId(hWnd, &dwPid);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
LPVOID lpBase = VirtualAllocEx(hProcess, NULL, 0x4096, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
DWORD dwInjectAddr = 0; DWORD dwCodeLen = 0;
__asm { lea eax, START mov dwInjectAddr, eax lea eax, END sub eax, dwInjectAddr mov dwCodeLen, eax }
WriteProcessMemory(hProcess, lpBase, (LPCVOID)dwInjectAddr, dwCodeLen, NULL);
DWORD dwTid;
HANDLE hRemoteProcess = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpBase, NULL, 0, &dwTid);
WaitForSingleObject(hRemoteProcess, INFINITE); CloseHandle(hRemoteProcess); CloseHandle(hProcess);
return; START: __asm { pushad // 一段简单的汇编 popad ret } END:
return; }

在上面的代码中,实际代码只执行到第一个 return 语句处,而要注入到目标进程的代码放到 START 和 END 标签之内,这段代码是不会被执行的。START 标签可以表明要注入代码的起始地址,END 标签和 START 标签可以得到实际注入代码的长度。整个计算过程,是在上面的内联汇编中完成的,代码如下:

      
          __asm
      
      
          {
      
      
              lea eax, START
      
      
              mov dwInjectAddr, eax
      
      
              lea eax, END
      
      
              sub eax, dwInjectAddr
      
      
              mov dwCodeLen, eax
      
      
          }
      
    

代码中 dwInjectAddr 是注入代码的起始地址,dwCodeLen 是注入代码的长度,非常的简单。这样做,就无需考虑编译的版本,也无需计算 jmp 指令的偏移了,省去了很多事情。


6dad723fcd357a7519ae799fa5d4e0cf.webp

公众号内回复 【mongo】 下载 SpringBoot 整合操作 MongoDB 的文档。

公众号内回复 【 cisp知识整理 】 下载 CISP 读书笔记。

公众号内 回复【java开发手册】获取《Java开发手册》黄山版。


浏览 38
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报