花费几天时间使用 nim 复刻了 ZwProcessHollowing,这个作者可能没自己编译过,源码有几个问题。
步骤
- 启动一个暂停的 notepad.exe
- 取消映像
- 申请 notepad.exe 映像区域的内存权限
- 将 test.exe 的区段写入
- 修复重定位区域
- 修改程序入口
实现情况
成功将 test.exe 注入挖空的 notepad.exe
后面尝试绕过了天擎
源码问题
两个问题全在这几行里面
1 | // Source.cpp |
第一个就是函数 RtlCopyMemory
,这里应该是比较内存,但是作者写成了复制内存,而且这个函数是没有返回值的。
第二个问题就是作者只在这里让区段遍历了,如果重定位区段后面还有区段,则会乱写内存,所以得在这个循环的最后面加个 sourceImgSection++;
也就是 for 循环的最后面。
注意事项
暂不支持 32 位,启动的进程必须是 64 位,注入的程序也必须是 64 位
我注入的 test.exe 是用 nim 写的,只是个弹窗程序
test.exe 编译的时候必须带 --app:gui
要不然会报错
源码
写的有点狗屎,就不贴出来了
不过可以提几个注意的点
数组取地址
c++ 的数组直接取值,是会取第一个元素的地址,nim 里就得手动取一下了,虽然也可以写个 converter
来自动转换,但只支持 seq
而不支持 array
回到代码举例,比方说这一行
1 | PIMAGE_DOS_HEADER sourceDosHeader = (PIMAGE_DOS_HEADER)buf; |
就可以等价替换成
1 | var sourceDosHeader: PIMAGE_DOS_HEADER = cast[PIMAGE_DOS_HEADER](buf[0].addr) |
数组内存运算
跟上面差不多,不过就是将地址转成数字类型而已
1 | PVOID sourceSectionLocation = (PVOID)((ULONG_PTR)buf + sourceImgSection->PointerToRawData); |
1 | sourceSectionLocation: PVOID = cast[PVOID](cast[ULONG_PTR](buf[0].addr) + sourceImgSection.PointerToRawData) |
类型问题
源码里有个类型声明很独特
1 | typedef struct BASE_RELOCATION_ENTRY { |
简单讲,就是只占了一个 USHORT
类型,但是高低位代表不同的信息。在 nim 没有这种操作,只能通过别的方式实现,下面直接贴源码
1 | type |
由于这里只牵扯了获取值,所以我们定义一个 USHORT
就行,然后获取值就自己位移。
画饼总结
关于 nim 的入门教程,以后可能有机会会写,不过说实话,现在卡巴也开始杀 winim 的那个资源文件了,360 目前不杀 relase
版,但是 --app:gui
杀完了。之后可能转 go 或者 c++。
Process Hollowing and Portable Executable Relocations
ZwProcessHollowing (罪魁祸首)