1. 通过给CreateProcess传递一个CREATE_SUSPENDED来启动A程序
2. 通过调用GetThreadContext来获取被挂起进程A的CONTEXT。
其中ebx寄存器指向进程的PEB,eax寄存器的值为进程的入口点地址。通过PEB来获取进程的基地址,如[EBX + 8]
3. 把要从内存启动的可执行程序(B)加载到内存中并进行对齐处理。
这里情况分为几种,先调用ZwUnmapViewOfSection来取消可执行程序A的映像映射,
a) 通过VirtualAllocEx在进程A中为可执行程序B在程序B的默认基地址分配足够的空间。成功后,把可执行程序B的镜像复制到进程A的内存空间并从分配的空间的起始地址开始执行
b) 调用VirtualAllocEx的时候失败,而可执行程序B时可重定位的(存在重定位表),那么可以从A的内存空间的任意位置为B分配足够的空间,然后基于所分配的空间进行为B进行重定位处理,然后把可执行程序B的镜像复制到进程A的内存空间并从分配的空间的起始地址开始执行。
4. 把进程A的PEB中的基地址改为可执行程序B的基地址。同时把线程上下文中的EAX寄存器的值设置为可执行程序B的入口点。
5. 通过调用SetThreadContext来设置线程的上下文。
6 . 通过调用ResumeThread来执行被挂起的进程。
这个网上有比较多的代码采用这种方式,其中大同小异。我在win10 x64上用32位的程序去实现这个过程,发现了一些问题。如下
一. 如果A程序和B程序有相同的基地址,那么上面流程3.a)中的操作可以成功,而且整个程序也可以执行。整个方法通过
二.如果A程序与B程序的基地址不同,那么3.a)也就是从B的基地址分配内存这步总是会失败,返回的错误是0x1e7,是权限问题
但是3.b)是可以成功的。再进行相应的重定位操作以后,4.5两步也都可以成功,但是第6步执行的时候,
总是会报一个0xc0000005的错误,这个错误应该是权限错误。
在网上搜索了一些资料,发现碰到这个问题的人有一些,但是都没有得到一个解决。我能想到的可能造成这个错误的原因有两种,
第一是重定位的代码有问题。
重定位的代码修复后如下,已经确认可以使用,所以可以排除这种原因了。
void DoRelocation(PIMAGE_NT_HEADERS peH, void *OldBase, void *NewBase)
{
IMAGE_BASE_RELOCATION ;
unsigned long Delta = (unsigned long)NewBase - peH->OptionalHeader.ImageBase;
PImageBaseRelocation p = (PImageBaseRelocation)((unsigned long)OldBase
+ peH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
while (p->VirtualAddress + p->SizeOfBlock)
{
unsigned short *pw = (unsigned short *)((int)p + sizeof(*p));
for (unsigned int i = 1; i <= (p->SizeOfBlock - sizeof(*p)) / 2; ++i)
{
//网上找到的代码这里少了一个括号,修改了一下
if (((*pw) & 0xF000) == 0x3000) {
unsigned long *t = (unsigned long *)((char*)(OldBase)+p->VirtualAddress + ((*pw) & 0x0FFF));
*t += Delta;
}
++pw;
}
p = (PImageBaseRelocation)pw;
}
}
第二,32位程序在64位系统上运行,是运行在Wow64中,
那么是不是Context或者PEB和32位系统有什么不同?
进行了一些思考也尝试了一些方法:
如果A和B基地址相同,整个方法通过,那么在基地址不同的时候,我在A的基地址上为B分配内存而不是如3.b)中所说的在任意位置分配,
这个时候,内存分配一般是可以成功,然后我把B向A的基地址进行重定位,这样可以保证PEB和Context中其他的内容不变,而只是需要需改入口点,
这时大部分情况可以通过。。。。我也还在继续尝试。。。发现傀儡程序使用自己写的exe大部分是可以,用了原来代码中使用的计算器(calc.exe),记事本(notpad.exe)就不行了,应该是Windows添加了某种保护?
求大神帮忙看看,这到底是什么问题。。有没有朋友愿意一起讨论