前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >VC 不同版本代码注入的区别

VC 不同版本代码注入的区别

作者头像
码农UP2U
发布2023-09-02 10:50:23
1270
发布2023-09-02 10:50:23
举报
文章被收录于专栏:码农UP2U

写一个简单的功能,需要对目标进程进行代码注入,大致代码如下:

代码语言:javascript
复制
__declspec(naked) void Inject()
{
    __asm
    {
        pushad
        // 一段简单的汇编
        popad
        ret
    }
}

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

    WriteProcessMemory(hProcess, lpBase, (LPCVOID)Inject, 0x4096, NULL);

    DWORD dwTid;

    HANDLE hRemoteProcess = CreateRemoteThread(hProcess, NULL, 0, (LPTHREAD_START_ROUTINE)lpBase, NULL, 0, &dwTid);

    WaitForSingleObject(hRemoteProcess, INFINITE);
      CloseHandle(hRemoteProcess);
    CloseHandle(hProcess);
}

写完上面的代码后,直接运行它进行代码注入,然后目标进程没有报错退出了。VC 默认使用 Debug 版编译,我就换 Release 版编译后,进行代码注入,想要的功能实现了,目标进程没有报错,没有退出。

是何缘故呢?原因很简单,这是 VC 的 Debug 编译和 Release 编译后很明显的一个差别。Debug 编译后,函数名不是函数实际的地址,而是一个 jmp 指令,通过 jmp 指令跳转到实际的函数位置处。而 Release 版本编译后,函数名就是实际的函数地址。因此 Debug 版本下并没有把我们的代码注入到目标进程,而是注入了 jmp 指令,而 Release 则会将代码注入成功。

那么知道问题就可以解决 Debug 版本的问题了,只要将得到的 jmp 指令解析一下,就可以得到函数的实际地址。通过函数名得到 jmp 指令后, jmp 对应的指令码是 E9。而 E9 之后跟着的并不是跳转的目标地址,而是一个偏移量。对于这个偏移量有一个简单的计算公式,即 目标地址 - (当前地址 + 指令长度)。当前地址指的是 jmp 指令所在的地址,也就是 Inject 函数名,而它的指令长度是 5,目标地址我们是不知道的,但是我们知道当前地址到目标地址的偏移。那么通过 当前地址 + 指令长度 + 偏移 就是跳转的目标地址了

看一下实际的例子:

代码语言:javascript
复制
0DE1CC6h e9 55 e8 00 00 e9 c8 2e 00 00 e9 cf 2e 00 00 cc

此时 Inject 的地址是 0DE1CC6h,然后指令长度是 5,那么偏移地址是 0x0000e855,0x0000e855 就是 e9 后的 4 个字节。按照上面的公式进行计算

代码语言:javascript
复制
0x00DE1CC6 + 5 + 0x0000e855 = 0x00DF0520

当然了,因为在 VC 2015 下编译后的程序每次加载的地址不一样,也就是 Inject 这个地址每次在变,所以我们需要进行动态计算,计算的代码如下:

代码语言:javascript
复制
DWORD dwAddr = (DWORD)Inject;
DWORD dwOffset = *(DWORD *)((PBYTE)dwAddr + 1);
dwInjectAddr = dwAddr + 5 + dwOffset;

通过上面的代码,就得到了 Inject 函数的真正地址,而非 jmp 的地址了。

但是,这样的代码在 Release 版本又无法正确执行了,因为 Release 版本是不需要 jmp 跳转的,那么我们就用宏来判断一下,通过宏来区分是 Debug 版本还是 Release 版本。(我们写完代码测试时通常是 Debug 版本,而如果要发布或者给别人使用会使用 Release 版本,所以用宏自行判断编译的版本会方便一些),代码如下:

代码语言:javascript
复制
#ifdef DEBUG
    DWORD dwAddr = (DWORD)Inject;
    DWORD dwOffset = *(DWORD *)((PBYTE)dwAddr + 1);
    dwInjectAddr = dwAddr + 5 + dwOffset;
#else
    dwInjectAddr = (DWORD)Inject;
#endif

上面的代码就适用于 Debug 版和 Release 版的编译了。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-06-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农UP2U 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
腾讯云服务器利旧
云服务器(Cloud Virtual Machine,CVM)提供安全可靠的弹性计算服务。 您可以实时扩展或缩减计算资源,适应变化的业务需求,并只需按实际使用的资源计费。使用 CVM 可以极大降低您的软硬件采购成本,简化 IT 运维工作。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档