用户态模块隐藏小记之LDR断链
该方法主要是对PEB结构体中的LDR链表进行操作,分别把三种排序的LDR链进行断链操作以达到模块隐藏的效果,TEB和PEB的结构随着系统的不同会有所变化,本文以Windows 10中的32位程序为例子。该例子仅为简单的断链,通过测试目前会影响CreateToolhelp32Snapshot + Module32First获取模块信息。
0x00 PEB&TEB介绍
PEB和TEB是Windows进程管理的两个重要数据结构,在PEB和TEB结构中存在许多有用的成员,本文主要着重于PEB里的LDR双向链表,链表的内容为各模块的相关信息。
FS:[0x00] == TEB
FS:[0x30] == TEB.ProcessEnvironmentBlock == address of PEB
// 使用内联汇编的方式获取PEB位于内存的虚拟地址。
mov eax, dword ptr fs:[30h]
TEB32
PEB32
PEB_LDR_DATA
0x01 隐藏思路
在LDR表中有三种链表排序顺序,分别是模块加载顺序、模块内存顺序、模块初始化顺序。
这三个链表结构是PEB_LDR_DATA和LDR_DATA_TABLE_ENTRY共用的
LDR_DATA_TABLE_ENTRY
所以需要分别遍历这三种排序方式,将目标模块的LDR_DATA_TABLE_ENTRY从链上断开。
0x02 代码实现
模块遍历代码
#include <stdio.h>
#include <windows.h>
#include <Tlhelp32.h>
int main(int argc, char* argv[])
{
HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (INVALID_HANDLE_VALUE == hProcessSnapshot)
{
return 0;
}
DWORD dwPid = 0;
PROCESSENTRY32 pi;
pi.dwSize = sizeof(PROCESSENTRY32);
BOOL Ret = Process32First(hProcessSnapshot, &pi);
while (Ret)
{
if (strcmp("LDR_Break_Link.exe", pi.szExeFile) == 0)
{
dwPid = pi.th32ProcessID;
break;
}
Ret = Process32Next(hProcessSnapshot, &pi);
}
CloseHandle(hProcessSnapshot);
HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPid);//上面获取了进程PID,下面使用即可.
if (INVALID_HANDLE_VALUE == hSnapshot)
{
return 0;
}
MODULEENTRY32 mi;
mi.dwSize = sizeof(MODULEENTRY32);
BOOL bRet = Module32First(hSnapshot, &mi);
while (bRet)
{
printf("%s\n", mi.szModule);
bRet = Module32Next(hSnapshot, &mi);
}
CloseHandle(hSnapshot);
return 0;
}
断链代码
// LDR断链 隐藏模块
// Coding By Jev0n
#include <stdio.h>
#include <windows.h>
typedef struct _UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef struct _PEB_LDR_DATA {
ULONG Length;
BOOLEAN Initialized;
PVOID SsHandle;
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
} PEB_LDR_DATA, * PPEB_LDR_DATA;
typedef struct _LDR_DATA_TABLE_ENTRY
{
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
LPVOID BaseAddress;
LPVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
SHORT LoadCount;
SHORT TlsIndex;
HANDLE SectionHandle;
ULONG CheckSum;
ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;
int main(int argc, const char* argv[])
{
if (argc != 2)
{
printf("[+] Using Example: LDR_Break_Link.exe ntdll.dll \n");
return -1;
}
// PEB LDR
DWORD pPEB = 0;
PPEB_LDR_DATA pLDR = NULL;
_asm
{
mov eax ,fs:[0x30]
mov pPEB ,eax
mov eax ,[eax + 0x0C]
mov pLDR ,eax
}
// Break_Link
DWORD status = 0;
char* pDllName = argv[1];
HMODULE hMod = GetModuleHandle(pDllName);
PLIST_ENTRY pMiddle, pNext;
PLDR_DATA_TABLE_ENTRY pLDTE;
pMiddle = &(pLDR->InLoadOrderModuleList);
pNext = pMiddle->Flink;
do
{
pLDTE = CONTAINING_RECORD(pNext, LDR_DATA_TABLE_ENTRY, InLoadOrderModuleList);
if (hMod == pLDTE->BaseAddress)
{
status = 1;
// InLoadOrderModuleList
pLDTE->InLoadOrderModuleList.Blink->Flink = pLDTE->InLoadOrderModuleList.Flink;
pLDTE->InLoadOrderModuleList.Flink->Blink = pLDTE->InLoadOrderModuleList.Blink;
// InInitializationOrderModuleList
pLDTE->InInitializationOrderModuleList.Blink->Flink = pLDTE->InInitializationOrderModuleList.Flink;
pLDTE->InInitializationOrderModuleList.Flink->Blink = pLDTE->InInitializationOrderModuleList.Blink;
// InMemoryOrderModuleList
pLDTE->InMemoryOrderModuleList.Blink->Flink = pLDTE->InMemoryOrderModuleList.Flink;
pLDTE->InMemoryOrderModuleList.Flink->Blink = pLDTE->InMemoryOrderModuleList.Blink;
break;
}
pNext = pNext->Flink;
} while (pMiddle != pNext);
if (status)
{
printf("Success !\n");
}
else
{
printf("Error !\n");
}
getchar();
return 0;
}