您的位置:首页 > 其它

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

2016-01-04 16:41 537 查看
接:壳的编写(2)-- 编写壳(Stub)部分(1) http://blog.csdn.net/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.csdn.net/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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: