zoukankan      html  css  js  c++  java
  • 利用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 }
  • 相关阅读:
    Blank page instead of the SharePoint Central Administration site
    BizTalk 2010 BAM Configure
    Use ODBA with Visio 2007
    Handling SOAP Exceptions in BizTalk Orchestrations
    BizTalk与WebMethods之间的EDI交换
    Append messages in BizTalk
    FTP protocol commands
    Using Dynamic Maps in BizTalk(From CodeProject)
    Synchronous To Asynchronous Flows Without An Orchestration的简单实现
    WSE3 and "Action for ultimate recipient is required but not present in the message."
  • 原文地址:https://www.cnblogs.com/onetrainee/p/11674211.html
Copyright © 2011-2022 走看看