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

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

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

__declspec(naked) voidInject()
{
__asm    {
pushad// 一段简单的汇编popadret    }
}
voidRemoteCall()
{
HWNDhWnd= ::FindWindow(NULL, L"XXXXXX");
DWORDdwPid=0;
    ::GetWindowThreadProcessId(hWnd, &dwPid);
HANDLEhProcess=OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPid);
LPVOIDlpBase=VirtualAllocEx(hProcess, NULL, 0x4096, MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, lpBase, (LPCVOID)Inject, 0x4096, NULL);
DWORDdwTid;
HANDLEhRemoteProcess=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,目标地址我们是不知道的,但是我们知道当前地址到目标地址的偏移。那么通过 当前地址 + 指令长度 + 偏移 就是跳转的目标地址了

看一下实际的例子:

0DE1CC6he955e80000e9c82e0000e9cf2e0000cc

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

0x00DE1CC6+5+0x0000e855=0x00DF0520

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

DWORDdwAddr= (DWORD)Inject;
DWORDdwOffset=*(DWORD*)((PBYTE)dwAddr+1);
dwInjectAddr=dwAddr+5+dwOffset;

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

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

#ifdef DEBUGDWORDdwAddr= (DWORD)Inject;
DWORDdwOffset=*(DWORD*)((PBYTE)dwAddr+1);
dwInjectAddr=dwAddr+5+dwOffset;
#elsedwInjectAddr= (DWORD)Inject;
#endif

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





相关文章
|
资源调度 前端开发 算法
前端依赖版本重写指南
感谢神奇的 Semver 动态规则,npm 社区经常会发生依赖包更新后引入破坏变更的情况(应用没有使用依赖锁的话),而应用开发者就要在自己的依赖声明里先临时绕过,避免安装到有问题的版本,如果是一级依赖,只需要改 package.json 的声明就可以了,但如果是子依赖,就需要进行版本重写(overrides/resolution)了。本文是一篇针对版本重写功能的指南性文章,当你遇到如下的问题时,就可以按照对应的依赖重写语法,解决这些依赖问题了。
6483 1
前端依赖版本重写指南
|
5月前
|
IDE Java 应用服务中间件
如何检查并解决类路径中的类库版本冲突问题
类路径中的类库版本冲突可能导致应用运行异常。解决方法包括:1. 使用依赖管理工具(如Maven、Gradle)检查依赖树,找出冲突的库;2. 调整依赖版本或排除特定版本;3. 清理缓存,重新构建项目。
136 2
|
7月前
|
Java Maven 容器
JDK版本特性问题之想使用接口中的私有方法,如何实现
JDK版本特性问题之想使用接口中的私有方法,如何实现
JDK版本特性问题之想使用接口中的私有方法,如何实现
|
9月前
|
Java API Android开发
ASM 框架:字节码操作的常见用法(生成类,修改类,方法插桩,方法注入)
ASM 框架:字节码操作的常见用法(生成类,修改类,方法插桩,方法注入)
180 0
|
10月前
|
C++
win32编程 -- 动态库中声明类
win32编程 -- 动态库中声明类
49 0
|
网络协议 C# C++
关于 C#调用一个C/C++dll库运行时实现多个应用(静态变量区分) 的解决方法
关于 C#调用一个C/C++dll库运行时实现多个应用(静态变量区分) 的解决方法
关于 C#调用一个C/C++dll库运行时实现多个应用(静态变量区分) 的解决方法
|
监控 安全 虚拟化
DLL注入的环境构建
DLL注入的环境构建
VC 不同版本代码注入的改进
在上篇文章中 《VC 不同版本代码注入的区别》 ,我们想要对目标进程进行代码的注入,由于 Debug 版编译生成的代码和 Release 版编译生成的代码有些不同(Debug 版编译后,调用函数时会有一条 jmp 指令,而 Release 没有),因此,通过 #ifdef 这样的宏来区别 VC 是以 Debug 版方式编译,还是通过 Release 版方式编译,从而编译不同的代码来针对不同的版本进行了处理。
94 0
|
Java Android开发
【错误记录】Android 编译时技术版本警告 ( 注解处理器与主应用支持的 Java 版本不匹配 )
【错误记录】Android 编译时技术版本警告 ( 注解处理器与主应用支持的 Java 版本不匹配 )
420 0
【错误记录】Android 编译时技术版本警告 ( 注解处理器与主应用支持的 Java 版本不匹配 )
|
监控 安全 Android开发
【Android 逆向】Android 进程代码注入原理 ( 注入本质 | 静态注入和动态注入 | 静态注入两种方式 | 修改动态库重打包 | 修改 /data/app/xx/libs 动态库 )
【Android 逆向】Android 进程代码注入原理 ( 注入本质 | 静态注入和动态注入 | 静态注入两种方式 | 修改动态库重打包 | 修改 /data/app/xx/libs 动态库 )
575 0