首页
社区
课程
招聘
[原创]对无重定位表的PE文件模拟启动的另一种思路,欢迎各位交流讨论。
发表于: 2021-7-26 18:09 15215

[原创]对无重定位表的PE文件模拟启动的另一种思路,欢迎各位交流讨论。

2021-7-26 18:09
15215

最近有个课后项目为模拟加载进程实现目标进程的控制,课上老师的具体思路如下:

由于我所测试的PE文件没有重定位表,如果不能VirtualAlloc到指定的ImageBase程序便无法运行。多方查询之后,发现大致的解决思路如下:
图片描述
减少壳子的外部依赖dll,即尽可能少地隐式加载dll,从而使得目标ImageBase得以保证。

问题的关键在于目标PE的ImageBase必须保证,但是操作系统能保证的只有壳子程序的ImageBase,利用这点特性,我们可以先用壳子程序先站住目标PE所需要的ImageBase,这样就能保证在加载过程中这个基址不会被其他资源占用。启动完毕后,壳子程序主动让出,之后再载入目标PE,这样就能保证目标PE的ImageBase了。

之后便可以在此处成功申请到虚拟内存了。

这种方法相对于楼主网上查的方法,壳子程序的构建难度更低,减少了程序运行时的偶然性,省去了手动GetProcAddress的步骤,在部分细节进行优化后,基本可以保证申请到目标内存。

注意,上面两张图里面,0x420000的内存已经被占用,意味着一旦PE文件的SizeOfImage超过了0x20000,便无法申请成功,楼主现在的思路是在壳子程序的屁股后面加一个空节,VirtualSize设置为目标PE的ImageSize,人为地拉长主模块的长度,这样就可以保证所申请的空间大小足够使用。

以上便是我对该问题的全部看法,如有纰漏,欢迎各位大佬指点纠正。

TCHAR g_lpszFilePath[] = TEXT("test.exe");
DWORD g_dwOffset = NULL;
TCHAR g_lpszFilePath[] = TEXT("test.exe");
DWORD g_dwOffset = NULL;
HMODULE hModule = GetModuleHandle(NULL);
//PEFILE为楼主自定义的类,相关函数功能如其名
 PEFILE* lpSrcFile = new PEFILE((LPVOID)hModule, IMAGE);
 DWORD dwSize = lpSrcFile->m_pOptionalHeader->SizeOfImage;
HMODULE hModule = GetModuleHandle(NULL);
//PEFILE为楼主自定义的类,相关函数功能如其名
 PEFILE* lpSrcFile = new PEFILE((LPVOID)hModule, IMAGE);
 DWORD dwSize = lpSrcFile->m_pOptionalHeader->SizeOfImage;
//保存EIP偏移
__asm {
    mov eax, L0
    mov ebx, hModule
    sub eax, ebx
    mov g_dwOffset, eax
}
//拷贝当前PE
LPVOID lpDesBuffer = VirtualAlloc(NULL, dwSize,
    MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(lpDesBuffer, (LPVOID)hModule, dwSize);
//重定位修复
PEFILE* lpNewFile = new PEFILE(lpDesBuffer, IMAGE);
lpNewFile->FixupReloc();
//计算jmp跳转的位置
DWORD dwDesAddress = (DWORD)lpDesBuffer + g_dwOffset;
__asm {
    mov eax, dwDesAddress
    jmp eax
    nop
    L0:
    nop
}
//保存EIP偏移
__asm {
    mov eax, L0
    mov ebx, hModule
    sub eax, ebx
    mov g_dwOffset, eax
}
//拷贝当前PE
LPVOID lpDesBuffer = VirtualAlloc(NULL, dwSize,
    MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
memcpy(lpDesBuffer, (LPVOID)hModule, dwSize);
//重定位修复
PEFILE* lpNewFile = new PEFILE(lpDesBuffer, IMAGE);
lpNewFile->FixupReloc();
//计算jmp跳转的位置
DWORD dwDesAddress = (DWORD)lpDesBuffer + g_dwOffset;
__asm {
    mov eax, dwDesAddress
    jmp eax
    nop
    L0:
    nop
}
//卸载当前镜像
hModule = LoadLibrary(TEXT("ntdll.dll"));
DWORD ZwUnmapViewOfSection = (DWORD)GetProcAddress(hModule, "ZwUnmapViewOfSection");
__asm {
    push NULL
    call GetModuleHandle
    push eax
    call GetCurrentProcess
    push eax
    mov eax, ZwUnmapViewOfSection
    call eax
}
FreeLibrary(hModule);
//卸载当前镜像
hModule = LoadLibrary(TEXT("ntdll.dll"));

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 3
支持
分享
最新回复 (4)
雪    币: 248
活跃值: (3789)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
有这么麻烦吗?
壳子程序在生成exe的时候指定ImageBase
不就可以跟目标exe的ImageBase不一样了吗?不用占住0x400000,因为dll不会在这里加载啊
壳子exe不需要unmap,不用free,更不用reloc啊
2021-10-25 08:49
0
雪    币: 43
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
yy虫子yy 有这么麻烦吗? 壳子程序在生成exe的时候指定ImageBase 不就可以跟目标exe的ImageBase不一样了吗?不用占住0x400000,因为dll不会在这里加载啊 壳子exe不需要unm ...
看到有人回帖很高兴,最近在备考,回复得有点迟,你的思路我最开始也尝试过,在链接的时候就指定好壳子程序的ImageBase,但是这样不能绝对保证源程序的ImageBase能定在0x400000,在我测试的Windows版本中(Windows10 20H2)进程在初始化的时候(即将跳转OEP执行壳子),0x400000会被申请为保留页,一部分线程栈也会被定在周围,在不清楚状况的条件下,没办法得到目标基址和足够的空间(强行释放很可能会失败),这样一来就不能保证源程序的正常运行条件。
2021-11-3 23:45
0
雪    币: 178
活跃值: (381)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4

看了你的文章,我在使用你的方法时,跳转之后,继续往下执行壳子程序会崩溃退出。而且即使我卸载了原image,也将0x400000位置留给目标PE,但VirtualAlloc那段内存还是会失败,依旧是错误487.

最后于 2022-4-12 10:57 被气旋编辑 ,原因:
2022-4-11 14:35
0
游客
登录 | 注册 方可回帖
返回
//