隐藏模块(无模块注入)

本文介绍了两种模块隐藏方法,包括往自己的进程注入游戏主模块和往游戏进程注入自身主模块。方法一涉及修复IAT,方法二利用重定位表。代码示例展示了关键函数和完整项目的实现。
摘要由CSDN通过智能技术生成

模块隐藏那节课要求完成两个作业,都是隐藏模块,本文介绍两种方法分别如何实现。

方法一:往自己的进程注入游戏主模块

在这里插入图片描述

这个题目的意思是将程序的基址设置成高地址,将0x400000空出来,然后将游戏主模块拉伸,修复IAT之后,注入进去。这样做的好处主要是简单,因为不需要修复重定位表了。缺点也很明显,很多时候根本没办法在0x400000处申请足够大的内存容纳游戏程序。,还有就是有些程序跳转到OEP之后会卡住,具体原因我也不知道。我这里用dbgview.exe测试,能够正常启动,这足够说明我的代码没有大的错误。
在这里插入图片描述
任务管理器中只会看到控制台的进程,看不到dbgview的进程:
在这里插入图片描述

所谓修复IAT,就是指,将游戏PE的IAT表修复成函数地址,做法也很简单,遍历IAT表,loadlibary获取dll句柄,然后遍历函数名或函数序号,一个个getprocaddress,将获取到的函数地址写入到IAT表就算修复好了。

下面是我的代码,我只给出关键的两个函数,一个是修复IAT表,一个是注入的函数,完整代码放在文章最后。

注入代码

// 将游戏模块注入到我的进程
// 修改ImageBase=0x20000000,使本程序在高地址运行
// 这种方式非常不推荐,因为游戏程序只要稍微大一些,就很可能无法在0x400000处申请内存了
void GameInjectMe()
{
   	
	if ((DWORD)GetModuleHandle(NULL) != (DWORD)0x20000000)
	{
   
		printf("当前进程句柄: 0x%X\n", (DWORD)GetModuleHandle(NULL));
		printf("请修改链接设置,将ImageBase设置为0x20000000\n");
		return;
	}
	// 读取游戏PE,拉伸,修复IAT,写入到其ImageBase处
	LPVOID pFileBuffer = NULL;
	//FileToMemory(TEXT("C:\\Documents and Settings\\nixiang\\桌面\\DTDebug(VT-O)专业版V1.0.025\\DTDebug\\DTDebug.exe"), &pFileBuffer);
	//FileToMemory(TEXT("c:\\program32\\Helloworld.exe"), &pFileBuffer);
	FileToMemory(TEXT("c:\\program32\\dbgview.exe"), &pFileBuffer);
	LPVOID pImageBuffer = NULL;
	DWORD dwImageSize = FileBufferToImageBuffer(pFileBuffer, &pImageBuffer);
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
	PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
	PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));		
	LPVOID pImageBase = VirtualAlloc((LPVOID)pOptionHeader->ImageBase, dwImageSize,MEM_COMMIT, PAGE_READWRITE);	
	if (pImageBase != (LPVOID)pOptionHeader->ImageBase)
	{
   
		printf("错误码:0x%X 在ImageBase(0x%X)处VirtualAlloc失败\n",GetLastError(),pOptionHeader->ImageBase);
		return;
	}
	// 修复IAT表
	RepairIAT(pImageBuffer);	
	memcpy(pImageBase, pImageBuffer, dwImageSize);
	
	// 跳转到游戏的入口点
	DWORD dwOEP = pOptionHeader->AddressOfEntryPoint + pOptionHeader->ImageBase;
	__asm{
   
		jmp dwOEP
	}	
}

修复IAT代码

// 传入一个imagebuffer,修复它的IAT表
void RepairIAT(LPVOID pImageBuffer)
{
   
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
	PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);
	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
	PIMAGE_SECTION_HEADER pSectionHeader = \
		(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
	
// 	PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pFileBuffer + \
// 		RvaToFoa(pFileBuffer, pOptionHeader->DataDirectory[1].VirtualAddress));
	PIMAGE_IMPORT_DESCRIPTOR pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pImageBuffer + \
		pOptionHeader->DataDirectory[1].VirtualAddress);
	
	// 严格来说应该是 sizeof(IMAGE_IMPORT_DESCRIPTOR) 个字节为0表示结束
	while (pImportTable->OriginalFirstThunk || pImportTable->FirstThunk)
	{
   
		// 打印模块名
		//printf("%s\n", (LPCSTR)(pImportTable->Name + (DWORD)pImageBuffer));
		// 获取模块句柄
		HMODULE hModule = LoadLibraryA((LPCSTR)(pImportTable->Name + (DWORD)pImageBuffer));
		if (NULL == hModule)
		{
   
			printf("获取模块句柄失败,模块名: %s\n",(LPCSTR)(pImportTable->Name + (DWORD)pImageBuffer));
		}
		// 修复IAT表
		//printf("--------------FirstThunkRVA:%x--------------\n", pImportTable->FirstThunk);		
		PIMAGE_THUNK_DATA32 pThunkData = (PIMAGE_THUNK_DATA32)((DWORD)pImageBuffer + \
			pImportTable->FirstThunk);
		while (*((PDWORD)pThunkData) != 0)
		{
   
			// IMAGE_THUNK_DATA32 是一个4字节数据
			// 如果最高位是1,那么除去最高位就是导出序号
			// 如果最高位是0,那么这个值是RVA 指向 IMAGE_IMPORT_BY_NAME
			if ((*((PDWORD)pThunkData) & 0x80000000) == 0x80000000)
			{
   
				//printf("按序号导入 Ordinal:%04x\n", (*((PDWORD)pThunkData) & 0x7FFFFFFF));
				DWORD dwProcAddress = (DWORD)GetProcAddress(hModule,MAKEINTRESOURCE((*((PDWORD)pThunkData) & 0x7FFFFFFF)));				
				*((PDWORD)pThunkData) = dwProcAddress;
			}
			else
			{
   
				PIMAGE_IMPORT_BY_NAME pIBN = (PIMAGE_IMPORT_BY_NAME)(*((PDWORD)pThunkData) + \
					(DWORD)pImageBuffer);
				
				//printf("按名字导入 Hint:%04x Name:%s\n", pIBN->Hint, pIBN->Name);
				DWORD dwProcAddress = (DWORD)GetProcAddress(hModule,(LPCSTR)pIBN->Name);
				*((PDWORD)pThunkData) = dwProcAddress;
			}
			pThunkData++;
		}
		pImportTable = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pImportTable + sizeof(IMAGE_IMPORT_DESCRIPTOR));		
	}	
}

其实方法一的局限性我在博客开头已经分析过了,不过我想了一下发现,其实问题关键就是0x400000处可能会申请内存失败,实际上我们不需要非得在这个地方申请内存,而是可以在任意地方申请,然后根据重定位表,修复地址即可。而这种方式,在方法二里面用到了。

以上是第一种方法的做法,下面是第二种做法,往游戏里注入自己,然后跳转到入口函数:

方法二:往游戏进程注入自己的主模块

在这里插入图片描述
这种方式不需要手工修复IAT,因为自身进程启动时操作系统帮我们修复了,我们只需要修复重定位表即可。

原理上图已经解释的非常明白了,下面给出关键代码:
重定位表修复代码

// 修改 ImageBase 并修复重定位表
// 内存镜像版本
VOID SetNewImageBase2(LPVOID pImageBuffer, DWORD dwNewImageBase)
{
   
	PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)pImageBuffer;
	PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)(pDosHeader->e_lfanew + (DWORD)pDosHeader + 4);
	PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(IMAGE_FILE_HEADER));
	PIMAGE_SECTION_HEADER pSectionHeader = \
		(PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
	
	PIMAGE_BASE_RELOCATION pRelocationTable = (PIMAGE_BASE_RELOCATION)((DWORD)pImageBuffer + \
		pOptionHeader->DataDirectory[5].VirtualAddress);
	DWORD dwImageBaseDelta = dwNewImageBase - pOptionHeader->ImageBase; // 新旧ImageBase 的差值	
	
	// 重定位表的 VirtualAddress + 低12位偏移 = RVA
	// RVA + ImageBase 这个内存里存储了一个“指针”
	// 要修改的是这个“指针”的值,要让这个“指针”加上两个ImageBase的差值
	while (pRelocationTable->VirtualAddress || pRelocationTable->SizeOfBlock)
	{
   
		size_t n = (pRelocationTable->SizeOfBlock - 8) / 2; // 可能需要修改的地址数量(高4位==0011才要修改)
		PWORD pOffset = (PWORD)((DWORD)pRelocationTable + 8); // 2字节偏移的数组
		for (size_t i = 0; i < n; i++)
		{
   
			// 高4位等于0011才需要重定位
			if ((pOffset[i] & 0xF000) == 0x3000)
			{
   
				// 计算需要重定位的数据的RVA地址
				DWORD dwRva = pRelocationTable->VirtualAddress + (pOffset[i] & 0x0FFF);				
				// 计算在镜像中的地址
				PDWORD pData = (PDWORD)((DWORD)pImageBuffer + dwRva);
				// 重定位,即修正写死的地址				
				*pData &#
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值