首页
论坛
课程
招聘
[原创]一点小技巧:利用Hook实现内存Dll(Dll数据重定向)
2022-9-7 11:56 9056

[原创]一点小技巧:利用Hook实现内存Dll(Dll数据重定向)

2022-9-7 11:56
9056

MemoryDll-DllRedirect

  • 这是一种很奇怪的实现Dll重定向到内存的方法。
  • 利用Hook实现。
  • 只能在Windows上使用。
  • Hook了NtOpenFile和NtMapViewOfSection。

源码解析:

  • MyHook.cpp里面仅仅是一个简单的APIHOOK,

    • 兼容x64和x86
    • 利用修改函数头实现
    • *兼容性未知。(可自行替换为自己的APIHOOK)
  • Main.cpp:

    • 首先Hook了NtOpenFile函数。
    • 然后利用LoadLibraryW加载“advapi32res.dll”(Dll无所谓,只要是一个Dll就行,这个Dll只是一个傀儡Dll,后面不会加载它的)
    • Hook的回调函数大致如下:
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
#if _WIN64
NTSTATUS MyNtOpenFile(PHANDLE FileHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    PIO_STATUS_BLOCK IoStatusBlock,
    ULONG ShareAccess,
    ULONG OpenOptions)
\#elif _WIN32
NTSTATUS __stdcall MyNtOpenFile(PHANDLE FileHandle,
    ACCESS_MASK DesiredAccess,
    POBJECT_ATTRIBUTES ObjectAttributes,
    PIO_STATUS_BLOCK IoStatusBlock,
    ULONG ShareAccess,
    ULONG OpenOptions)
#endif
{
 
    g_MyNtOpenFileHook.Suspended();
    //先暂停Hook
    NTSTATUS result;
    result = ((MyNtOpenFileCall)g_MyNtOpenFileHook.GetOldFunction())(FileHandle,
        DesiredAccess,
        ObjectAttributes,
        IoStatusBlock,
        ShareAccess,
        OpenOptions);
    //调用原函数
    if (wcsstr(ObjectAttributes->ObjectName->Buffer, L"advapi32res.dll")) {
        //如果是加载的advapi32res.dll
        g_MyNtOpenFileHook.Stop();
        //取消Hook
        g_MyNtMapViewOfSectionHook.Start("ntdll.dll", "NtMapViewOfSection", (FARPROC)MyNtMapViewOfSection);
        return result;
    }
    g_MyNtOpenFileHook.Restore();
    //恢复Hook
    return result;
}
  • 可以看到,其实加载Dll时先调用NtOpenFile打开Dll文件(内部接着会调用NtCreateSection和NtMapViewOfSection对文件句柄进行映射)
  • 接着便是Hook了NtMapViewOfSection函数,然后使用自己的“MyMapDll”函数将另外一个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
    43
    44
    45
    46
    47
    48
    49
    void MyMapDll(byte* DllData,
    PVOID* BaseAddress,
    PSIZE_T ViewSize
    ) {
    //获取基地址
    __int64 DllMemoryBase = (__int64)&DllData[0];
    PIMAGE_DOS_HEADER DOSHeader = (PIMAGE_DOS_HEADER)DllMemoryBase;
    //读出文件DOS头
    if (DOSHeader->e_magic == IMAGE_DOS_SIGNATURE) {
       //检测MZ头
       PIMAGE_NT_HEADERS NTHeader = (PIMAGE_NT_HEADERS)(DllMemoryBase + DOSHeader->e_lfanew);
       if (NTHeader->Signature == IMAGE_NT_SIGNATURE) {
           //检测PE头
           PVOID l_location, l_lpBaseAddress;
           PIMAGE_SECTION_HEADER Sectionheaders;
           __int64 SectionOffset;
           SectionOffset = DOSHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS);//IMAGE_SECTION_HEADER在内存中的偏移
           *BaseAddress = VirtualAlloc(NULL,
               NTHeader->OptionalHeader.SizeOfImage,
               MEM_COMMIT,
               PAGE_EXECUTE_READWRITE);
           //分配一块内存供给Dll使用
           *ViewSize = NTHeader->OptionalHeader.SizeOfImage;
           //下面开始根据SECTION信息将Dll数据逐步映射到内存
           int l_count = NTHeader->FileHeader.NumberOfSections;
           //拷贝DOS头
           RtlMoveMemory(*BaseAddress, &DllData[0], SectionOffset + sizeof(IMAGE_SECTION_HEADER) * l_count);
           //设置头部的保护
           VirtualProtect(&DllData[0], 0x1000, PAGE_READONLY, 0);
           for (int i = 0; i < l_count; i++) {
               Sectionheaders = (PIMAGE_SECTION_HEADER)(DllMemoryBase + SectionOffset);
               if (Sectionheaders->PointerToRawData != 0) {
                   //如果Sectionheaders->PointerToRawData不为0,表示该段是有数据的,需要拷贝。
                   l_location = (PVOID)(DllMemoryBase + Sectionheaders->PointerToRawData);
                   l_lpBaseAddress = (PVOID)((__int64)*BaseAddress + Sectionheaders->VirtualAddress);
                   //将段数据拷贝到分配的内存中
                   RtlMoveMemory(l_lpBaseAddress, l_location, Sectionheaders->SizeOfRawData);
                   //设置保护
                   VirtualProtect(l_lpBaseAddress,
                       Sectionheaders->Misc.VirtualSize,
                       GetProtect(Sectionheaders->Characteristics),
                       0);
               }
               SectionOffset += sizeof(IMAGE_SECTION_HEADER);
               //读取下一个SECTION
           }
       }
    }
    }
  • 其实说白了就是将Dll的SECTION全部加载到内存就可以了
  • 拷贝完真实的Dll之后,只需将“映射”到的地址和大小数据给回“NtMapViewOfSection”即可。
  • 什么IAT修复、TLS、异常之类东西,就全部交回给系统原生的Dll加载程序处理就好了。

一些闲话:

  • 为什么使用这样的方法加载Dll呢?
  • 因为这样可以提高内存Dll的稳定性(比较我们只处理了加载数据部分,其他部分全是系统处理的)
  • 本方法实现的MemoryDll可以基本兼容系统的相关Library命令(比如GetProcAddress)
  • 同时因为没有调用NtMapViewOfSection,可以绕过监听到Dll加载的方法(注意Dll文件的PE头、PEB中的双链并没有抹掉)
  • 不过话说回来,C++中这样搞的意义确实不是很大,可能对我来说主要的价值是学习价值吧。
  • 提示:如果你在其他论坛看到类似代码,那也可能我发的。

效果:

  • 测试前记得将源码中的“CDll_x64.dll”和“CDll_x86.dll”编译出来(也可以换其他Dll测试,经过测试VMP加壳程序不行,其他都还好)
  • x64测试:
  • x86测试:

源码:


[2022冬季班]《安卓高级研修班(网课)》月薪三万班招生中~

收藏
点赞2
打赏
分享
最新回复 (4)
雪    币: 2517
活跃值: 活跃值 (765)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
wonghouleong 活跃值 2022-9-7 14:53
2
0
牛啊牛啊 还以为只有易语言版 
雪    币: 928
活跃值: 活跃值 (641)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
ANormalUser 活跃值 2022-9-7 15:16
3
0
wonghouleong 牛啊牛啊 还以为只有易语言版 [em_13]
易语言版那个是我最早解析原理时写的一个例子。
后来用C++写才发现C++其实没必要用这么麻烦的方法,C++调用内存Dll可方便多了
雪    币: 2983
活跃值: 活跃值 (2309)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
caolinkai 活跃值 2022-9-30 08:51
4
0
感谢分享
雪    币: 838
活跃值: 活跃值 (450)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
恒大 活跃值 2022-9-30 09:02
5
0
是 win11 么? 这个圆角信息框好看多了
游客
登录 | 注册 方可回帖
返回