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

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

2020-1-6 15:40
18785

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

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

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


①DLL编写:




添加C语言代码如下:





然后进行编译生成:




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

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

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

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

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


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



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


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


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


 



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



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

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

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

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

  

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



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

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


①何为DLL?


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

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

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

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

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




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

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


①何为DLL?


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

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

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


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

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

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


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

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

1
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语言代码如下:


1
2
3
4
5
6
7
8
9
10
11
12
13
#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注入测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
#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为例:


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#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卸载吧,尝试一下:



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



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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//获取模块句柄
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;
}


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#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

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

1
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函数地址:


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





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

1
2
3
4
5
6
7
    DWORD dwOldProtect;
 
    VirtualProtectEx(OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetProcessIDByName("代码注入器.exe")), 
        addrfun, 
        6, 
        PAGE_EXECUTE_READWRITE, 
        &dwOldProtect);


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

1
2
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考研大佬,一起交流进步学习,高数线代概率论冲起来?

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#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;
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#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,我们编译生成:
 


丢到虚拟机测试运行:

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2020-1-23 16:04 被小迪xiaodi编辑 ,原因:
上传的附件:
收藏
免费 8
支持
分享
赞赏记录
参与人
雪币
留言
时间
704088
为你点赞~
2023-2-23 16:43
PLEBFE
为你点赞~
2023-1-19 05:02
艾鱼
为你点赞~
2020-11-29 19:31
xxRea
为你点赞~
2020-11-11 17:07
清风残月
为你点赞~
2020-10-31 00:34
ghcracker
为你点赞~
2020-2-27 20:54
Editor
为你点赞~
2020-1-6 19:44
PlaneJun
为你点赞~
2020-1-6 19:01
最新回复 (49)
雪    币: 3836
活跃值: (4142)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
6666666
2020-1-6 16:35
0
雪    币: 197
活跃值: (10398)
能力值: ( LV13,RANK:385 )
在线值:
发帖
回帖
粉丝
3
搞了半天一大堆废话. 最后是对Freelibrary下HOOK. 调用ZwUnMapViewOfSection 思路不错.加油.  总结为一句话 HOOK Freelibrary 调用Freelibrary. HOOK位置是不让调用真正的ZwUnmapViewOfSection 最后还原.FreeLibrary.
2020-1-6 16:54
0
雪    币: 5836
活跃值: (2038)
能力值: ( 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
活跃值: (2038)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
6
113254.. 迪哥66 考研也要写文章值得学习
写技术文章我感觉都是娱乐活动,写毕业论文实在是煎熬...另外数学也让我难受呀
2020-1-6 17:26
0
雪    币: 197
活跃值: (10398)
能力值: ( 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
雪    币: 6824
活跃值: (6376)
能力值: ( LV5,RANK:65 )
在线值:
发帖
回帖
粉丝
10
好的,感谢分享,思路不错,学习了!
2020-1-6 18:52
0
雪    币: 151
活跃值: (7686)
能力值: ( LV9,RANK:335 )
在线值:
发帖
回帖
粉丝
11
第一次发现隐藏DLL是这么的简单
2020-1-6 19:02
0
雪    币: 37864
活跃值: (65136)
能力值: (RANK:135 )
在线值:
发帖
回帖
粉丝
12
666 支持!
2020-1-6 19:45
0
雪    币: 8441
活跃值: (3015)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
13
BlackBone  可以了解下
2020-1-6 20:32
0
雪    币: 15
活跃值: (575)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
14
小迪xiaodi 写技术文章我感觉都是娱乐活动,写毕业论文实在是煎熬...另外数学也让我难受呀
写的很风趣啊,支持一下!
2020-1-7 08:29
0
雪    币: 7080
活跃值: (4936)
能力值: ( LV10,RANK:163 )
在线值:
发帖
回帖
粉丝
15
VirtualQuery
GetModuleBaseName
就撸死了。
2020-1-7 08:55
0
雪    币: 5836
活跃值: (2038)
能力值: ( 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
活跃值: (2038)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
18
BillBox 看着看着就蒙了 看来我水平不到家啊
加油哦
2020-1-7 16:03
0
雪    币: 106
活跃值: (155)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
我水平也不到家
2020-1-7 23:59
0
雪    币: 5836
活跃值: (2038)
能力值: ( LV8,RANK:130 )
在线值:
发帖
回帖
粉丝
20
薛定谔消失的弦 我水平也不到家[em_85]
加油,我写的帖子还是比较倾向于初学者的,以诙谐幽默的教学形式来撰写文章,希望大家能够喜欢并学到真东西
2020-1-8 00:06
0
雪    币: 433
活跃值: (2100)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
21
r3层 内存注入+hook zwquerymemory = 无敌
2020-1-8 00:06
0
雪    币: 83
活跃值: (1092)
能力值: ( 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
雪    币: 1630
活跃值: (2841)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
24
楼主,可以试一下 按 loadliabrary 的方式,自己手动加载DLL,将dll在内存中展开,然后HOOK关键点,挂钩后,再把头抹掉,最后手动释放
2020-1-8 09:45
0
雪    币: 1258
活跃值: (1439)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
25
小伙子,潜力无限
2020-1-8 09:51
0
游客
登录 | 注册 方可回帖
返回

账号登录
验证码登录

忘记密码?
没有账号?立即免费注册