程序节区重新映射时设置Section属性为SEC_NO_CHANGE,避免代码被inline hook和设置断点调试。

本方法主要参考 https://github.com/changeofpace/Self-Remapping-Code 这个GitHub项目,作者在19年重构了一版但是没有放到release上。

0x01 编译注意事项

  1. 因为在使用NtMapViewOfSection函数时基址或文件的偏移量参数需要和系统分配颗粒度对齐,这个颗粒度一般为0x10000,所以在项目配置中设置节区对齐大小 #pragma comment(linker, "/ALIGN:0x10000")
  2. 在x64环境下调用相关导入表函数多为 CALL + 偏移,而x86环境是直接CALL导入表地址,但是在Unmap原始IMAGE的节区空间之后原先导入表地址访问是不合法的,所以想要代码在x86环境下运行就需要做重定位,使用新VirtualAlloc后申请的地址内容。
  3. 在属性选项中需要关闭符合模式

image-20210916115817386

0x02 原理介绍

使用未文档化的SEC_NO_CHANGE属性调用NtCreateSection函数创建一个Section

image-20210916152810251

映射Section并复制原始模块代码至映射的Section空间

image-20210916152901621

然后使用NtUnmapViewOfSection把原始IMAGE的节区空间和映射的Section空间给Unmap掉

image-20210916152928554

最后在循环重新映射刚才被Unmap的Section空间到原始模块代码内存地址空间。

image-20210916153059925

0x03 效果

image-20210916155828713

0x04 参考链接

NTSTATUS Values

ZwUnmapViewOfSection function (wdm.h)

ZwMapViewOfSection function (wdm.h)

Self-Remapping Code

0x05 修改后的代码

https://github.com/Jevon101/Self-Remapping-Code