把exe当dll来调用破解的技巧方法
调用第三方exe程序里面的函数,一直是大家所向往并已经讨论过不少的问题,其方法大体有三类:
1、让第三方exe启动,然后自己程序注入进去调用之;
2、让第三方exe启动,然后远程读入其内容;
3、把第三方exe,当作DLL进行加载,并调用里面的函数。
前2个方法容易实现,但无法摆脱让exe运行的缺点,今天我们讨论第三条思路,把exe向dll一样加载,然后调用里面的函数。
就此,主要面临以下三个问题:
1、导入表修复;
2、重定位dll数据,也就是exe数据。
其实这2个问题归结起来都是重定位问题,这里就不再解释重定位原理和原因了。
有些人认为PE文件重定位,就要搞重定位表,exe要输出函数,就要搞导出表,这些其实是不必要的,只要理解了他们的原理,自己实现反而更方便。当然,另外一点原因是我现在基本忘却PE结构了。以下方法不涉及给exe增加导出表或者重定位表。
举例来说碰到的问题和解决思路,exe里面有如下代码:
push 425570 ; /kernel32.dll
call dword ptr [425280] ; \GetModuleHandleA
这个很正常,也很简单, 0x425570 指向一个字符串, [425280] 里面是 GetModuleHandleA 函数指针,也就是导入表内容。但如果把这个exe作为DLL进行LoadLibrary,你会发现这2行代码仍然如此,一点也没变,但却无法执行了,因为这时候涉及到的这2个指针,指向的内容已经不是我们预想的了,需要把它们重定位,才能让他们指向预期目的地。重定位方法也很简单:
1、 0x425570 ,这个地址,用当前自身模块基地址加上 0x25570 ,就是新的地址;
2、 0x425280 ,这个地址,用当前自身模块基地址加上 0x25280 ,就是新的地址;
理解了这一点,就知道我们需要作什么了。
口说无凭,动手为真,下面我们举例来进行说明。就采用壳狼最近写的antidebugger测试程序吧,当然没经过他的同意 :)
目的:加载 AntiDebug.exe,调用它的第一个标签里面的 Find Debugger 功能,里面有20多个选项,我们争取把它调用完!
调用函数:
简单跟踪以下,他这些反调试手段都在一个函数体内,调用方式如下:
00404FCC . 50 push eax
00404FCD . 8BCB mov ecx, ebx //这句无所谓,可以nop
00404FCF . E8 ECF1FFFF call 004041C0
实现方式也很简单:
hMod = LoadLibrary(ExePath);
DWORD FindDebugerCall = (DWORD)hMod + 0x41C0;
_asm{
push 0xFFFFFFFF //这里是调用标记,如果为这个参数,表示所有的反调试功能都选了
mov eax, FindDebugerCall
call eax;
}
最终我们的目的就是要让上面这个函数正常运行。为此,我们要作以下大量工作。
一、修复输入表
思路很简单,首先根据被加载dll的模块基地址,找到其导入表,然后根据其函数名,自己获取导入函数地址,重新填入到正常位置。
在此感谢鸡蛋壳,让我在茫茫网海搜到了他的一些代码并加以改之。
pDosHeader = (PIMAGE_DOS_HEADER)hMod;
pNTHeaders = (PIMAGE_NT_HEADERS)((BYTE *)hMod + pDosHeader->e_lfanew);
pOptHeader = (PIMAGE_OPTIONAL_HEADER)&(pNTHeaders->OptionalHeader);
pImportDescriptor = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE *)hMod + pOptHeader->DataDirectory[1].VirtualAddress);
while(pImportDescriptor->FirstThunk)
{
//获取dll名称
char * dllname = (char *)((BYTE *)hMod + pImportDescriptor->Name);
pThunkData = (PIMAGE_THUNK_DATA)((BYTE *)hMod + pImportDescriptor->OriginalFirstThunk);
int no = 1;
while(pThunkData->u1.Function)
{
if ((pThunkData->u1.Ordinal & IMAGE_ORDINAL_FLAG) != IMAGE_ORDINAL_FLAG)
{
//获取函数名称
char *funname = (char *)((BYTE *)hMod + (DWORD)pThunkData->u1.AddressOfData + 2);
myaddr = (int*)GetProcAddress(GetModuleHandle(dllname), funname);
}
PDWORD lpAddr = (DWORD *)((BYTE *)hMod + (DWORD)pImportDescriptor->FirstThunk) +(no-1);
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(lpAddr,&mbi,sizeof(mbi));
VirtualProtect(lpAddr,sizeof(DWORD),PAGE_READWRITE,&dwOLD);
WriteProcessMemory(GetCurrentProcess(),
lpAddr, &myaddr, sizeof(DWORD), NULL);
VirtualProtect(lpAddr,sizeof(DWORD),dwOLD,0);
no++;
pThunkData++;
}
pImportDescriptor++;
}
这个东西搞完,DLL(也就是加载的exe,后面都叫dll)里面的API和其他导入函数可以正常使用了。
二、重定位代码段指针
重定位意义比较广泛,大体有三类数据需要重定位。
1、代码段指针。比如我上面举例的 push 425570 ,这个0x425570是个字符串指针。再比如 mov edi, dword ptr [425400] , 这里面的 0x425400,也是指针,也需要重定位;
2、数据段指针。比如这个exe里面,用到了一些SEH结构,SEH结构里面含有指向函数体的指针,比如:
SEH异常处理结构:
0042BA94 E0 1C 40 00 E6 1C 40 00 00 00 00 00 FE FF FF FF ?@.?@…..?