抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

基于对Windows TEB/PEB的分析, 可以做一些免杀中的应用

动态获取API

通过对Windows TEB/PEB的分析, 可以获取到KERNEL32.dll的基址, 然后解析PE, 遍历导出表获取GetProcAddress函数地址, 再使用GetProcAddress函数获取其他函数地址

x32位动态获取API

32位下通过fs:[0]寄存器获取TEB, 然后偏移0x30得到PEB;

解析TEB/PEB获取KERNEL32.dll基址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// TEB结构体
_TEB* pTeb = NtCurrentTeb(); // 指向当前线程的线程环境块的指针
// PEB结构体
PDWORD pPeb = (PDWORD) * (PDWORD)((DWORD)pTeb + 0x30); // 偏移0x30得到PEB
// LDR结构体
PDWORD pLdr = (PDWORD) * (PDWORD)((DWORD)pPeb + 0x0c); // PEB偏移0x0c得到_PEB_LDR_DATA
// 进程所有模块
PDWORD pInLoadOrderLinks = (PDWORD) * (PDWORD)((DWORD)pLdr + 0x0c); // _PEB_LDR_DATA偏移0x0c得到InLoadOrderModuleList

// 主模块
PDWORD pModExe = (PDWORD)*pInLoadOrderLinks; // 第一个为主模块(程序本身)
// NtDll
PDWORD pModNtDll = (PDWORD)*pModExe; // 第二个为ntdll.dll
// Kernel32Dll
PDWORD pModKernel32 = (PDWORD)*pModNtDll; // 第三个为KERNEL32.dll
// 基址
dwKernel32 = pModKernel32[6]; // 偏移0x18 => 24 / 4 = 6 其中第6个为基址

printf("Kernel32 Base: %08x %S\n", dwKernel32, (const char*)pModKernel32[12]);

解析PE, 获取GetProcAddress

获取到KERNEL32.dll基址, 通过解析PE遍历KERNEL32.dll的导出表获取到GetProcAddress函数地址;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 解析pe文件
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dwKernel32;
// 解析NtHeader
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)(pDosHeader->e_lfanew + dwKernel32);
// 定位导出表
PIMAGE_DATA_DIRECTORY pExportDir = (PIMAGE_DATA_DIRECTORY)(pNtHeader->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT);
// 解析导出表
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(pExportDir->VirtualAddress + dwKernel32);
// 获取函数地址表
PDWORD dwAddrOfFunctions = (PDWORD)(pExport->AddressOfFunctions + dwKernel32);
// 获取函数序号表
PWORD dwAddrOfNameOrds = (PWORD)(pExport->AddressOfNameOrdinals + dwKernel32);
// 获取函数名称表
PDWORD dwAddrOfNames = (PDWORD)(pExport->AddressOfNames + dwKernel32);
// 获取函数名称总数
DWORD dwNumOfNames = (DWORD)pExport->NumberOfNames;
DWORD dwGetProcAddress = 0;
// 通过遍历函数名称总数
for (size_t i = 0; i < dwNumOfNames; i++)
{
// 获取函数名
const char* szFuncName = (char*)(dwAddrOfNames[i] + dwKernel32);
// 局部变量
char szGetProcAddress[] = { 'G','e','t','P','r','o','c','A','d','d','r','e','s','s' };
int nFlags = 0;
for (size_t j = 0; j < 14; j++)
{
if (szFuncName[j] == szGetProAddress[j])
{
nFlags++;
}
if (nFlags == 14)
{
dwGetProcAddress = (DWORD)(dwAddrOfFunctions[dwAddrOfNameOrds[i]] + dwKernel32);
break;
}
}
}

定义相关函数字符串以及函数原型并调用

定义函数名称时不能使用常量字符串, 因为程序会将字符串放在常量区段, 所以要对字符串进行打散处理, 以这种方式定义会将字符串存放在堆栈区域;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 定义LoadLibraryW函数字符串
char szLoadLibraryW[] = { 'L','o','a','d','L','i','b','r','a','r','y','W','\0' };
// 定义MessageBoxW函数字符串
char szMessagesBoxW[] = { 'M','e','s','s','a','g','e','B','o','x','W','\0' };
// 定义KERNEL32.dll字符串
char szKernel32[] = { 'K','e','r','n','e','l','3', '2', '.', 'd', 'l', 'l','\0' };
// 定义ExitProcess函数字符串
char szExitProcss[] = { 'E','x','i','t','P','r','o','c','e','s','s','\0' };
// 定义user32.dll字符串
WCHAR szUser32[] = { 'U','s','e','r','3','2','.','d','l','l', '\0' };
WCHAR szDokey[] = { 'D','o','k','e','y','\0' };
// 定义LoadLibraryW函数
typedef HMODULE(WINAPI* MyLoadLibraryW)(_In_ LPCWSTR lpLibFileName);
// 定义GetProcAddress函数
typedef FARPROC(WINAPI* MyGetProcAddress)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);
// 定义MessageBoxW函数
typedef int (WINAPI* MyMessageBoxW)(_In_opt_ HWND hWnd, _In_opt_ LPCWSTR lpText, _In_opt_ LPCWSTR lpCaption, _In_ UINT uType);
// 定义ExitProcess函数
typedef VOID(WINAPI* MyExitProcess)(_In_ UINT uExitCode);
HMODULE hKernel = (HMODULE)dwKernel32;

MyGetProcAddress pFuncGetProcAddress = (MyGetProcAddress)dwGetProcAddress;
if (pFuncGetProcAddress == NULL)
{
return -1;
}
MyLoadLibraryW pFuncLoadLibraryW = (MyLoadLibraryW)pFuncGetProcAddress(hKernel, szLoadLibraryW);
if (NULL == pFuncLoadLibraryW)
{
return -1;
}
HMODULE hUser32 = pFuncLoadLibraryW((LPCWSTR)szUser32);
MyMessageBoxW pFuncMessageBoxW = (MyMessageBoxW)pFuncGetProcAddress(hUser32, szMessagesBoxW);
pFuncMessageBoxW(NULL, szDokey, (LPCWSTR)szDokey, MB_OK);
MyExitProcess pFuncExitProcess = (MyExitProcess)pFuncGetProcAddress(hKernel, szExitProcss);
pFuncExitProcess(0);

x64位动态获取API

64位下通过gs:[0]寄存器获取TEB, 然后偏移0x60得到PEB; 64位下和32位获取方式不同;

解析TEP/PEB获取KERNEL32.dll基址

需要包含winternl.hintrin.h两个头文件;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   // 通过PEB获取KERNEL32.dll基址
PVOID pPeb = reinterpret_cast<PVOID>(__readgsqword(0x60));
if (!pPeb)
{
return -1;
}
// 计算Ldr地址的偏移量
ULONG64 ldrOrder = 0x18;
// 获取_PEB_LDR_DATA
PVOID pLdr = *reinterpret_cast<PVOID*>(reinterpret_cast<PBYTE>(pPeb) + ldrOrder);
if (!pLdr)
{
return -1;
}
// 计算InMemoryOrderModuleList地址的偏移量
ULONG64 moduleListOffset = offsetof(PEB_LDR_DATA, InMemoryOrderModuleList);
// 计算InMemoryOrderModuleList地址
PLIST_ENTRY pModList = reinterpret_cast<PLIST_ENTRY>(reinterpret_cast<PBYTE>(pLdr) + moduleListOffset);
// 获取KERNEL32.dll基址
PLIST_ENTRY pKernel32Entry = pModList->Flink->Flink->Flink;
PLDR_DATA_TABLE_ENTRY pKernelTableEntry = CONTAINING_RECORD(pKernel32Entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
// 获取KERNEL32.dll基址
PVOID pKernelBase = pKernelTableEntry->DllBase;

解析PE, 获取GetProcAddress

和32位一样, 解析PE遍历导出表获取GetProcAddress函数地址;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
   // 解析PE
PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)pKernelBase;
// 获取NT头
PIMAGE_NT_HEADERS pNt = (PIMAGE_NT_HEADERS)(pDos->e_lfanew + (DWORD64)pKernelBase);
// 获取导出表虚拟地址
PIMAGE_DATA_DIRECTORY pDir = (PIMAGE_DATA_DIRECTORY)(pNt->OptionalHeader.DataDirectory + IMAGE_DIRECTORY_ENTRY_EXPORT);
// 通过导出表虚拟地址获取导出表
PIMAGE_EXPORT_DIRECTORY pExport = (PIMAGE_EXPORT_DIRECTORY)(pDir->VirtualAddress + (DWORD64)pKernelBase);
// 获取函数地址表
PDWORD pwAddressOfFunctions = (PDWORD)(pExport->AddressOfFunctions + (DWORD64)pKernelBase);
// 获取函数名称表
PDWORD pwAddressOfNames = (PDWORD)(pExport->AddressOfNames + (DWORD64)pKernelBase);
// 获取函数序号表
PWORD pwAddressOfNameOrdinals = (PWORD)(pExport->AddressOfNameOrdinals + (DWORD64)pKernelBase);
// 获取函数名称总数
DWORD dwNumberOfNames = pExport->NumberOfNames;

DWORD64 dwGetProcAddress = 0;
// 通过函数名称总数遍历
for (size_t i = 0; i < dwNumberOfNames; i++)
{
// 获取函数名
const char* szFuncName = (char*)(pwAddressOfNames[i] + (DWORD64)pKernelBase);
// 局部变量
char szGetProcAddress[] = {'G','e','t','P','r','o','c','A','d','d','r','e','s','s'};
int nFlags = 0;
for (size_t j = 0; j < 14; j++)
{
if (szFuncName[j] == szGetProcAddress[j])
{
nFlags++;
}
if (nFlags == 14)
{
dwGetProcAddress = (DWORD64)(pwAddressOfFunctions[pwAddressOfNameOrdinals[i]] + (DWORD64)pKernelBase);
break;
}
}
}

定义相关函数字符串以及函数原型并调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
   // 定义LoadLibraryW字符串
char szLoadLibraryW[] = { 'L','o','a','d','L','i','b','r','a','r','y','W','\0' };
// 定义MessagesBoxW字符串
char szMessagesBoxW[] = { 'M','e','s','s','a','g','e','B','o','x','W','\0' };
// 定义KERNEL32.dll字符串
char szKernel32[] = { 'K','e','r','n','e','l','3', '2', '.', 'd', 'l', 'l','\0' };
// 定义ExitProcss字符串
char szExitProcss[] = { 'E','x','i','t','P','r','o','c','e','s','s','\0' };
WCHAR szUser32[] = { 'U','s','e','r','3','2','.','d','l','l', '\0' };
WCHAR szDokey[] = { 'D','o','k','e','y', '_', 'x', '6', '4', '\0' };

typedef HMODULE(WINAPI* MyLoadLibraryW)(_In_ LPCWSTR lpLibFileName);
typedef FARPROC(WINAPI* MyGetProcAddress)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName);
typedef int (WINAPI* MyMessageBoxW)(_In_opt_ HWND hWnd, _In_opt_ LPCWSTR lpText, _In_opt_ LPCWSTR lpCaption, _In_ UINT uType);
typedef VOID(WINAPI* MyExitProcess)(_In_ UINT uExitCode);
HMODULE hKernel = (HMODULE)pKernelBase;

MyGetProcAddress pFuncGetProcAddress = (MyGetProcAddress)dwGetProcAddress;
if (pFuncGetProcAddress == NULL)
{
return -1;
}
MyLoadLibraryW pFuncLoadLibraryW = (MyLoadLibraryW)pFuncGetProcAddress(hKernel, szLoadLibraryW);
if (NULL == pFuncLoadLibraryW)
{
return -1;
}
HMODULE hUser32 = pFuncLoadLibraryW((LPCWSTR)szUser32);
MyMessageBoxW pFuncMessageBoxW = (MyMessageBoxW)pFuncGetProcAddress(hUser32, szMessagesBoxW);
pFuncMessageBoxW(NULL, szDokey, (LPCWSTR)szDokey, MB_OK);
MyExitProcess pFuncExitProcess = (MyExitProcess)pFuncGetProcAddress(hKernel, szExitProcss);
pFuncExitProcess(0);

评论