会遇到的主要问题:
1、只要PE文件头正确,作为Exe运行不难,不过要做为DLL被LoadLibrary加载,那么重定位表是不可缺少的。
2、如果有可能实现,那么两者的入口点是相同的,应该要进行区分,看到底是调用WinMain还是DllMain。
对应的解决方法:
1、因为VC只会对DLL生成重定位表,手工生成重定位表又不太现实,所以项目上只好建DLL项目,生成一个扩展名为EXE的DLL,再用UE之类的二进制编辑器打开这个EXE,找到"PE\0\0",偏移18个字节,把0E 21修改为0F 01保存,那么这个DLL现在就是真正的EXE了,可以双击运行。
2、由于文件入口点是相同的,所以不能把入口函数写死,那么怎么判断这次进入入口函数是EXE调用还是DLL调用呢?
这里我定义了一个函数:
__declspec(naked) HMODULE WINAPI GetSelfModuleHandle()
{
_asm
{
sub esp, 28
mov eax, esp
push 28
push eax
call label0
label0:
call dword ptr[VirtualQuery]
test eax, eax
jz label1
mov eax, [esp + 4]
label1:
add esp, 28
ret
}
}
这个函数可以找到自身的HINSTANCE,也就是通常WinMain或者DllMain的第一个参数。
另外我们知道GetModuleHandle(NULL)会返回EXE的HINSTANCE,所以可以用这两者比较一下,如果GetModuleHandle(NULL)==GetSelfModuleHandle()为真说明是EXE调用,否则是DLL调用。
因此这样来构造入口函数:
//手动指明入口函数
#pragma comment(linker,"/entry:SmartMain")
//声明一下通常意义上的EXE和DLL入口函数
extern "C" BOOL WINAPI _DllMainCRTStartup(HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved);
#ifndef _UNICODE
extern "C" int WinMainCRTStartup(void);
#else
extern "C" int wWinMainCRTStartup(void);
#endif
extern "C" __declspec(naked) int WINAPI SmartMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved)
{ //智能选择WinMain和DllMain去执行
_asm
{
push ebx
push 0
call dword ptr[GetModuleHandle]
mov ebx, eax
call GetSelfModuleHandle
cmp ebx, eax
jnz label0
#ifndef _UNICODE
call WinMainCRTStartup ;EXE情况下调用原来的EXE入口函数
#else
call wWinMainCRTStartup ;EXE情况下调用原来的EXE入口函数
#endif
pop ebx
ret ;Exe执行的WinMain实际上没有调用参数,所以是ret而不是ret 12。
label0:
push dword ptr[esp + 16]
push dword ptr[esp + 16]
push dword ptr[esp + 16]
call _DllMainCRTStartup ;DLL情况下调用原来的DLL入口函数
pop ebx
ret 12
}
}
1、完成以上两点后,将项目属性修改为EXE项目,编译,运行正常;
2、修改回DLL项目,编译,用其他EXE加载之,运行也正常;
3、将此DLL用UE将0E 21修改0F 01保存(前面提到的类型标记),也就是改正真正的EXE,运行也正常;
4、将此EXE用其他EXE加载之,运行……,挂了
,出错位置,调用某一个API的时候挂了,API对应的IAT表似乎没有生成,所以不能调API,一调就挂,不过原因不明
,可以知道的是,这个EXE和第2步的DLL,仅仅是0F 01和0E 21这个标记的区别,其他都一样。
各位大侠认为此帖的标题是真命题还是假命题呢?我暂时没辙了……
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)