• 利用C++实现模块隐藏(R3层断链)


    一、模块隐藏的实现原理

      普通API查找模块实现思路:其通过查询在R3中的PEB(Process Environment Block 进程环境块)与TEB(Thread Environment Block 进程环境块)来找到一个双向链表,通过遍历双向链表中某一成员(字符串)来查找全部模块。

      模块隐藏实现思路:在R3层的模块隐藏,我们需要做的就是将其该链表断链,将某一模块从这个双向链表中摘除,这样再调用传统的API时就会搜索不到。

    二、结构体成员详细介绍

    <1> TEB结构体 -- 内存地址为 fs:[0] 处。

    使用Windbg的 "dt _TEB"命令来查看TEB结构体

    kd> dt _TEB
    ntdll!_TEB
       +0x000 NtTib            : _NT_TIB
       +0x01c EnvironmentPointer : Ptr32 Void
       +0x020 ClientId         : _CLIENT_ID
       +0x028 ActiveRpcHandle  : Ptr32 Void
       +0x02c ThreadLocalStoragePointer : Ptr32 Void
       +0x030 ProcessEnvironmentBlock : Ptr32 _PEB
       +0x034 LastErrorValue   : Uint4B

    1. 属性介绍 

      1.1)_NT_TIB:重点两个属性,栈顶与栈大小。

       http://www.nirsoft.net/kernel_struct/vista/NT_TIB.html

      1.2) _CLIENT_ID: 存储该进程ID与当前主线程ID。

      https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-tsts/a11e7129-685b-4535-8d37-21d4596ac057?redirectedfrom=MSDN

      1.3) _PEB:进程环境块 ,记住其在 TEB 偏移 0x30处即可。

    2. 通过olldbg查看该结构体

      2.1) 打开任意进程,在寄存器窗口找到 fs:[0],查看其内存地址。

        

      2.2) 在内存窗口使用命令 "db 5E7000" 跳转到该内存,使用地址格式(长型-地址)显示。

        

    <2>  PEB结构体 -- fs:[0x30]

    使用 Windbg 指令 dt _PEB 查看 PEB结构体,重点关注最后一个 进程加载信息表。

    kd> dt _PEB
    ntdll!_PEB
       +0x000 InheritedAddressSpace : UChar
       +0x001 ReadImageFileExecOptions : UChar
       +0x002 BeingDebugged    : UChar
       +0x003 BitField         : UChar
       +0x003 ImageUsesLargePages : Pos 0, 1 Bit
       +0x003 IsProtectedProcess : Pos 1, 1 Bit
       +0x003 IsLegacyProcess  : Pos 2, 1 Bit
       +0x003 IsImageDynamicallyRelocated : Pos 3, 1 Bit
       +0x003 SkipPatchingUser32Forwarders : Pos 4, 1 Bit
       +0x003 SpareBits        : Pos 5, 3 Bits
       +0x004 Mutant           : Ptr32 Void
       +0x008 ImageBaseAddress : Ptr32 Void
       +0x00c Ldr              : Ptr32 _PEB_LDR_DATA // PEB_LOADER_DATA 进程加载信息表

    1. 查看 _PEB_LDR_DATA 进程加载信息表 的结构体

      1.1)重点关注 0x00c处的指针,其指向 _PEB_LDR_DATA 这个结构体,在这个结构体中 0x00c、0x014、0x01c 分别表示 模块加载顺序 / 加载后在内存中的顺序 / 模块初始化的顺序。

            kd > dt _PEB_LDR_DATA
            ntdll!_PEB_LDR_DATA
            + 0x000 Length           : Uint4B
            + 0x004 Initialized : UChar
            + 0x008 SsHandle : Ptr32 Void
            + 0x00c InLoadOrderModuleList : _LIST_ENTRY  // 模块加载顺序
            + 0x014 InMemoryOrderModuleList : _LIST_ENTRY // 加载后在内存中的顺序
            + 0x01c InInitializationOrderModuleList : _LIST_ENTRY // 模块初始化的顺序
            + 0x024 EntryInProgress : Ptr32 Void
            + 0x028 ShutdownInProgress : UChar
            + 0x02c ShutdownThreadId : Ptr32 Void

      2.2)理解其三个成员的顺序,其指向_LDR_DATA_TABLE_ENTRY元素中开始的三个成员,而 _LDR_DATA_TABLE_ENTRY 中存储着就是关于有关模块信息的元素(比如模块名等)

            kd > dt _LDR_DATA_TABLE_ENTRY
            ntdll!_LDR_DATA_TABLE_ENTRY
            + 0x000 InLoadOrderLinks : _LIST_ENTRY   
            + 0x008 InMemoryOrderLinks : _LIST_ENTRY
            + 0x010 InInitializationOrderLinks : _LIST_ENTRY
            + 0x018 DllBase : Ptr32 Void  // 模块基地址
            + 0x01c EntryPoint : Ptr32 Void  // 入口函数(对于 exe 模块有效)
            + 0x020 SizeOfImage : Uint4B  // 模块大小
            + 0x024 FullDllName : _UNICODE_STRING  // 完成模块名称(带路径)
            + 0x02c BaseDllName : _UNICODE_STRING // 模块名称
            + 0x034 Flags : Uint4B

    2. 使用olldbg来查看查找首先加载模块的模块名称(TEB->PEB-> InLoadOrderModuleList -> BaseDllName)

      2.1)接之前TEB内容查找到PEB的所在位置 fs:[0x30]。

      2.2)  在其0x00c处发现InLoadOrderModuleList成员,其指向的是一个_LDR_DATA_TABLE_ENTRY 结构体。

        

      2.3)  跳转到 _LDR_DATA_TABLE_ENTRY 结构体,从0x0c开始依次是三个 _LIST_ENTRY 结构体,该结构体双向链表存储着两个地址。

        

      2.4)选中第一个进入,在其偏移0x02c处(UNICODE结构体占四字),可以查看字符串名称。

        

      2.5)通过开头 _LIST_ENTRY结构体可以遍历前一个模块的内容和下一个模块的内容。

        

    三、利用C++断链来实现模块隐藏

      如果你看懂上面分析,则源代码非常好理解。

    复制代码
      1 // 隐藏模块.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
      2 //
      3 
      4 #include "pch.h"
      5 #include <iostream>
      6 #include <Windows.h>
      7 
      8 
      9 /* 所需要的结构体
     10 1. _LDR_DATA_TABLE_ENTRY 链表指向数据
     11 2. _PEB_LDR_DATA 表示其 PEB0x处指向的数据表
     12 3. _LIST_ENTRY 指针指向的链表
     13 */
     14 
     15 typedef struct _LSA_UNICODE_STRING {
     16     USHORT Length;
     17     USHORT MaximumLength;
     18     PWSTR  Buffer;
     19 }
     20 UNICODE_STRING, *PUNICODE_STRING;
     21 
     22 typedef struct _PEB_LDR_DATA
     23 {
     24     DWORD Length; // +0x00
     25     bool Initialized; // +0x04
     26     PVOID SsHandle; // +0x08
     27     LIST_ENTRY InLoadOrderModuleList; // +0x0c
     28     LIST_ENTRY InMemoryOrderModuleList; // +0x14
     29     LIST_ENTRY InInitializationOrderModuleList;// +0x1c
     30 } PEB_LDR_DATA, *PPEB_LDR_DATA; // +0x24
     31 
     32 typedef struct _LDR_MODULE
     33 {
     34     LIST_ENTRY          InLoadOrderModuleList;
     35     LIST_ENTRY          InMemoryOrderModuleList;
     36     LIST_ENTRY          InInitializationOrderModuleList;
     37     void*               BaseAddress;
     38     void*               EntryPoint;
     39     ULONG               SizeOfImage;
     40     UNICODE_STRING   FullDllName;
     41     UNICODE_STRING      BaseDllName;
     42     ULONG               Flags;
     43     SHORT               LoadCount;
     44     SHORT               TlsIndex;
     45     HANDLE              SectionHandle;
     46     ULONG               CheckSum;
     47     ULONG               TimeDateStamp;
     48 } LDR_MODULE, *PLDR_MODULE;
     49 
     50 //所谓模块句柄,即该模块的入口地址
     51 void hide_module(char* szDllName)
     52 {
     53     HMODULE hMod = GetModuleHandleA(szDllName);
     54     PLIST_ENTRY Head, Cur;
     55     PPEB_LDR_DATA ldr;
     56     PLDR_MODULE ldm;
     57     __asm
     58     {
     59         mov eax, fs:[0x30]
     60         mov ecx, [eax + 0x0c] //Ldr  
     61         mov ldr, ecx
     62     }
     63     Head = &(ldr->InLoadOrderModuleList);
     64     Cur = Head->Flink;
     65     do
     66     {
     67         ldm = CONTAINING_RECORD(Cur, LDR_MODULE, InLoadOrderModuleList);
     68         if (hMod == ldm->BaseAddress)
     69         {
     70             // 三个链表同时给断掉
     71             ldm->InLoadOrderModuleList.Blink->Flink =
     72                 ldm->InLoadOrderModuleList.Flink;
     73             ldm->InLoadOrderModuleList.Flink->Blink =
     74                 ldm->InLoadOrderModuleList.Blink;
     75 
     76             //
     77             ldm->InInitializationOrderModuleList.Blink->Flink =
     78                 ldm->InInitializationOrderModuleList.Flink;
     79             ldm->InInitializationOrderModuleList.Flink->Blink =
     80                 ldm->InInitializationOrderModuleList.Blink;
     81             
     82             //
     83             ldm->InMemoryOrderModuleList.Blink->Flink =
     84                 ldm->InMemoryOrderModuleList.Flink;
     85             ldm->InMemoryOrderModuleList.Flink->Blink =
     86                 ldm->InMemoryOrderModuleList.Blink;
     87             break;
     88         }
     89         Cur = Cur->Flink;
     90     } while (Head != Cur);
     91 }
     92 
     93 
     94 
     95 int main()
     96 {
     97     // 通过模块名,来获取模块句柄
     98     printf("****按任意键隐藏模块*****");
     99     getchar();
    100     hide_module((char*)"kernel32.dll");
    101     printf("****隐藏模块完成*****");
    102     getchar();
    103     getchar();
    104 
    105 }
    复制代码
  • 相关阅读:
    MyBatis入门基础
    复制复杂链表
    二叉树中和为某一值的所有路径
    树的层次遍历
    Statement, PreparedStatement和CallableStatement的区别
    JSP有哪些动作?
    latex 输入矩阵
    Struts简单入门实例
    在Eclipse里面配置Struts2
    Windows使用Github
  • 原文地址:https://www.cnblogs.com/ylaoda/p/11678432.html
Copyright © 2020-2023  润新知