3环的模块隐藏主要就是 断链和抹PE头,虽然我感觉现在一般的检测技术都会在0环进行检测,3环下的隐藏没有太大的作用,但是对于新手练手还是不错的。
抹去PE头的原理很简单,代码下面会给出,主要讲断链的原理.
原理:
在windows操作系统中,每个被映射到进程地址空间的模块(EXE或DLL),在进程中都有一个LDR_MODULE结构体与之对应,这个结构体存在与PEB(进程环境块)中,PEB存放着当前进程的一些信息,比如进程在内存中的基址,是否被调试等。一些Windows API 函数比如Module32First,Module32Next,就是通过LDR_MODULE来
检索进程中的模块,通过断链的方式可以让这种windows函数无法检测到我们的模块
PEB的地址在fs:[30]的位置
在WinDbg中输入命令 dd fs:[30]可以定位PEB的地址
上图画圈的地址就是进程中PEB的地址
跟进去
这个地址就是主模块LDR_MODULE信息存放的地址
再跟进去
LDR_MODULE中有3个链表存放着进程模块加载的信息,InLoadOrderModuleList 按模块加载顺序排序的链表,InMemoryOrderModuleList 按模块在内存中的顺序排序的链表,InInitialIzationOrderModuleList 按模块初始化顺序排序的链表
它们之间的关系如下
InLoadOrderModuleList
,
InMemoryOrderModuleList
,
InInitialIzationOrderModuleList
这是3个结构体,每个结构体只有2个成员FLink和BLink,分别存放着下一个模块的FLink地址和上一个模块的FLink地址,就像上图描述的一样。(其实这个结构就是标准的双链表)
代码部分#include <windows.h>
#include <Tlhelp32.h>
BOOL BreakLink(DWORD dwBaseAddr); //断链函数
int GetCount(); //测试函数,通过Module32First和Module32Next来检索当前进程中的模块数
//定义LDR_MODULE结构体
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
typedef struct _LDR_MODULE
{
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;
int main()
{
HMODULE hModule = LoadLibraryA("first.dll"); //加载一个dll,自己随便写一个dll,放在这个工程的工作目录就会
printf("count:%d\n", GetCount()); //第一次获取模块数
//抹去PE头代码
//去掉PE头部分的内存保护属性
DWORD dwOldProtect;
VirtualProtect((LPVOID)hModule, 1024, PAGE_READWRITE, &dwOldProtect);
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
//抹去MZ标志
pDosHeader->e_magic = 0;
//DOS头后面就是PE头
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
//抹去PE标志
pNtHeader->Signature = 0;
//恢复内存保护
VirtualProtect((LPVOID)hModule, 1024, dwOldProtect, &dwOldProtect);
//进行断链
BreakLink((DWORD)hModule);
//断链之后再测试模块数,会发现少了一个
printf("count:%d\n", GetCount());
MessageBoxA(NULL, "Test", "Test", MB_OK);
return 0;
}
int GetCount()
{
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
MODULEENTRY32 buffer;
int count = 0;
if (Module32First(snap, &buffer))
count++;
else
return GetLastError();
while (Module32Next(snap, &buffer))
count++;
return count;
}
BOOL BreakLink(DWORD dwBaseAddr)
{
//当前节点
PLDR_MODULE nowNode = NULL;
//上一个节点
PLDR_MODULE lastNode = NULL;
//下一个节点
PLDR_MODULE nextNode = NULL;
BOOL beSuccess = FALSE;
//找到主模块的LDR_MODULE
__asm {
pushad
pushfd
xor edx, edx
mov ebx, fs:[0x30] //找到PEB表
mov ecx, [ebx + 0xC] //找到PEB_LDR_DATA地址
mov edx, [ecx + 0xC] //找到LDR_MODULE链首节点地址
mov nowNode, edx
popfd
popad
}
do
{
//LDR_MODULE有个属性表示的是当前模块在内存中的基址
if ((DWORD)nowNode->BaseAddress == dwBaseAddr) //根据模块的基址来找对应的模块
{
beSuccess = TRUE;
break;
}
lastNode = nowNode;
nowNode = (PLDR_MODULE)nowNode->InLoadOrderModuleList.Flink;
} while (lastNode != nowNode);
if (!beSuccess)
{
printf("cannot find the dest module!\n");
return beSuccess;
}
//断开InLoadOrderModuleList链
//重建链表
nextNode = (PLDR_MODULE)nowNode->InLoadOrderModuleList.Flink;
lastNode->InLoadOrderModuleList.Flink = (PLIST_ENTRY)nextNode;
nextNode->InLoadOrderModuleList.Blink = (PLIST_ENTRY)lastNode;
//断开InMemoryOrderModuleList链
//重建链表
nextNode = (PLDR_MODULE)(nowNode->InMemoryOrderModuleList.Flink - sizeof(LIST_ENTRY));
lastNode->InMemoryOrderModuleList.Flink = nowNode->InMemoryOrderModuleList.Flink;
nextNode->InMemoryOrderModuleList.Blink = nowNode->InMemoryOrderModuleList.Blink;
//printf("%x\n",nowNode);
//断开InitializationOrderModuleList链
//重建链表
nextNode = (PLDR_MODULE)(nowNode->InInitializationOrderModuleList.Flink - 2 * sizeof(LIST_ENTRY));
lastNode->InInitializationOrderModuleList.Flink = nowNode->InMemoryOrderModuleList.Flink;
nextNode->InInitializationOrderModuleList.Blink = nowNode->InMemoryOrderModuleList.Blink;
return beSuccess;
}
断链的作用是可以躲开windows API的检测
但是抹去PE头我并不知道有什么作用,大家知道的可以说一下,谢谢了
#include <windows.h>
#include <Tlhelp32.h>
BOOL BreakLink(DWORD dwBaseAddr); //断链函数
int GetCount(); //测试函数,通过Module32First和Module32Next来检索当前进程中的模块数
//定义LDR_MODULE结构体
typedef struct _LSA_UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING, *PUNICODE_STRING;
typedef struct _LDR_MODULE
{
LIST_ENTRY InLoadOrderModuleList;
LIST_ENTRY InMemoryOrderModuleList;
LIST_ENTRY InInitializationOrderModuleList;
PVOID BaseAddress;
PVOID EntryPoint;
ULONG SizeOfImage;
UNICODE_STRING FullDllName;
UNICODE_STRING BaseDllName;
ULONG Flags;
USHORT LoadCount;
USHORT TlsIndex;
LIST_ENTRY HashTableEntry;
ULONG TimeDateStamp;
} LDR_MODULE, *PLDR_MODULE;
int main()
{
HMODULE hModule = LoadLibraryA("first.dll"); //加载一个dll,自己随便写一个dll,放在这个工程的工作目录就会
printf("count:%d\n", GetCount()); //第一次获取模块数
//抹去PE头代码
//去掉PE头部分的内存保护属性
DWORD dwOldProtect;
VirtualProtect((LPVOID)hModule, 1024, PAGE_READWRITE, &dwOldProtect);
PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule;
//抹去MZ标志
pDosHeader->e_magic = 0;
//DOS头后面就是PE头
PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
//抹去PE标志
pNtHeader->Signature = 0;
//恢复内存保护
VirtualProtect((LPVOID)hModule, 1024, dwOldProtect, &dwOldProtect);
//进行断链
BreakLink((DWORD)hModule);
//断链之后再测试模块数,会发现少了一个
printf("count:%d\n", GetCount());
MessageBoxA(NULL, "Test", "Test", MB_OK);
return 0;
}
int GetCount()
{
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPALL, 0);
MODULEENTRY32 buffer;
int count = 0;
if (Module32First(snap, &buffer))
count++;
else
return GetLastError();
while (Module32Next(snap, &buffer))
count++;
return count;
}
BOOL BreakLink(DWORD dwBaseAddr)
{
//当前节点
PLDR_MODULE nowNode = NULL;
//上一个节点
PLDR_MODULE lastNode = NULL;
//下一个节点
PLDR_MODULE nextNode = NULL;
BOOL beSuccess = FALSE;
//找到主模块的LDR_MODULE
__asm {
pushad
pushfd
xor edx, edx
mov ebx, fs:[0x30] //找到PEB表
mov ecx, [ebx + 0xC] //找到PEB_LDR_DATA地址
mov edx, [ecx + 0xC] //找到LDR_MODULE链首节点地址
mov nowNode, edx
popfd
popad
}
do
{
//LDR_MODULE有个属性表示的是当前模块在内存中的基址
if ((DWORD)nowNode->BaseAddress == dwBaseAddr) //根据模块的基址来找对应的模块
{
beSuccess = TRUE;
break;
}
lastNode = nowNode;
nowNode = (PLDR_MODULE)nowNode->InLoadOrderModuleList.Flink;
} while (lastNode != nowNode);
if (!beSuccess)
{
printf("cannot find the dest module!\n");
return beSuccess;
}
//断开InLoadOrderModuleList链
//重建链表
nextNode = (PLDR_MODULE)nowNode->InLoadOrderModuleList.Flink;
lastNode->InLoadOrderModuleList.Flink = (PLIST_ENTRY)nextNode;
nextNode->InLoadOrderModuleList.Blink = (PLIST_ENTRY)lastNode;
//断开InMemoryOrderModuleList链
//重建链表
nextNode = (PLDR_MODULE)(nowNode->InMemoryOrderModuleList.Flink - sizeof(LIST_ENTRY));
lastNode->InMemoryOrderModuleList.Flink = nowNode->InMemoryOrderModuleList.Flink;
nextNode->InMemoryOrderModuleList.Blink = nowNode->InMemoryOrderModuleList.Blink;
//printf("%x\n",nowNode);
//断开InitializationOrderModuleList链
//重建链表
nextNode = (PLDR_MODULE)(nowNode->InInitializationOrderModuleList.Flink - 2 * sizeof(LIST_ENTRY));
lastNode->InInitializationOrderModuleList.Flink = nowNode->InMemoryOrderModuleList.Flink;
nextNode->InInitializationOrderModuleList.Blink = nowNode->InMemoryOrderModuleList.Blink;
return beSuccess;
}
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)
最后于 2018-4-12 19:25
被想飞的超超编辑
,原因: 代码有些错误