滴水逆向三期 内存注入/无模块注入

找文章看了一下, 讲的很好, 但是地址没了…尴尬, 03年的技术, 大致内容就是介绍了两种注入技术, 一个为常规的远程线程注入, 另一个就是内存注入. 只不过文章思路是只插入一段代码, 也就是编写shellcode. 而无模块注入则是将模块整体以内存写入的形式加载实现隐藏 .
杀软依然秒杀…但是我封装起来写后就检测不到, 特征码竟然消失了…

原理与问题

思路是将自己复制一份加载到目标进程中, 即注入进程和寄生程序是一个东西.不过想想也知道, 注入dll也是一个原理, 无非是拉伸之后再贴到目标进程, 最后依然是起远程线程设置入口点.
但重点在于, 寄生程序IAT中的地址与目标进程的地址可能不一致, 或者没有加载相关dll, 这个时候就要根据目标进程的IAT修复寄生进程的IAT.
在写这个的时候遇到了一些问题如下:

  1. 难道LoadLibrary和GetPrcAddress的地址都一样吗??
    不一样的话岂不是要从文件读dll再拉伸然后按照宿主进程的基址重定位后再读地址

实在是过于麻烦了…带着这个问题, 我们找到了如下答案

https://bbs.pediy.com/thread-259087.htm
滴水逆向三期 内存注入/无模块注入_第1张图片

又因为每个程序都用到了LoadLibrary和GetProcAddress那么就可以放心大胆的用了.

  1. char szBuffer[256] = 0; printf
    初始化操作字符串其实用了memset这个函数, 由于软件编译版本不同, 这个函数所处的dll有些许差异, 或者在一个窗口程序用了printf, 别人都没有加载这个dll用了不就直接蹦了, 所以这两行代码可以帮助我们检测自己写的修复IAT是否可用.

步骤

  1. 获取自身Buferr
  2. 目标进程中申请空间
  3. 修复重定位表, 如果没有则直接失败(没有重定位的EXE)
  4. 写入数据
  5. 计算线程函数入口地址并开启远程线程

此时进入目标进程, 线程函数首先需要修复IAT表, 绝对要注意, 进入了线程函数之后修复IAT之前, 当然包括修复IAT都不可以使用非系统必要dll的函数.

代码

给出主体与修复重定位和IAT表的代码

主体代码

//通过GetCurrent得到的句柄是假句柄, 只对当前进程有用, 无需CloseHandle;
//获取自身Buffer, 当然这里完全可以换成从文件读一个dll然后展开注入, 思路一样
Process::GetProcInfo(&curPi);
sizeOfImage = Pe::GetSizeOfImage(::GetModuleHandleA(NULL));
pImageBuffer = malloc(sizeOfImage);
memcpy(pImageBuffer, ::GetModuleHandleA(NULL),sizeOfImage);

//申请空间
hostPi.hProcess = Process::OpenProcessM(hostName);
if (!(allocBaseAddr = Process::VirtualAllocate(
	hostPi.hProcess,
	NULL,
	sizeOfImage,
	PAGE_EXECUTE_READWRITE
)))
	return NULL;

//可能出现无重定位的exe, 直接返回失败
if (!Pe::RebaseRelocation(pImageBuffer, (DWORD)allocBaseAddr))
{
	::VirtualFreeEx(hostPi.hProcess, allocBaseAddr, NULL, MEM_RELEASE);
	return NULL;
}

//写入修复重定位的Buffer, 并计算起始地址开远程线程
::WriteProcessMemory(hostPi.hProcess, allocBaseAddr, pImageBuffer, sizeOfImage, NULL);
crtStartUp = (LPTHREAD_START_ROUTINE)((DWORD)allocBaseAddr + (DWORD)injectThread - (DWORD)GetModuleHandle(NULL));
hostPi.hThread = ::CreateRemoteThread(
	hostPi.hProcess,
	NULL,
	NULL,
	crtStartUp,
	allocBaseAddr,
	NULL,
	NULL);

//释放句柄, 栈空间
::WaitForSingleObject(hostPi.hThread, INFINITE);
//如果为完全释放, 大小必须为空, 系统会按照分配时的大小释放空间
::VirtualFreeEx(hostPi.hProcess, allocBaseAddr, NULL, MEM_RELEASE);
free(pImageBuffer);
Process::CloseHandles(hostPi);

线程函数与调用

void InjectFunc()
{
	char szTemp[256] = { 0 };
	printf("测试程序..如果IAT未修复则无法运行.\n");
	//这两行程序可以测试IAT是否修复成功, 字符串初始化操作调用memset

	for (int i = 0; i < 3; i++)
		MessageBoxA(0, 0, "注入成功", 0);
}


DWORD WINAPI ThreadProc(LPVOID lparams)
{
	if (!Pe::RestoreIAT(lparams))
	{
		MessageBoxA(NULL, NULL, "修复重定位失败!", NULL);
		return -1;
	}
	InjectFunc();
	return 1;
}

int main()
{
    CHAR hostName[] = "demo.exe";
    injector::InjectThrowMemory(hostName, (LPTHREAD_START_ROUTINE)(ThreadProc));
	printf("内存注入线程结束, 任意键退出.\n");
	system("pause");
    return 0;
}

修复重定位

PIMAGE_NT_HEADERS Pe::GetNtHeaders(LPVOID pBuffer)
{
	return (PIMAGE_NT_HEADERS)((DWORD)pBuffer + *((DWORD*)((DWORD)(pBuffer)+0x3c)));
}

BOOL Pe::RebaseRelocation(LPVOID pImageBuffer, DWORD newImageBase)
{
	DWORD rvaBaseRelocation = 0;
	PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
	PIMAGE_BASE_RELOCATION pBaseRelocation = NULL;
	PIMAGE_NT_HEADERS pNtHeaders = NULL;
	pNtHeaders = GetNtHeaders(pImageBuffer);
	pDataDirectory = pNtHeaders->OptionalHeader.DataDirectory;
	rvaBaseRelocation = (*(pDataDirectory + 5)).VirtualAddress;
	if (!rvaBaseRelocation)
	{
		MessageBoxA(NULL, "软件无重定位!!! 无法更改ImageBase", "Error", NULL);
		return FALSE;
	}
	pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pImageBuffer + rvaBaseRelocation);
	DWORD foaOfData = 0;
	DWORD rvaOfData = 0;
	DWORD currentBlockItems = 0;
	DWORD foaOfBlock = 0;
	DWORD differenceOfImageBase = 0;
	DWORD addressOf_rvaOfData = 0;
	DWORD* pFarAddress = NULL;
	differenceOfImageBase = newImageBase - pNtHeaders->OptionalHeader.ImageBase;
	pNtHeaders->OptionalHeader.ImageBase = newImageBase;
	while (pBaseRelocation->SizeOfBlock)
	{
		//要修改的数据大表在文件中的偏移 大表 + word 型偏移
		foaOfBlock = (DWORD)pBaseRelocation - (DWORD)pImageBuffer;
		currentBlockItems = (pBaseRelocation->SizeOfBlock - 8) / 2;	//word 表示
		addressOf_rvaOfData = pBaseRelocation->VirtualAddress;
		while (--currentBlockItems)
		{
			rvaOfData = ((*(WORD*)(foaOfBlock + (DWORD)pImageBuffer + 8)) & 0x0fff) + addressOf_rvaOfData;
			pFarAddress = (DWORD*)(rvaOfData + (DWORD)pImageBuffer);
			*pFarAddress += differenceOfImageBase;
			foaOfBlock += 2;
		}
		pBaseRelocation = (PIMAGE_BASE_RELOCATION)((DWORD)pBaseRelocation + pBaseRelocation->SizeOfBlock);
	}
	return TRUE;
}


BOOL Pe::RestoreIAT(LPVOID pImageBuffer)
{
	PIMAGE_NT_HEADERS pNtHeaders = NULL;
	PIMAGE_IMPORT_DESCRIPTOR pImport = NULL;
	HMODULE hModule = 0;
	DWORD rvaImport = 0;
	DWORD rvaName = 0;
	DWORD rvaOriginalThunk = 0;
	DWORD rvaFirstThunk = 0;
	DWORD originalValue = 0;
	LPVOID pFirstValue = NULL;
	DWORD ordinalOfFunc = 0;
	CHAR* pFuncNameAddr = NULL;
	DWORD oldProtect = 0;
	
	pNtHeaders = GetNtHeaders(pImageBuffer);
	rvaImport = (*(pNtHeaders->OptionalHeader.DataDirectory + 1)).VirtualAddress;
	pImport = (PIMAGE_IMPORT_DESCRIPTOR)((DWORD)pImageBuffer + rvaImport);

	//以OriginalFirstThunk为基准找到函数名/序号
	//将新值补充到FirstThunk对应的ThunkValue中
	while (rvaOriginalThunk = *((DWORD*)pImport))
	{
		rvaFirstThunk = *((DWORD*)pImport + 4);
		rvaName = pImport->Name;
		if (!(hModule = ::LoadLibraryA((CHAR*)(DWORD)pImageBuffer + rvaName)))
		{
			//这一句不能加, printf 的dll可能没加载导致程序异常退出.
			//printf("加载dll失败 LastError: %d", GetLastError());
			return FALSE;
		}
		//printf("DLL's name: %s\n", (char*)((DWORD)pImageBuffer + rvaName));
		while (originalValue = *(DWORD*)((DWORD)pImageBuffer + (DWORD)rvaOriginalThunk))
		{
			pFirstValue = (LPVOID)((DWORD)pImageBuffer + rvaFirstThunk);
			VirtualProtect(pFirstValue, 4, PAGE_READWRITE, &oldProtect);
			if ((originalValue & 0x80000000) == 0x80000000)
			{
				ordinalOfFunc = (originalValue & 0x7FFFFFFF);
				*(DWORD*)pFirstValue = (DWORD)GetProcAddress(hModule, MAKEINTRESOURCEA(ordinalOfFunc));
			}
			else
			{
				pFuncNameAddr = (CHAR*)((DWORD)pImageBuffer + originalValue + 2);
				*(DWORD*)pFirstValue = (DWORD)GetProcAddress(hModule, pFuncNameAddr);
			}
			rvaOriginalThunk += 4;
		}
		pImport++;
	}
	return TRUE;
}

你可能感兴趣的:(windows逆向)