滴水加密壳,类似傀儡进程

文章讲述了作者在处理外壳软件时遇到的问题,涉及到dll的IAT表修复、ASLR机制以及如何在ASLR保护下正确处理重定位表,强调了在现代软件中这些技术的过时性和注意事项。
摘要由CSDN通过智能技术生成

这里的话是新加的,过了好几天,终于想明白了问题,因为系统只帮我们加载了外壳软件的dll,并没有帮我们加载节里的dll,所以我们需要加一步把IAT表修复了,这是课程没有的内容,网络上大部分回答(我看到的都没有讲)也没有解决这个东西

该踩的坑有,ASLR基址,大意就是不能修改imagebase不然后系统一检测到就报c0000005,所以大多数软件都不需要修复重定位表,大部分imagebase都是0x400000。
还有就是滴水给出的细节没给全,因为我的试验是,两个软件如果文件对齐,内存对齐是一样的就能运行,否则不行。我试了两组例子,一组1000 10000,一组1000 200,一样就能够运行,不一样就不行。
我猜测是因为上下文(context)里还有需要修改的东西。总之是一项过时的技术,体验思路就行,没必要抓着这些细枝末节刨根问底

//附加
VOID AttachShell(PVOID pShellFileName, PVOID pInFileName, PVOID pOutFileName) {
	//读取shell文件和src文件,再给壳增加节
	PVOID pShellFileBuffer = 0;
	PBYTE pInFileBuffer = 0;
	PVOID pOutFileBuffer = 0;
	DWORD dwOutFileSize = 0;
	DWORD dwShellSize = FileToFileBuffer(pShellFileName, &pShellFileBuffer);
	DWORD dwInFileSize = FileToFileBuffer(pInFileName, &pInFileBuffer);
	dwOutFileSize = IncreaseSection(pShellFileBuffer, &pOutFileBuffer, dwInFileSize);
	//套入headers结构
	PIMAGE_DOS_HEADER pDosHeader = pOutFileBuffer;
	PIMAGE_NT_HEADERS pNTHeader = (DWORD)pDosHeader + pDosHeader->e_lfanew;
	if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
		printf("File is not PE\n");
		free(pOutFileBuffer);
		return FALSE;
	}
	PIMAGE_FILE_HEADER pFileHeader = &pNTHeader->FileHeader;
	PIMAGE_OPTIONAL_HEADER pOptHeader = (DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER);
	PIMAGE_SECTION_HEADER pSecHeader = (DWORD)pOptHeader + pFileHeader->SizeOfOptionalHeader;
	//计算最后一个节,然后转换成文件偏移
	PIMAGE_SECTION_HEADER pLastSection = pSecHeader + pFileHeader->NumberOfSections - 1;
	PBYTE pEncryPosition = VAToFOA(pLastSection->VirtualAddress, pOutFileBuffer);
	//给每一个字节的代码做加密并且写入新加的节里
	for (size_t i = 0; i < dwInFileSize; i++) {
		pEncryPosition[i] = ~pInFileBuffer[i];
	}
	//存盘
	FileBufferToFile(pOutFileName, pOutFileBuffer, dwOutFileSize);
	free(pShellFileBuffer);
	free(pInFileBuffer);
	free(pOutFileBuffer);
}
VOID DumpShell(PVOID pFileName) {
	//函数指针显示调用
	typedef NTSTATUS(WINAPI* FnNtUnmapViewOfSection)(HANDLE, PVOID);
	STARTUPINFO si = { 0 };
	si.cb = sizeof(STARTUPINFO);
	PROCESS_INFORMATION pi;
	//定位最后一节
	PBYTE pFileBuffer = 0;
	DWORD dwFileSize = FileToFileBuffer(pFileName, &pFileBuffer);
	PIMAGE_DOS_HEADER pDosHeader = pFileBuffer;
	PIMAGE_NT_HEADERS pNTHeader= (DWORD)pFileBuffer + pDosHeader->e_lfanew;
	if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
		printf("File is not PE\n");
		free(pFileBuffer);
		return FALSE;
	}
	
	PIMAGE_FILE_HEADER pFileHeader = (DWORD)pNTHeader + sizeof(pNTHeader->Signature);
	PIMAGE_OPTIONAL_HEADER pOptionalHeader = (DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER);
	PIMAGE_SECTION_HEADER pSectionHeader = (DWORD)pOptionalHeader + pFileHeader->SizeOfOptionalHeader;
	PIMAGE_SECTION_HEADER pLastSecHeader = pSectionHeader + pFileHeader->NumberOfSections - 1;
	//解密
	PBYTE pDecryPosition = pLastSecHeader->PointerToRawData + (DWORD)pFileBuffer;
	for (size_t i = 0; i < pLastSecHeader->SizeOfRawData; i++) {
		pDecryPosition[i] = ~pDecryPosition[i];
	}
	/*拉伸因为之前那些函数太高耦合了,而且参数输入输出有问题,所以打算重写。。。
	一重写就重写一堆因为全是嵌套的学到教训了*/
	PBYTE pImageBuffer;
	DWORD dwImageSize = FileBufferToImageBuffer(pDecryPosition, &pImageBuffer, pLastSecHeader->SizeOfRawData);
	PIMAGE_DOS_HEADER pNewDosHeader = pImageBuffer;
	PIMAGE_NT_HEADERS pNewNTHeader = (DWORD)pImageBuffer + pNewDosHeader->e_lfanew;
	if (pNewNTHeader->Signature != IMAGE_NT_SIGNATURE) {
		printf("File is not PE\n");
		free(pFileBuffer);
		return FALSE;
	}
	PIMAGE_FILE_HEADER pNewFileHeader = (DWORD)pNewNTHeader + sizeof(pNewNTHeader->Signature);
	PIMAGE_OPTIONAL_HEADER pNewOptionalHeader = (DWORD)pNewFileHeader + sizeof(IMAGE_FILE_HEADER);
	PIMAGE_SECTION_HEADER pNewSectionHeader = (DWORD)pNewOptionalHeader + pNewFileHeader->SizeOfOptionalHeader;
	//自启动然后挂起然后卸载然后不用申请特定位置的imagebase,直接去修复重定位表,我觉得没有软件没有重定位表吧
	//我收回那句话。。。之后调试的时候遇到了
	if (!CreateProcessA(pFileName, 0, 0, 0, 0, CREATE_SUSPENDED, 0, 0, &si, &pi)) {
		printf("create process failed\n");
		return;
	}
	CONTEXT threadContext;
	threadContext.ContextFlags = CONTEXT_ALL;
	GetThreadContext(pi.hThread, &threadContext);
	PVOID pImageBase;
	ReadProcessMemory(pi.hProcess, (LPCVOID)(threadContext.Ebx + 8), &pImageBase, sizeof(PVOID), 0);
	FnNtUnmapViewOfSection NtUnmapViewOfSection =
		(FnNtUnmapViewOfSection)GetProcAddress(LoadLibraryA("ntdll.dll"), "NtUnmapViewOfSection");
	if (!NtUnmapViewOfSection) {
		printf("NtUnmapViewOfSection \n");
		return;
	}
	NtUnmapViewOfSection(pi.hProcess, pImageBase);
	/*上网反复查询资料才发现有ASLR这种东西的存在所以,如果imagebase发生了变化就会报c00005
		也就是说我们只能只能在原基址上加载,不然就会报错。。。不过修复重定位表还是要做的要是imagebase不是0x400000
		也能用上*/
	PVOID pNewImageBase =  
		VirtualAllocEx(pi.hProcess, pNewOptionalHeader->ImageBase, pNewOptionalHeader->SizeOfImage
			, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
	if (!pNewImageBase) {
		printf("VirtualAllocEx failed\n");
		return;
	}
	//计算镜像差然后改掉imagebuffer里的imagebase,顺带把oep也改一下

	DWORD dwImageDiff = (DWORD)pNewImageBase - pNewOptionalHeader->ImageBase;
	pNewOptionalHeader->ImageBase = (DWORD)pNewImageBase;
	threadContext.Eax = pNewOptionalHeader->ImageBase + pNewOptionalHeader->AddressOfEntryPoint;
	pImageBase = pNewImageBase;
	/*因为aslr的存在我们都不需要修改这里的imagebase
	if (!WriteProcessMemory(pi.hProcess, (LPCVOID)(threadContext.Ebx + 8), &pImageBase, sizeof(PVOID), 0)) {
		printf("WriteProcessMemory failed\n");
		return;
	}*/
	//设置上下文
	SetThreadContext(pi.hThread, &threadContext);
	//定位重定位表,然后修复
	if (pNewOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress || dwImageDiff) {
		PIMAGE_BASE_RELOCATION pBaseRelocation =
			pNewOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress + (DWORD)pImageBuffer;
		while (pBaseRelocation->VirtualAddress) {
			DWORD items = (pBaseRelocation->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / 2;
			PWORD pBlock = pBaseRelocation + 1; 
			for (size_t i = 0; i < items; i++) {
				if (*pBlock & 0x3000) {
					DWORD VA = pBaseRelocation->VirtualAddress + (*pBlock & 0xfff);
					//printf("%x + %x\n", *((PDWORD)(pImageBuffer + VA)), dwImageDiff);
					*((PDWORD)(pImageBuffer + VA)) += dwImageDiff;
				}
				pBlock++;
			}
			pBaseRelocation = (DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock;
		}
	}
	//应该还要修复IAT因为两个软件之间的加载的dll不一样
	FixIATTable(&pImageBuffer);
	//终于做到了...
	//修复完后将imageBuffer写入内存中
	if (!WriteProcessMemory(pi.hProcess, pNewImageBase, pImageBuffer, dwImageSize, 0)) {
		printf("WriteProcessMemory failed\n");
		return;
	}

	ResumeThread(pi.hThread);
	free(pFileBuffer);
	free(pImageBuffer);
	return;
}

这是我自己写的IAT修复函数

//修复IAT表,递归导入互相依赖的dll(还没有实现)
VOID FixIATTable(PVOID* pImageBufferBuffer) {
	//MessageBoxA(0, 0, 0, 0);
	PBYTE pImageBuffer = *pImageBufferBuffer;
	PIMAGE_DOS_HEADER pDosHeader = pImageBuffer;
	PIMAGE_NT_HEADERS pNTHeader = (DWORD)pImageBuffer + pDosHeader->e_lfanew;
	if (pNTHeader->Signature != IMAGE_NT_SIGNATURE) {
		printf("File is not PE\n");
		free(pImageBuffer);
		return;
	}
	PIMAGE_FILE_HEADER pFileHeader = (DWORD)pNTHeader + sizeof(pNTHeader->Signature);
	PIMAGE_OPTIONAL_HEADER pOptionalHeader = (DWORD)pFileHeader + sizeof(IMAGE_FILE_HEADER);
	if (!pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress) {
		//printf("No import table\n");
		return;
	}
	PIMAGE_IMPORT_DESCRIPTOR pImportTable =
		pOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress + pImageBuffer;
	while (pImportTable->OriginalFirstThunk) {
		//大坑,不要乱用函数,因为还没有加载过...
		PBYTE lpDllName = pImportTable->Name + pImageBuffer;
		HANDLE hModule = GetModuleHandleA(lpDllName);
		if (!hModule) {
			hModule = LoadLibraryA(lpDllName);
			if (!hModule) {
				//printf("hModule获取失败\n");
				return;
			}
		}
		PDWORD pINT = pImportTable->OriginalFirstThunk + pImageBuffer;
		PDWORD pIAT = pImportTable->FirstThunk + pImageBuffer;
		while (*pINT) {
			PVOID pFunAddr;
			if (*pINT & 0x80000000) {
				pFunAddr = GetProcAddress(hModule, *pINT & 0x7fffffff);
			}
			else {
				PIMAGE_IMPORT_BY_NAME pImportByName = *pINT + pImageBuffer;
				pFunAddr = GetProcAddress(hModule, pImportByName->Name);
			}
			DWORD dwProtect;
			if (!VirtualProtectEx(-1, pIAT, sizeof(PVOID), PAGE_READWRITE, &dwProtect)) {
				printf("VirtualProtect");
				return;
			}
			*pIAT = pFunAddr;
			pIAT++;
			pINT++;
		}
		//FixIATTable(&hModule);
		pImportTable++;
	}
}
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值