逆向 | 双进程保护模板

逆向 | 双进程保护模板

继续补充书中的代码:

#include <stdio.h>
#include <Windows.h>
#define _PEB void

int FatherProcessMain();
int ChildProcessMain();     // 提前声明函数
int main(){
	_PEB* pPEB = 0;
	_asm{ 
		mov eax, dword ptr fs:[0x18];
		mov eax, dword ptr [eax+0x30];
		mov pPEB, eax;
	};
	BYTE BeingDebugged = (BYTE)(*((BYTE*)pPEB + 0x02));   
	// 对这里有疑问的小伙伴可以参见7.2.1节中的内容
	if(!BeingDebugged)    // 如果没有被调试,则调用父进程函数
	{
		return FatherProcessMain(); 
	}
	return ChildProcessMain(); // 如果被调试,则调用子进程函数
}

int FatherProcessMain(){
	unsigned char shellcode[] =
	{ 0x66,0xb8,0xdf,0xb0,0xdf,0x73,0x60,0x65,0x64,0xbe,
0x4e,0xf3,0x8a,0x23,0x33,0x33,0x33,0x8b,0xff,0xff,
0xff,0xff,0xc0,0x98,0x5b,0xe7,0x53,0x71,0x33,0xdb,
0x1d,0x32,0x33,0x33,0xb0,0xf7,0x37,0x6c,0x6d,0x68,
0xb0,0xf7,0x73,0x8,0xdf,0xdb,0xed,0x33,0x33,0x33,
0xb8,0xd6,0x6e,0xf0
};
	printf("I am Father \n");
	char filename[MAX_PATH];
	GetModuleFileName(0, filename, MAX_PATH); // 获取自身文件名
	STARTUPINFO  si={0};
	GetStartupInfo(&si);
	PROCESS_INFORMATION  pi={0};
	printf("path: %s \n", filename);
	// 以调试模式创建子进程
	if(!CreateProcess(filename,NULL,NULL,NULL,FALSE,DEBUG_PROCESS|DEBUG_ONLY_THIS_PROCESS,NULL,NULL,&si,&pi)) //创建被调试进程
	{
		printf("fail to create subprocess! \n");
		return 0; 
	}
	printf("[f] subprocess id: %d \n", pi.dwProcessId);
	BOOL WhileDoFlag=TRUE;
	DEBUG_EVENT DBEvent ;
	DWORD dwState;
	while (WhileDoFlag) 
	{
		WaitForDebugEvent(&DBEvent, INFINITE);  // 等待调试事件
		dwState = DBG_EXCEPTION_NOT_HANDLED;
		switch(DBEvent.dwDebugEventCode)
		{
		case CREATE_PROCESS_DEBUG_EVENT:     // 子进程创建
			dwState = DBG_CONTINUE;
			break;      
		case EXIT_PROCESS_DEBUG_EVENT:       // 子进程退出
			WhileDoFlag = FALSE;
			break;
		case EXCEPTION_DEBUG_EVENT:
			switch (DBEvent.u.Exception.ExceptionRecord.ExceptionCode)  // 获取异常编号
			{
			case EXCEPTION_BREAKPOINT:
				{
					CONTEXT Regs = {0};
					Regs.ContextFlags = CONTEXT_FULL;    // 获取全部的上下文
					DWORD ret = GetThreadContext(pi.hThread, &Regs);
					if (Regs.Eip == 0x4016b6){           // 子进程人工抛出的断点
						printf("[f]: 接收到预定中断,不处理 \n");
						dwState = DBG_CONTINUE;          // 忽略,继续运行
						// dwState = DBG_EXCEPTION_NOT_HANDLED;   // 执行except handle
					}else{
						printf("[f]: 接收到int3中断 from %x \n", Regs.Eip);
						dwState = DBG_CONTINUE;
					}
					break;
				}
			case EXCEPTION_INT_DIVIDE_BY_ZERO:
				{
					CONTEXT Regs = {0};
					Regs.ContextFlags = CONTEXT_FULL;    // 获取全部的上下文
					DWORD ret = GetThreadContext(pi.hThread, &Regs);
					printf("[f]: 接收到divided by zero from %x \n", Regs.Eip);
					// 使用smc的方式修改子进程空间内容
					for (int i = 0; i < sizeof(shellcode); i ++){
						shellcode[i] ^= 0x33;
					}
					VOID* p_smccode = (VOID*)0x00401660u;   // 这里通过调试确定位置
					DWORD flOldProtect;
					
					VirtualProtectEx(pi.hProcess, p_smccode, sizeof(shellcode), PAGE_EXECUTE_READWRITE,&flOldProtect);
					BOOL bRet = WriteProcessMemory(pi.hProcess, p_smccode, shellcode, sizeof(shellcode), NULL);
					if (!bRet){
						printf("[f] WriteProcessMemory err! \n");
					}
					// 调整下一步的eip
					Regs.Eip += 2; // 跳过2字节的除0操作
					SetThreadContext(pi.hThread, &Regs);
					dwState = DBG_CONTINUE;    // 继续运行
					break;
				}
			}
			break;
		}    
		ContinueDebugEvent(pi.dwProcessId, pi.dwThreadId, dwState);   // 继续执行子进程的线程
	}
	
	CloseHandle(pi.hProcess);   // 关闭相应的句柄
	CloseHandle(pi.hThread);
	return 0;
}


// 代码长度54字节, 编译完成后手动删除这个函数中的字节码
void smc_code(){
	printf("[win] you reach the real path of child process! \n");
}

int ChildProcessMain(){
	printf("I am Child. \n");
	int a = 1;
	__try{
		__asm{
			int 3;   // 软中断
		}  
		a = a / 0;   // 触发父进程smc
		smc_code();
	} 
	__except(1)
	{
		printf("child err. \n");
		exit(-1);    // 调试器不处理异常,说明程序没有正常运行,直接退出
	}
	printf("child over. \n");
	return 0;
}
posted @ 2024-10-27 22:18  Mz1  阅读(12)  评论(0编辑  收藏  举报