首页
社区
课程
招聘
[原创]VC黑防日记(一):DLL隐藏和逆向(上)
发表于: 2020-1-6 15:40 18071

[原创]VC黑防日记(一):DLL隐藏和逆向(上)

2020-1-6 15:40
18071

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 


①DLL编写:




添加C语言代码如下:





然后进行编译生成:




------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 


------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 



这样的话,我们就把FreeLibrary阉割了,阉割后,就没有办法将DLL从内存中清出去,且擦出了注入的痕迹


这就好比隔壁老王办完事儿把床铺床单收拾整齐,擦出了痕迹,然后躲在了衣柜里~


这里的办事儿当然指的是隔壁老王去找隔壁老李媳妇儿打游戏啦,别多想


 



------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 



------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

【实验平台】:Win7 x64
【开发平台】:Win10 x64 + VS2017
【调试工具】:Ollydbg
【本文作者】:小迪xiaodi,一个非著名技术段子文章菜鸟写手
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

看到这个标题,DLL隐藏,还逆向?这有什么鸟关系?很多大佬一定是这个表情:

  

且慢,听我给大家娓娓道来~



--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

0x01:何为DLL?又为何要将其隐藏?


①何为DLL?


首先来看一下来自于百度的解释:

动态链接库英文为DLL,是Dynamic Link Library的缩写。DLL是一个包含可由多个程序,同时使用的代码和数据的库。例如,在 Windows 操作系统中,Comdlg32.dll 执行与对话框有关的常见函数。因此,每个程序都可以使用该 DLL 中包含的功能来实现“打开”对话框。这有助于避免代码重用和促进内存的有效使用。
通过使用 DLL,程序可以实现模块化,由相对独立的组件组成。例如,一个计帐程序可以按模块来销售。可以在运行时将各个模块加载到主程序中(如果安装了相应模块)。因为模块是彼此独立的,所以程序的加载速度更快,而且模块只在相应的功能被请求时才加载。

啰里啰唆说了一堆,实际上就是说:使用DLL这种技术可以使得程序模块化,同时让程序加载速度更快~

更加详细的解释请大家参考:

https://encyclopedia.thefreedictionary.com/Dynamic-link+library




--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

0x01:何为DLL?又为何要将其隐藏?


①何为DLL?


首先来看一下来自于百度的解释:

动态链接库英文为DLL,是Dynamic Link Library的缩写。DLL是一个包含可由多个程序,同时使用的代码和数据的库。例如,在 Windows 操作系统中,Comdlg32.dll 执行与对话框有关的常见函数。因此,每个程序都可以使用该 DLL 中包含的功能来实现“打开”对话框。这有助于避免代码重用和促进内存的有效使用。
通过使用 DLL,程序可以实现模块化,由相对独立的组件组成。例如,一个计帐程序可以按模块来销售。可以在运行时将各个模块加载到主程序中(如果安装了相应模块)。因为模块是彼此独立的,所以程序的加载速度更快,而且模块只在相应的功能被请求时才加载。

啰里啰唆说了一堆,实际上就是说:使用DLL这种技术可以使得程序模块化,同时让程序加载速度更快~


首先来看一下来自于百度的解释:

动态链接库英文为DLL,是Dynamic Link Library的缩写。DLL是一个包含可由多个程序,同时使用的代码和数据的库。例如,在 Windows 操作系统中,Comdlg32.dll 执行与对话框有关的常见函数。因此,每个程序都可以使用该 DLL 中包含的功能来实现“打开”对话框。这有助于避免代码重用和促进内存的有效使用。
通过使用 DLL,程序可以实现模块化,由相对独立的组件组成。例如,一个计帐程序可以按模块来销售。可以在运行时将各个模块加载到主程序中(如果安装了相应模块)。因为模块是彼此独立的,所以程序的加载速度更快,而且模块只在相应的功能被请求时才加载。

动态链接库英文为DLL,是Dynamic Link Library的缩写。DLL是一个包含可由多个程序,同时使用的代码和数据的库。例如,在 Windows 操作系统中,Comdlg32.dll 执行与对话框有关的常见函数。因此,每个程序都可以使用该 DLL 中包含的功能来实现“打开”对话框。这有助于避免代码重用和促进内存的有效使用。
通过使用 DLL,程序可以实现模块化,由相对独立的组件组成。例如,一个计帐程序可以按模块来销售。可以在运行时将各个模块加载到主程序中(如果安装了相应模块)。因为模块是彼此独立的,所以程序的加载速度更快,而且模块只在相应的功能被请求时才加载。


更加详细的解释请大家参考:

https://encyclopedia.thefreedictionary.com/Dynamic-link+library

https://encyclopedia.thefreedictionary.com/Dynamic-link+library



另外,我们在日常生活中,经常可以看到dll的影子,比如以PC版微信为例:

我们打开其安装目录,便可以看到许多文件后缀为“.dll”的文件



如果看不到文件后缀,大家可以在这样设置:



OK,这个呢,就是给大家讲的一些生活中我们应该知道的DLL的常识了,怎么样,学到了没?

看到这里感觉自己学不会也没关系,送大家一本书,包邮 ,评论区抽奖送书,包邮~



②DLL为什么要被隐藏?


DLL为何要被隐藏,这事儿啊说来话长,看看这其中到底有啥事~

先举个例子:

老李和老王是邻居,也就是说老李隔壁是老王,老王也就是所谓的“隔壁老王”。

一天,老李去出差,只剩下老李媳妇儿在家。这天正值越黑风高,老王大半夜嘭嘭嘭敲开了老李家的大门,老李媳妇儿见了面便说:“死鬼儿,这么晚才来”。于是,在今天晚上...



可没料到,老王刚进卧室,老李突然回来了,老王赶忙将自己隐藏到了衣柜里,姿势刚好像DLL的拼写,这个姿势,我画了一下,大致是这样的:



后面的故事,我不再清楚了,因为,这全都是我一个人通过努力编出来的



最后总结一句话:没做坏事,干嘛要隐藏自己呢?所以,隐藏了自己,那有很大的可能性要做坏事

既然可以做坏事,我们看看都有什么坏事可以做吧:

1.木马以DLL形式注入进正常进程并隐藏自己,导致无法追踪溯源,如键盘记录木马、感染型木马

2.游戏外挂通过该种方式躲避游戏自身的“模块检测”,如DXF的第三方木马检测


DLL为何要被隐藏,这事儿啊说来话长,看看这其中到底有啥事~

先举个例子:

老李和老王是邻居,也就是说老李隔壁是老王,老王也就是所谓的“隔壁老王”。

一天,老李去出差,只剩下老李媳妇儿在家。这天正值越黑风高,老王大半夜嘭嘭嘭敲开了老李家的大门,老李媳妇儿见了面便说:“死鬼儿,这么晚才来”。于是,在今天晚上...



可没料到,老王刚进卧室,老李突然回来了,老王赶忙将自己隐藏到了衣柜里,姿势刚好像DLL的拼写,这个姿势,我画了一下,大致是这样的:



后面的故事,我不再清楚了,因为,这全都是我一个人通过努力编出来的



最后总结一句话:没做坏事,干嘛要隐藏自己呢?所以,隐藏了自己,那有很大的可能性要做坏事

既然可以做坏事,我们看看都有什么坏事可以做吧:

1.木马以DLL形式注入进正常进程并隐藏自己,导致无法追踪溯源,如键盘记录木马、感染型木马

2.游戏外挂通过该种方式躲避游戏自身的“模块检测”,如DXF的第三方木马检测
除了坏事儿,在一些常见的攻防对抗中,如若用到Windows木马,不仅要注意免杀工作,有时也需要通过该技术实现持久隐藏攻击

OK,这就是我们第一部分的内容,怎么样,还要继续坚持往下学习吗?



------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

0x02:DLL隐藏常见手法


通过在谷歌搜索“DLL隐藏 看雪”,我发现结果点进去大都是这样的:







通过在谷歌搜索“DLL隐藏 看雪”,我发现结果点进去大都是这样的:





我赶忙给自己倒了一杯82年的哇哈哈压了压惊,一定是我技术太菜了,一看到这种大批量的汇编,直接慌了,劝退



总结了一下,如果要实现DLL隐藏,方法有很多,但是大体分为两类:

1.手动载入:

所谓手动载入就是自己去实现LoadLibrary函数,记得科锐钱老师哪个视频讲过:“他检测或处理了Windows api那咋办啊,自己实现啊!”,高,实在是高,如果自己去实现,就可以更加精确的控制内存读入信息,从理论上讲,这种方法隐藏效果是最棒的,但是实现一个基本的PEloader并不是一件容易的事情,如果还要考虑兼容性完美,就必须还得妥善处理TLS,资源,线程等问题,在这方面处理的话,反正我不会,劝退。。。



2.痕迹消除:

这个就比较好说了,我们在注入DLL的时候,通常要选择去调用 LoadLibrary函数,然后再擦除痕迹,这样不用去处理那些TLS,资源,线程等为了解决兼容性产生的问题。但是Windows大家都清楚,表面风平浪静,背地里风起云涌,说不准现在你电脑的XXX安全卫士正在对你的电脑做些什么呢,这些都是你不通过技术手段看不到的东西,实际上没有什么好的方法把痕迹真正的消除。

以DLL擦除痕迹为例,网上大多数的方法都是通过PEB双向断链,需要很多的硬编码,麻烦不说,通过内存暴力搜索还是会露出尾巴来

攻防无绝对,攻防本身就是一个提升本身技术的过程,只要能够通过一个比较合适的方法,实现相应的功能,便具有可行性




------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

0x03:DLL编写与注入测试-小白入门


①DLL编写:




添加C语言代码如下:


#include <Windows.h>

BOOL WINAPI DllMain(HMODULE hDll, DWORD dwReason, LPVOID lpReserved)
{
	DisableThreadLibraryCalls(hDll);

	if (dwReason == DLL_PROCESS_ATTACH)
	{
		//================================== OPTIONAL =========================================
		MessageBoxA(0, "Message Test","Message Title", 0); 
	}
	return 1;
}

同时在项目属性中设置:





然后进行编译生成:




②DLL注入测试:
#include <Windows.h>

BOOL WINAPI DllMain(HMODULE hDll, DWORD dwReason, LPVOID lpReserved)
{
	DisableThreadLibraryCalls(hDll);

	if (dwReason == DLL_PROCESS_ATTACH)
	{
		//================================== OPTIONAL =========================================
		MessageBoxA(0, "Message Test","Message Title", 0); 
	}
	return 1;
}

同时在项目属性中设置:



我们进行注入的话,可以自己编写注入器,也可以利用其它工具,在这里我们为了方便暂且使用其他工具辅助我们进行测试:

工具:代码注入器,见附件~





使用方法:打开两份,用一个程序注入进另一个程序,依次点击:





继续依次点击:





我将DLL放在了桌面,于是选中它,点击注入就可以了,提示了一个信息框:




刚好对应我们的源代码:





接下来我们观察进程中是否已经注入进了我们的DLL,依次点击:



这个时候,程序已经被我们“射”进去了一个DLL,这款工具显示出了该DLL在QQ进程中的内存地址:0x710B0000



最后总结一下,效果就是这么个效果,木马或者外挂通常采用类似的技术手段 “注入”进其他进程,与其他进程融为一体,为所欲为

注入进去之后,通常这些木马或者外挂DLL就开始做一些隐藏工作了,接下来的内容,也是我们要讨论的东西,开始~



------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

0x04:手写代码实现注入


谈完DLL编写和利用工具注入测试,我们来大致了解一下代码实现注入,方法有很多,但原理大致相同,我在这里以线程注入dll为例:


#include <Windows.h>

void Inject(int pID, char* Path)
{
	//获取进程句柄
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);

	//申请一块内存给DLL路径
	LPVOID pReturnAddress = VirtualAllocEx(hProcess, NULL, strlen(Path) + 1, MEM_COMMIT, PAGE_READWRITE);

	//写入路径到上一行代码申请的内存中
	WriteProcessMemory(hProcess, pReturnAddress, Path, strlen(Path) + 1, NULL);


	//获取LoadLibraryA函数的地址
	HMODULE hModule = LoadLibrary("KERNEL32.DLL");
	LPTHREAD_START_ROUTINE lpStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");


	//创建远程线程-并获取线程的句柄
	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, pReturnAddress, 0, NULL);

	//等待线程事件
	WaitForSingleObject(hThread, 2000);


	//防止内存泄露
	CloseHandle(hThread);
	CloseHandle(hProcess);

}

int main()
{
        //传dll路径
	const char* a = "C:\\Users\\asus\\Desktop\\C++\\FirstDll.dll";

        //传入进程ID
	Inject(12944, (char*)a);

	return 0;
}


根据传入参数,记录我们要传入的数据:

进程ID:3872
路径:C:\Users\86186\Desktop\mydll.dll



修改路径和pid,我们编译生成:
 


丢到虚拟机测试运行:




成功注入,弹出信息框,通过分析源代码,我们总结了一下注入流程:

1.在别人的程序里开辟内存空间A
2.将 LoadLibrary 函数参数写入A
3.获取LoadLibrary函数地址
4.在别人的程序里远程执行 LoadLibrary实现加载外部DLL

由此可见 LoadLibrary多么重要~这个小玩意儿竟然实现了注入~




------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

0x04:与 LoadLibrary相对的FreeLibrary


既然一个东西有安装,那必然就有卸载,凡是都是相对的,不可能只有单方面(我什么时候把哲学搞这么明白了)

话说回来,如果存在DLL注入,能安装进去,那也就必然能从上面卸载下来

安装是LoadLibrary,卸载就是FreeLibrary,为啥是呢,因为我是从MSDN上面看到的:

https://docs.microsoft.com/zh-cn/windows/win32/api/libloaderapi/nf-libloaderapi-loadlibrarya

https://docs.microsoft.com/zh-cn/windows/win32/api/libloaderapi/nf-libloaderapi-freelibrary






同样的道理,注入可以远程调用LoadLibrary注射进去,那么我们也可以远程调用FreeLibrary卸载吧,尝试一下:



看一下该函数都有什么参数:



发现需要传一个句柄,那么我们直接放源码吧,可以远程获取进程模块的句柄:

//获取模块句柄
HMODULE GetProcessModuleHandleByName(DWORD pid, LPCSTR ModuleName)
{
	MODULEENTRY32 ModuleInfo;
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
	if (!hSnapshot)
	{
		return 0;
	}
	ZeroMemory(&ModuleInfo, sizeof(MODULEENTRY32));
	ModuleInfo.dwSize = sizeof(MODULEENTRY32);
	if (!Module32First(hSnapshot, &ModuleInfo))
	{
		return 0;
	}
	do
	{
		if (!lstrcmpi(ModuleInfo.szModule, ModuleName))
		{
			CloseHandle(hSnapshot);
			return ModuleInfo.hModule;
		}
	} while (Module32Next(hSnapshot, &ModuleInfo));
	CloseHandle(hSnapshot);
	return 0;
}
//获取进程id
DWORD GetProcessIDByName(const char* pName)
{
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (INVALID_HANDLE_VALUE == hSnapshot) {
		return NULL;
	}
	PROCESSENTRY32 pe = { sizeof(pe) };
	for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe)) {
		if (strcmp(pe.szExeFile, pName) == 0) {
			CloseHandle(hSnapshot);
			return pe.th32ProcessID;
		}
		//printf("%-6d %s\n", pe.th32ProcessID, pe.szExeFile);
	}
	CloseHandle(hSnapshot);
	return 0;
}


简单的修改一下,然后去调用该函数,完整代码如下:

#include <Windows.h>
#include <stdio.h>
#include "tlhelp32.h"

void Inject(int pID, char* Path)
{
	//获取进程句柄
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);

	//申请一块内存给DLL路径
	LPVOID pReturnAddress = VirtualAllocEx(hProcess, NULL, strlen(Path) + 1, MEM_COMMIT, PAGE_READWRITE);

	//写入路径到上一行代码申请的内存中
	WriteProcessMemory(hProcess, pReturnAddress, Path, strlen(Path) + 1, NULL);


	//获取LoadLibraryA函数的地址
	HMODULE hModule = LoadLibrary("KERNEL32.DLL");
	LPTHREAD_START_ROUTINE lpStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");


	//创建远程线程-并获取线程的句柄
	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, pReturnAddress, 0, NULL);

	//等待线程事件
	WaitForSingleObject(hThread, 2000);


	//防止内存泄露
	CloseHandle(hThread);
	CloseHandle(hProcess);

}

HMODULE GetProcessModuleHandleByName(DWORD pid, LPCSTR ModuleName)
{
	MODULEENTRY32 ModuleInfo;
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
	if (!hSnapshot)
	{
		return 0;
	}
	ZeroMemory(&ModuleInfo, sizeof(MODULEENTRY32));
	ModuleInfo.dwSize = sizeof(MODULEENTRY32);
	if (!Module32First(hSnapshot, &ModuleInfo))
	{
		return 0;
	}
	do
	{
		if (!lstrcmpi(ModuleInfo.szModule, ModuleName))
		{
			CloseHandle(hSnapshot);
			return ModuleInfo.hModule;
		}
	} while (Module32Next(hSnapshot, &ModuleInfo));
	CloseHandle(hSnapshot);
	return 0;
}

DWORD GetProcessIDByName(const char* pName)
{
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (INVALID_HANDLE_VALUE == hSnapshot) {
		return NULL;
	}
	PROCESSENTRY32 pe = { sizeof(pe) };
	for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe)) {
		if (strcmp(pe.szExeFile, pName) == 0) {
			CloseHandle(hSnapshot);
			return pe.th32ProcessID;
		}
		//printf("%-6d %s\n", pe.th32ProcessID, pe.szExeFile);
	}
	CloseHandle(hSnapshot);
	return 0;
}


void UnInject(int pID, char* Path)
{
	//获取进程句柄
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);

	LPVOID pReturnAddress = GetProcessModuleHandleByName(GetProcessIDByName("代码注入器.exe"), "mydll.dll");

	//获取LoadLibraryA函数的地址
	HMODULE hModule = LoadLibrary("KERNEL32.DLL");
	LPTHREAD_START_ROUTINE lpStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");


	//创建远程线程-并获取线程的句柄
	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, pReturnAddress, 0, NULL);

	//等待线程事件
	WaitForSingleObject(hThread, 2000);


	//防止内存泄露
	CloseHandle(hThread);
	CloseHandle(hProcess);

}

int main()
{
	const char* a = "C:\\Users\\86186\\Desktop\\mydll.dll";

	UnInject(GetProcessIDByName("代码注入器.exe"), (char*)a);

	getchar();
	return 0;
}



上一秒,这个DLL还在程序内:



下一秒,我们运行Uninject程序,然后刷新一下,它就不见了:







------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

0x05:FreeLibrary卸载DLL跟DLL隐藏有什么关系?


很多朋友都会这么问,你卸载了,就没了,你还隐藏个鸡儿?

按道理确实是这样,但是也正是如此,真正的隐藏就是卸载掉,让他真正的不存在!

我们因此可以从Freelibrary函数开刀,分析该函数的流程,然后下手



------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

0x06:分析FreeLibrary


先从MSDN给的官方文档下手:




总结一下,大致就是四部分:

1.判断DLL句柄是否有效,有效就说明该DLL存在于进程中

2. 递减模块的引用计数,且判断是否为0

3. 调用模块的DllMain函数响应 DLL_PROCESS_DETACH消息

4.从进程空间撤销对DLL的内存映射

我们不难看出,前三步只是假把式,最后一笔才是点睛之笔,才真正把DLL从内存清出去

因此,如果我们让系统帮我们清楚前面的痕迹,等到清楚内存映射的时候,直接阻止它,这样既清了痕迹,又保了我们的DLL




------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

0x07:逆向FreeLibrary


打开OD,附加进程,跳转到函数处:



此时我们可以看到这个函数在里面到底偷偷做了些什么:



我们会发现,其调用了很多来自ntdll的函数,而且最后,是通过调用下面的call来实现最后的内存清除工作:

76021DF7  |.  FF15 C0110176 call    dword ptr [<&ntdll.NtUnmapViewOfSection>]               ;  ntdll_12.ZwUnmapViewOfSection





不懂就查: https://docs.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-zwunmapviewofsection

看一下这玩意到底是不是清除内存的:

The ZwUnmapViewOfSection routine unmaps a view of a section from the virtual address space of a subject process.





我们发现的确是这样的,那么就专门搞它就可以了~



------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

0x07:Patch函数首地址阉割FreeLibrary


先进函数内部观察:




patch方法也很简单,首地址,利用shellcode技巧直接retn 8




这样的话,我们就把FreeLibrary阉割了,阉割后,就没有办法将DLL从内存中清出去,且擦出了注入的痕迹


这就好比隔壁老王办完事儿把床铺床单收拾整齐,擦出了痕迹,然后躲在了衣柜里~


这里的办事儿当然指的是隔壁老王去找隔壁老李媳妇儿打游戏啦,别多想


 



------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

0x08:ShellCode代码实现


1.先获取ZwUnmapViewOfSection函数地址:


	DWORD a = GetProcAddress(LoadLibrary("ntdll.dll"), "ZwUnmapViewOfSection");





2.修改该处的内存属性使其可读写:

	DWORD dwOldProtect;

	VirtualProtectEx(OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcessIDByName("代码注入器.exe")), 
        addrfun, 
        6, 
        PAGE_EXECUTE_READWRITE, 
        &dwOldProtect);


3.写入内存数据阉割该函数:

	BYTE shellcode[] = { 0xc2, 0x08 , 0x00 , 0x90 , 0x90 };
	WriteProcessMemory(OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcessIDByName("代码注入器.exe")), addrfun, shellcode, 5, NULL);

效果:



如此以来,我们便可以再重新整理一下代码和思路了:

先阉割,再FreeLibrary即可!然后再还原,不还原不行,万一别的地方调用有可能会出错,所以你办完事,就不能露痕迹,代码如下:



------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

0x09:DLL隐藏测试


①:先注入一个DLL到程序:







啊!进去了,

②编译运行我们自己的程序:




重新点击小电脑附加进程,重新选中,你会发现DLL没有了,我们再去用OD看一下:

发现模块信息的确被擦除了,没有了路径和版本信息




换用CheatEngine工具测试一下:发现完全消失



换用修改版的OD-吾爱破解OD测试:



发现竟然暴露了,说明该方法依然是存在不足的,对某些程序依然会被检测

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- 

0x10:总结


1.该方法采用正向分析逻辑,然后逆向分析函数实现流程,通过阉割函数,实现擦除注入痕迹
2.理论上DLL注入痕迹已经完全擦除,但是内存中仍然保留着完整的镜像,擦除的也只是操作系统中数据结构的记录
3.如果要对抗暴力穷搜,还需处理PE文件头特征

后续:干掉吾爱破解OD这类工具DLL的检测 


欲知后事如何,且听我下回分晓~ 





最近还要写毕业论文,实在不知道怎么写,我写技术文章可以一直写一直写,写论文几小时扣不出几个字,是我太俗了,不太会写官方的语言

另外有没有2021考研大佬,一起交流进步学习,高数线代概率论冲起来?

最后代码有些乱,贴给大家:

#include <Windows.h>
#include <stdio.h>
#include "tlhelp32.h"

void Inject(int pID, char* Path)
{
	//获取进程句柄
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);

	//申请一块内存给DLL路径
	LPVOID pReturnAddress = VirtualAllocEx(hProcess, NULL, strlen(Path) + 1, MEM_COMMIT, PAGE_READWRITE);

	//写入路径到上一行代码申请的内存中
	WriteProcessMemory(hProcess, pReturnAddress, Path, strlen(Path) + 1, NULL);


	//获取LoadLibraryA函数的地址
	HMODULE hModule = LoadLibrary("KERNEL32.DLL");
	LPTHREAD_START_ROUTINE lpStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");


	//创建远程线程-并获取线程的句柄
	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, pReturnAddress, 0, NULL);

	//等待线程事件
	WaitForSingleObject(hThread, 2000);


	//防止内存泄露
	CloseHandle(hThread);
	CloseHandle(hProcess);

}

HMODULE GetProcessModuleHandleByName(DWORD pid, LPCSTR ModuleName)
{
	MODULEENTRY32 ModuleInfo;
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid);
	if (!hSnapshot)
	{
		return 0;
	}
	ZeroMemory(&ModuleInfo, sizeof(MODULEENTRY32));
	ModuleInfo.dwSize = sizeof(MODULEENTRY32);
	if (!Module32First(hSnapshot, &ModuleInfo))
	{
		return 0;
	}
	do
	{
		if (!lstrcmpi(ModuleInfo.szModule, ModuleName))
		{
			CloseHandle(hSnapshot);
			return ModuleInfo.hModule;
		}
	} while (Module32Next(hSnapshot, &ModuleInfo));
	CloseHandle(hSnapshot);
	return 0;
}

DWORD GetProcessIDByName(const char* pName)
{
	HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
	if (INVALID_HANDLE_VALUE == hSnapshot) {
		return NULL;
	}
	PROCESSENTRY32 pe = { sizeof(pe) };
	for (BOOL ret = Process32First(hSnapshot, &pe); ret; ret = Process32Next(hSnapshot, &pe)) {
		if (strcmp(pe.szExeFile, pName) == 0) {
			CloseHandle(hSnapshot);
			return pe.th32ProcessID;
		}
		//printf("%-6d %s\n", pe.th32ProcessID, pe.szExeFile);
	}
	CloseHandle(hSnapshot);
	return 0;
}



void UnInject(int pID, char* Path)
{
	//获取进程句柄
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);

	//WriteProcessMemory("")

	LPVOID pReturnAddress = GetProcessModuleHandleByName(GetProcessIDByName("代码注入器.exe"), "mydll.dll");

	//获取LoadLibraryA函数的地址
	HMODULE hModule = LoadLibrary("KERNEL32.DLL");
	LPTHREAD_START_ROUTINE lpStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");


	//创建远程线程-并获取线程的句柄
	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, pReturnAddress, 0, NULL);

	//等待线程事件
	WaitForSingleObject(hThread, 2000);


	//防止内存泄露
	CloseHandle(hThread);
	CloseHandle(hProcess);

}

int main()
{
	const char* a = "C:\\Users\\86186\\Desktop\\mydll.dll";

	HANDLE hToken = NULL;
	//打开当前进程的访问令牌
	int hRet = OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &hToken);

	if (hRet)
	{
		TOKEN_PRIVILEGES tp;
		tp.PrivilegeCount = 1;
		//取得描述权限的LUID
		LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid);
		tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
		//调整访问令牌的权限
		AdjustTokenPrivileges(hToken, FALSE, &tp, sizeof(tp), NULL, NULL);

		CloseHandle(hToken);
	}

	//定位函数地址
	DWORD addrfun = GetProcAddress(LoadLibrary("ntdll.dll"), "ZwUnmapViewOfSection");
	printf("%x \n\n", addrfun);
	DWORD dwOldProtect;
	//修改内存属性
	VirtualProtectEx(OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcessIDByName("代码注入器.exe")), addrfun, 6, PAGE_EXECUTE_READWRITE, &dwOldProtect);
	//阉割函数
	BYTE shellcode[] = { 0xc2, 0x08 , 0x00 , 0x90 , 0x90 };
	WriteProcessMemory(OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcessIDByName("代码注入器.exe")), addrfun, shellcode, 5, NULL);

	//调用FreeLibrary实现卸载
	UnInject(GetProcessIDByName("代码注入器.exe"), (char*)a);

	//还原原函数
	//B8 27 00 00 00
	BYTE Oldcode[] = { 0xB8, 0x27 , 0x00 , 0x00 , 0x00 };
	WriteProcessMemory(OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcessIDByName("代码注入器.exe")), addrfun, Oldcode, 5, NULL);


	getchar();
	return 0;
}



#include <Windows.h>

void Inject(int pID, char* Path)
{
	//获取进程句柄
	HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pID);

	//申请一块内存给DLL路径
	LPVOID pReturnAddress = VirtualAllocEx(hProcess, NULL, strlen(Path) + 1, MEM_COMMIT, PAGE_READWRITE);

	//写入路径到上一行代码申请的内存中
	WriteProcessMemory(hProcess, pReturnAddress, Path, strlen(Path) + 1, NULL);


	//获取LoadLibraryA函数的地址
	HMODULE hModule = LoadLibrary("KERNEL32.DLL");
	LPTHREAD_START_ROUTINE lpStartAddress = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "LoadLibraryA");


	//创建远程线程-并获取线程的句柄
	HANDLE hThread = CreateRemoteThread(hProcess, NULL, 0, lpStartAddress, pReturnAddress, 0, NULL);

	//等待线程事件
	WaitForSingleObject(hThread, 2000);


	//防止内存泄露
	CloseHandle(hThread);
	CloseHandle(hProcess);

}

int main()
{
        //传dll路径
	const char* a = "C:\\Users\\asus\\Desktop\\C++\\FirstDll.dll";

        //传入进程ID
	Inject(12944, (char*)a);

	return 0;
}


根据传入参数,记录我们要传入的数据:

进程ID:3872
路径:C:\Users\86186\Desktop\mydll.dll



修改路径和pid,我们编译生成:
 


丢到虚拟机测试运行:

[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)

最后于 2020-1-23 16:04 被小迪xiaodi编辑 ,原因:
上传的附件:
收藏
免费 8
支持
分享
最新回复 (49)
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
6666666
2020-1-6 16:35
0
雪    币: 871
活跃值: (9841)
能力值: ( LV13,RANK:385 )
在线值:
发帖
回帖
粉丝
3
搞了半天一大堆废话. 最后是对Freelibrary下HOOK. 调用ZwUnMapViewOfSection 思路不错.加油.  总结为一句话 HOOK Freelibrary 调用Freelibrary. HOOK位置是不让调用真正的ZwUnmapViewOfSection 最后还原.FreeLibrary.
2020-1-6 16:54
0
雪    币: 5836
活跃值: (1913)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
4
张新琪 [em_85]搞了半天一大堆废话. 最后是对Freelibrary下HOOK. 调用ZwUnMapViewOfSection[em_85] 思路不错.加油.[em_63] 总结为一句话 HOOK F ...
那写文章不得有头有尾儿啊,得让技术不好的也能看懂才行
2020-1-6 16:57
1
雪    币: 128
活跃值: (169)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
迪哥66 考研也要写文章值得学习
2020-1-6 17:21
0
雪    币: 5836
活跃值: (1913)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
6
113254.. 迪哥66 考研也要写文章值得学习
写技术文章我感觉都是娱乐活动,写毕业论文实在是煎熬...另外数学也让我难受呀
2020-1-6 17:26
0
雪    币: 871
活跃值: (9841)
能力值: ( LV13,RANK:385 )
在线值:
发帖
回帖
粉丝
7
小迪xiaodi [em_86]那写文章不得有头有尾儿啊,得让技术不好的也能看懂才行
这倒是,哈哈.鼓励鼓励.继续发原创帖 
2020-1-6 17:44
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
技术×
2020-1-6 18:06
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
9
表情包✔
2020-1-6 18:06
0
雪    币: 6095
活跃值: (5510)
能力值: ( LV5,RANK:65 )
在线值:
发帖
回帖
粉丝
10
好的,感谢分享,思路不错,学习了!
2020-1-6 18:52
0
雪    币: 141
活跃值: (7486)
能力值: ( LV9,RANK:335 )
在线值:
发帖
回帖
粉丝
11
第一次发现隐藏DLL是这么的简单
2020-1-6 19:02
0
雪    币: 26245
活跃值: (63297)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
12
666 支持!
2020-1-6 19:45
0
雪    币: 8057
活跃值: (2625)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
13
BlackBone  可以了解下
2020-1-6 20:32
0
雪    币: 15
活跃值: (393)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
小迪xiaodi 写技术文章我感觉都是娱乐活动,写毕业论文实在是煎熬...另外数学也让我难受呀
写的很风趣啊,支持一下!
2020-1-7 08:29
0
雪    币: 6533
活跃值: (4331)
能力值: ( LV10,RANK:163 )
在线值:
发帖
回帖
粉丝
15
VirtualQuery
GetModuleBaseName
就撸死了。
2020-1-7 08:55
0
雪    币: 5836
活跃值: (1913)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
16
Editor 666 支持!
2020-1-7 10:15
0
雪    币: 7
活跃值: (25)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
17
看着看着就蒙了 看来我水平不到家啊
2020-1-7 12:07
0
雪    币: 5836
活跃值: (1913)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
18
BillBox 看着看着就蒙了 看来我水平不到家啊
加油哦
2020-1-7 16:03
0
雪    币: 106
活跃值: (140)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
我水平也不到家
2020-1-7 23:59
0
雪    币: 5836
活跃值: (1913)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
20
薛定谔消失的弦 我水平也不到家[em_85]
加油,我写的帖子还是比较倾向于初学者的,以诙谐幽默的教学形式来撰写文章,希望大家能够喜欢并学到真东西
2020-1-8 00:06
0
雪    币: 433
活跃值: (1910)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
21
r3层 内存注入+hook zwquerymemory = 无敌
2020-1-8 00:06
0
雪    币: 83
活跃值: (1087)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
22
图片太多了 收藏不过来 感谢分享
2020-1-8 04:17
0
雪    币: 205
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
23
模块扫描,可以尝试一下在DLL卸载的HOOK函数里擦除DLL的PE头?哈哈哈,要是不行就算了,要是可以就白嫖楼主代码了
2020-1-8 08:12
0
雪    币: 1613
活跃值: (2827)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
24
楼主,可以试一下 按 loadliabrary 的方式,自己手动加载DLL,将dll在内存中展开,然后HOOK关键点,挂钩后,再把头抹掉,最后手动释放
2020-1-8 09:45
0
雪    币: 1258
活跃值: (1434)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
小伙子,潜力无限
2020-1-8 09:51
0
游客
登录 | 注册 方可回帖
返回
//