首页
社区
课程
招聘
[原创]3环下的模块隐藏
发表于: 2018-4-9 17:16 9503

[原创]3环下的模块隐藏

2018-4-9 17:16
9503

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;
}

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2018-4-12 19:25 被想飞的超超编辑 ,原因: 代码有些错误
收藏
免费 3
支持
分享
最新回复 (14)
雪    币: 8188
活跃值: (2842)
能力值: ( LV9,RANK:180 )
在线值:
发帖
回帖
粉丝
2
抹去PE头就是不让扫内存时候扫到。
2018-4-9 20:34
0
雪    币: 2293
活跃值: (159)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
pe头地址错了吧,应该是PIMAGE_NT_HEADERS  pNtHeader  =  (PIMAGE_NT_HEADERS)((char*)hModule  +  pDosHeader->e_lfanew);
2018-4-10 14:47
0
雪    币: 7695
活跃值: (1544)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
4
sdestroyer pe头地址错了吧,应该是PIMAGE_NT_HEADERS pNtHeader = (PIMAGE_NT_HEADERS)((char*)hModule + pDosHeader->e_lfan ...
我这样写也是可以的,(PIMAGE_NT_HEADERS)(PDosHeader+1)      指针刚好移动了一个PIMAGE_DOS_HEADER,dos头过了之后就是PE头,正好定位在PE头,你可以去试试
2018-4-10 16:02
0
雪    币: 253
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
学习了
2018-4-11 09:36
0
雪    币: 350
活跃值: (13)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
想飞的超超 我这样写也是可以的,(PIMAGE_NT_HEADERS)(PDosHeader+1) 指针刚好移动了一个PIMAGE_DOS_HEADER,dos头过了之后就是PE头,正好定位在PE头,你可以去 ...
问题在于DOS头后面不一定要跟着PE头
2018-4-12 01:58
0
雪    币: 7695
活跃值: (1544)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
7
Diabloking 问题在于DOS头后面不一定要跟着PE头
嗯,我理解错了,谢谢了
2018-4-12 19:25
0
雪    币: 6818
活跃值: (153)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
2018-4-12 23:32
0
雪    币: 49
活跃值: (261)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
留个脚印,以后看
2018-4-16 21:23
0
雪    币: 300
活跃值: (2447)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
谢谢分享
2018-4-16 21:31
0
雪    币: 49
活跃值: (261)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
请问利用全局加载的方式,加载DLL  怎么隐藏DLL啊
2018-5-25 08:25
0
雪    币: 285
活跃值: (371)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
12
能猜到你看的是哪本书
2018-5-28 00:27
0
雪    币: 4913
活跃值: (2345)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
13
mark
2018-5-28 18:14
0
雪    币: 17
活跃值: (278)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
14
RtlGetCurrentPeb      了解一下
2018-5-28 18:22
0
雪    币: 140
活跃值: (125)
能力值: ( LV5,RANK:60 )
在线值:
发帖
回帖
粉丝
15
发帖之前看看是否重复也许是个好习惯
2018-5-28 19:32
0
游客
登录 | 注册 方可回帖
返回
//