壳的编写(3)-- 编写壳(Stub)部分(2)

动态加载API技术详解
本文详细介绍了如何在程序中动态加载API,包括获取GetProcAddress函数地址的过程及其实现代码,同时还涉及了其他常用API的初始化方法,并给出了解密函数的具体实现。

接:壳的编写(2)-- 编写壳(Stub)部分(1)  http://blog.youkuaiyun.com/obuyiseng/article/details/50456807

5、动态加载API--获取到GetProcAddress

         由于我们程序到时会丢弃掉自己的IAT和导入表信息,这样就不能够直接调用API,因此,我们需要使用动态加载API方法。我们需要获取到GetProcAddress函数的地址,而该函数是从系统文件Kernel32.dll中导出的,所有的运行的程序都会加载该动态链接库。那么,我们获取到Kernel32.dll加载基址,就可以获取GetProcAddress函数了。要想获取到Kernel32基址,我们可以使用TEB的信息找到Kernel32.dll的加载基址:

         1)通过FS得到TEB的地址

         2)TEB偏移0x30处指向的是PEB指针

         3)PEB偏移0x0C处指向PEB_LDR_DATA结构指针

         4)PEB_LDR_DATA偏移0x1C处是InInitializationOrderModuleLis(模块初始化链表的头指针)

         5)InInitializationOrderModuleLis中按顺序存在着此进程的初始化模块信息,在NT5.x内核中,第一个节点为ntdll.dll的基址,第二个节点为Kernel32.dll的基址; 在NT6.1内核中,第二个节点为KernelBase.dll的基址(包含着Kernel32.dll的大部分实现,其中就有GetProcAddress函数)

为此我们需要在Stub.h中定义两个函数,一个用于获取Kernel32.dll基址,一个用于获取GetProcAddress基址,代码如下:

extern DWORD GetKernel32Base();   // 获取Kernel32.dll的模块基址
extern DWORD GetGPAFunAddr();     // 获取GetProcAddress的函数地址
在Stub.cpp中获取Kernel32.dll的代码如下:

DWORD GetKernel32Base()
{
	DWORD dwKernel32Addr = 0;
	__asm
	{
		push eax
		mov eax,dword ptr fs:[0x30] // eax = PEB的地址
		mov eax,[eax+0x0C]          // eax = 指向PEB_LDR_DATA结构的指针
		mov eax,[eax+0x1C]          // eax = 模块初始化链表的头指针InInitializationOrderModuleList
		mov eax,[eax]               // eax = 列表中的第二个条目
		mov eax,[eax+0x08]          // eax = 获取到的Kernel32.dll基址(Win7下获取的是KernelBase.dll的基址)
		mov dwKernel32Addr,eax
		pop eax
	}

	return dwKernel32Addr;
}

注:可参考http://blog.youkuaiyun.com/obuyiseng/article/details/50456090

         此时我们就能够获取到Kernel32.dll的基址,那么就可以通过遍历Kernel32.dll的导出表获取到GetProcAddress函数的基址了。

         在Stub.cpp文件中获取GetProcAddress的基址代码如下:

DWORD GetGPAFunAddr()
{
	DWORD dwAddrBase = GetKernel32Base();

	// 1. 获取DOS头、NT头
	PIMAGE_DOS_HEADER pDos_Header;
	PIMAGE_NT_HEADERS pNt_Header;
	pDos_Header = (PIMAGE_DOS_HEADER)dwAddrBase;
	pNt_Header = (PIMAGE_NT_HEADERS)(dwAddrBase + pDos_Header->e_lfanew);

	// 2. 获取导出表项
	PIMAGE_DATA_DIRECTORY   pDataDir;
	PIMAGE_EXPORT_DIRECTORY pExport;
	pDataDir = pNt_Header->OptionalHeader.DataDirectory; 
	pDataDir = &pDataDir[IMAGE_DIRECTORY_ENTRY_EXPORT];
	pExport = (PIMAGE_EXPORT_DIRECTORY)(dwAddrBase + pDataDir->VirtualAddress);

 
	// 3、获取导出表的必要信息
	DWORD dwModOffset = pExport->Name;									// 模块的名称
	DWORD dwFunCount = pExport->NumberOfFunctions;						// 导出函数的数量
	DWORD dwNameCount = pExport->NumberOfNames;							// 导出名称的数量

	PDWORD pEAT = (PDWORD)(dwAddrBase + pExport->AddressOfFunctions);	// 获取地址表的RVA
	PDWORD pENT = (PDWORD)(dwAddrBase + pExport->AddressOfNames);		// 获取名称表的RVA
	PWORD pEIT = (PWORD)(dwAddrBase + pExport->AddressOfNameOrdinals);	//获取索引表的RVA

	// 4、获取GetProAddress函数的地址
	for (DWORD i = 0; i < dwFunCount; i++)
	{
		if (!pEAT[i])
		{
			continue;
		}

		// 4.1 获取序号
		DWORD dwID = pExport->Base + i;

		// 4.2 变量EIT 从中获取到 GetProcAddress的地址
		for (DWORD dwIdx = 0; dwIdx < dwNameCount; dwIdx++)
		{
			// 序号表中的元素的值 对应着函数地址表的位置
			if (pEIT[dwIdx] == i)
			{
				//根据序号获取到名称表中的名字
				DWORD dwNameOffset = pENT[dwIdx];
				char * pFunName = (char*)(dwAddrBase + dwNameOffset);

				//判断是否是GetProcAddress函数
				if (!strcmp(pFunName, "GetProcAddress"))
				{
					// 获取EAT的地址 并将GetProcAddress地址返回
					DWORD dwFunAddrOffset = pEAT[i];
					return dwAddrBase + dwFunAddrOffset;
				}
			}
		}
	}
	return -1;
}

6、动态加载API--获取到其他用到的API

         在Stub.h中定义相关的函数和函数指针,如下:

extern bool  InitializationAPI(); // 初始化各个API

// 基础API定义声明
typedef DWORD (WINAPI *LPGETPROCADDRESS)(HMODULE,LPCSTR);        // GetProcAddress
typedef HMODULE (WINAPI *LPLOADLIBRARYEX)(LPCTSTR,HANDLE,DWORD); // LoadLibaryEx
extern LPGETPROCADDRESS g_funGetProcAddress;
extern LPLOADLIBRARYEX  g_funLoadLibraryEx;


// 其他API定义声明
typedef VOID (WINAPI *LPEXITPROCESS)(UINT);                          // ExitProcess
typedef int (WINAPI *LPMESSAGEBOX)(HWND,LPCTSTR,LPCTSTR,UINT);       // MessageBox
typedef HMODULE (WINAPI *LPGETMODULEHANDLE)(LPCWSTR);                // GetModuleHandle
typedef BOOL (WINAPI *LPVIRTUALPROTECT)(LPVOID,SIZE_T,DWORD,PDWORD); // VirtualProtect
extern LPEXITPROCESS     g_funExitProcess;
extern LPMESSAGEBOX      g_funMessageBox;
extern LPGETMODULEHANDLE g_funGetModuleHandle;
extern LPVIRTUALPROTECT  g_funVirtualProtect;

在Stub.cpp中实现InitializationAPI函数,并初始化要使用的API函数。由于KernelBase.dll中没有导出LoadLibrary函数,所以为了兼容性考虑,我们在加载DLL的时候使用LoadLibraryExW。
LPGETPROCADDRESS  g_funGetProcAddress = nullptr;
LPLOADLIBRARYEX   g_funLoadLibraryEx = nullptr;

LPEXITPROCESS     g_funExitProcess = nullptr;
LPMESSAGEBOX      g_funMessageBox = nullptr;
LPGETMODULEHANDLE g_funGetModuleHandle = nullptr;
LPVIRTUALPROTECT  g_funVirtualProtect = nullptr;

bool InitializationAPI()
{
	HMODULE hModule;

	// 1. 初始化基础API 这里使用的是LoadLibraryExW
	g_funGetProcAddress = (LPGETPROCADDRESS)GetGPAFunAddr();
	g_funLoadLibraryEx = (LPLOADLIBRARYEX)g_funGetProcAddress((HMODULE)GetKernel32Base(), "LoadLibraryExW");

	// 2. 初始化其他API
	hModule = NULL;
	if (!(hModule = g_funLoadLibraryEx(L"kernel32.dll", NULL, NULL)))  return false;
	g_funExitProcess = (LPEXITPROCESS)g_funGetProcAddress(hModule, "ExitProcess");
	hModule = NULL;
	if (!(hModule = g_funLoadLibraryEx(L"user32.dll", NULL, NULL)))  return false;
	g_funMessageBox = (LPMESSAGEBOX)g_funGetProcAddress(hModule, "MessageBoxW");
	hModule = NULL;
	if (!(hModule = g_funLoadLibraryEx(L"kernel32.dll", NULL, NULL)))  return false;
	g_funGetModuleHandle = (LPGETMODULEHANDLE)g_funGetProcAddress(hModule, "GetModuleHandleW");
	hModule = NULL;
	if (!(hModule = g_funLoadLibraryEx(L"kernel32.dll", NULL, NULL)))  return false;
	g_funVirtualProtect = (LPVIRTUALPROTECT)g_funGetProcAddress(hModule, "VirtualProtect");

	return true;
}

7、添加解密函数

         在Stub.h中定义一个加密函数

extern void  Decrypt();           // 解密函数
在Stub.cpp中实现该函数

void Decrypt()
{
	// 在导出的全局变量中读取需解密区域的起始于结束VA
	PBYTE lpStart = g_stcParam.lpStartVA;
	PBYTE lpEnd = g_stcParam.lpEndVA;

	// 循环解密
	while (lpStart < lpEnd)
	{
		*lpStart ^= 0x15;
		lpStart++;
	}
}

8、完善start函数

         我们先初始化GLOBAL_PARAM结构,并在Start函数中调用InitializationAPI()初始化所有API ,调动Decrypt()进行解密,并跳转到被加壳程序的原始OEP。

extern __declspec(dllexport) GLOBAL_PARAM g_stcParam={0};

void start()
{
	// 1. 初始化所有API
	if (!InitializationAPI())  return;

	// 2. 解密宿主程序
	Decrypt();

	// 3. 跳转到OEP
	__asm jmp g_stcParam.dwOEP;
}

为了退出进程时的兼容性,在StubEntryPoint中添加退出进程的兼容代码,总体如下:
void __declspec(naked) StubEntryPoint()
{
	__asm sub esp, 0x50;        // 抬高栈顶,提高兼容性
	start();                    // 执行壳的主体部分
	__asm add esp, 0x50;        // 平衡堆栈

	// 主动调用ExitProcess函数退出进程可以解决一些兼容性问题
	if (g_funExitProcess)
	{
		g_funExitProcess(0);
	}

	__asm retn;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值