当前位置: 首页 > article >正文

获取Kernel32基地址

暴力搜索

32位在4G内存搜索有一定可行性,但是处理起来其实还是比较麻烦的,因为内存不可读会触发异常,需要对这些异常问题进行处理。

优化思路:缩小范围、增大搜索步长

(1)不优化,原始内存特征匹配,容易出错,利用复杂。

(2)优化暴力搜索,有三种方法

方法一

只要系统没有做模块基址重定位,那么32位下kernel32的加载地址在0x70000000-0x80000000之间,然后Kernel32.dll加载是64k对齐的,所以查找次数<256MB/64K+1= 4097次,就可以找到。

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

int main()
{
    HANDLE kernelA = LoadLibrary(L"kernel32.dll");
    printf("0x:%p\n", kernelA);
    system("pause");
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

但是这里判断定位成功条件仍然需要采取两重判断,先判断MZ头再解析PE结构来获取DLL名称进行判断,从而来降低在其他环境出现地址错误的概率。

导入表与exe实际加载顺序:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

ntdll.dll->kernel32.dll->ucrtbase.dll->kernelbase.dll->…

可以看到关键的系统模块都分配在了0x70000000上面,故单一匹配MZ头不是100%准确。

方法二

什么是异常处理链表

当异常发生时,系统从fs:[0]指向的内存地址处取出ExceptionList字段,然后从ExceptionList字段指向的_EXCEPTION_REGISTRATION_RECORD结构中取出handler字段,并根据其中的地址去调用异常处理程序(回调函数)。

异常处理链表是提到的由_EXCEPTION_REGISTRATION_RECORD结构构成的单链表

typedef struct _EXCEPTION_REGISTRATION_RECORD
{
     PEXCEPTION_REGISTRATION_RECORD Next;
     PEXCEPTION_DISPOSITION Handler;
} EXCEPTION_REGISTRATION_RECORD, *PEXCEPTION_REGISTRATION_RECORD;

Next指向异常处理程序的地址,prev 则指向下一个 _EXCEPTION_REGISTRATION_RECORD 结构体,来构成一个单向链表。

异常处理链表有什么特点

当异常发生时,系统会遍历异常处理链表,直到找到正确的异常处理程序。链表最后一项的prev值为0xFFFFFFFF,说明链表已经遍历完毕。

最后一项指向的是系统默认的位于Kernel32.dll的UnhandledExceptionFilter顶层异常处理程序的过滤函数,该过滤函数的地址是存在于Kernel32.dll内存空间的

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

查找Kernel32.dll加载基址

1)取fs:[0]的值即ExceptionList指针指向的地址赋予给edx寄存器

2)判断Next指针指向的值是否为0xffffffff计算机负数用补码表示即-1,是的话

mov edx, [edx],将值传递到edx寄存器中,接着mov edx,[edx+4]将Handler指向的值赋值给edx,此时edx就在Kernel32.dll内存空间中,然后开始逐一递减dec edx来回溯PE头<-cmp word ptr [edx], ‘ZM’,数值存储比较采用小端字节序,CPU读取从低地址读到高地址,所以这里是’ZM’而不是’MZ’,网上有些代码是错的,如果不是-1,那么就遍历下一个mov edx, [edx]

代码实现

#include <stdio.h>
#include <windows.h>
int main()
{
    unsigned int kernelAddr;
    __asm {
        mov edx, fs: [0] ;
    Foreach:
        cmp [edx], 0xffffffff
        je Handle; //if equal : jump   
        mov edx, [edx];
        jmp Foreach;

    Handle:
        mov edx, [edx + 4];

    _Loop:
        cmp word ptr[edx], 'ZM';
        jz Kernel;
        dec edx;
        xor dx, dx;
        jmp _Loop
     Kernel :
        mov kernelAddr, edx;
    }
    printf(TEXT("Kernel32.dll address: %x\r\n"), kernelAddr);
    printf(TEXT("LoadLibrary Kernel32.dll address: %x\r\n"),
        LoadLibrary(TEXT("kernel32.dll")));
    return 0;
}

基于PEB搜索

TEB->PEB

TEB(Thread Environment Block,线程环境块)系统在此TEB中保存频繁使用的线程相关的数据。位于用户地址空间,在比 PEB 所在地址低的地方。用户模式下,当前线程的TEB位于独立的4KB段(页),可通过CPU的FS寄存器来访问该段,一般存储在[FS:0]

PEB(Process Environment Block,进程环境块)存放进程信息,每个进程都有自己的PEB信息。位于用户地址空间。可在TEB结构地址偏移0x30处获得PEB的地址位置。

查看结构Windbg 命令:

TEB: !tebdt -r1 ntdll!_teb

PEB: !pebdt -r1 ntdll!_peb

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

我们已经知道可以通过fs:[0]寄存器访问到TEB的地址,这里我们又知道了可以通过TEB结构偏移0x30处指向的地址是PEB结构地址,即fs:[0]->TEB->PEB,在这一步完成PEB地址的定位。

PEB结构

微软文档:https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb

typedef struct _PEB {
  BYTE                          Reserved1[2];
  BYTE                          BeingDebugged;
  BYTE                          Reserved2[1];
  PVOID                         Reserved3[2];
  PPEB_LDR_DATA                 Ldr;
  PRTL_USER_PROCESS_PARAMETERS  ProcessParameters;
  PVOID                         Reserved4[3];
  PVOID                         AtlThunkSListPtr;
  PVOID                         Reserved5;
  ULONG                         Reserved6;
  PVOID                         Reserved7;
  ULONG                         Reserved8;
  ULONG                         AtlThunkSListPtr32;
  PVOID                         Reserved9[45];
  BYTE                          Reserved10[96];
  PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine;
  BYTE                          Reserved11[128];
  PVOID                         Reserved12[1];
  ULONG                         SessionId;
} PEB, *PPEB;

文档中很多是保留(Reserved)字段,这里我们关注下其中一个成员Ldr,其结构为PPEB_LDR_DATA。

微软文档:https://learn.microsoft.com/en-us/windows/win32/api/winternl/ns-winternl-peb_ldr_data

Contains information about the loaded modules for the process.
包含有关该过程的加载模块的信息。

typedef struct _PEB_LDR_DATA {
  BYTE       Reserved1[8];
  PVOID      Reserved2[3];
  LIST_ENTRY InMemoryOrderModuleList;
} PEB_LDR_DATA, *PPEB_LDR_DATA;

第三个参数InMemoryOrderModuleList

The head of a doubly-linked list that contains the loaded modules for the process. Each item in the list is a pointer to an LDR_DATA_TABLE_ENTRY structure. For more information, see Remarks.
双向链表的头部包含进程的加载模块。链表的每一个都是指向LDR_DATA_TABLE_ENTRY结构的指针

这个链表到底有什么信息

typedef struct _LDR_DATA_TABLE_ENTRY {
    PVOID Reserved1[2];
    LIST_ENTRY InMemoryOrderLinks;
    PVOID Reserved2[2];
    PVOID DllBase; // 模块基地址
    PVOID EntryPoint;
    PVOID Reserved3;
    UNICODE_STRING FullDllName;// 模块名称
    BYTE Reserved4[8];
    PVOID Reserved5[3];
    union {
        ULONG CheckSum;
        PVOID Reserved6;
    };
    ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

文档是不够全面的,下面我们用Windbg来看下具体的结构和值
!peb->dt -r1 0x774bdca0 _PEB_LDR_DATA
!peb
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

dt -r1 0x778a5d80 _PEB_LDR_DATA
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

可以看到这里,除了文档InMemoryOrderModuleList,实际还有两个:

InLoadOrderModuleList
InMemoryOrderModuleList
InInitializationOrderModuleLists

这个其实是模块在不同状态的顺序

InLoadOrderModuleList 指的是模块加载的顺序

InMemoryOrderModuleList指的是在内存的排列顺序

InInitializationOrderModuleLists 指的是模块初始化装载顺序。

这里选择跟进InLoadOrderModuleList指向的结构

1)dt -r1 _LIST_ENTRY 0x00485bc0(这里取第二个,第一个是exe本身)->dt -r1 0x00485ab8 _LDR_DATA_TABLE_ENTRY

2)lm 列举出加载的模块信息
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

从这图可以得出两个信息,Flink总是指向下一个_LDR_DATA_TABLE_ENTRY结构对应加载顺序的Flink指针,_LDR_DATA_TABLE_ENTRY在0x2c处是加载模块的名称,在0x18偏移处,是该模块的加载基地址。

基于上述认识,使用Windbg遍历一下InMemoryOrderModuleList加载顺序的完整链结构:

(1)dt -r1 0x004f5bc0-0x8 _LDR_DATA_TABLE_ENTRY
第一个结构是:*.exe
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)dt -r1 0x004f5ab8-0x8 _LDR_DATA_TABLE_ENTRY
第二个模块:ntdll.dll
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(3)dt -r1 0x004f5fa8-0x8 _LDR_DATA_TABLE_ENTRY
第三个模块:kerner32.dll
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

程序,遍历三个链表内容

#include<stdio.h>
#include<windows.h>

typedef struct UNICODE_STRING
{
    USHORT _ength;
    USHORT MaximumLength;
    PWSTR Buffer;
}UNICODE_STRING, * PUNICODE_STRING;

typedef struct PEB_LDR_DATA {
    ULONG Length;
    BOOLEAN initialized;
    PVOID SsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;

}PEB_LDR_DATA, * PPEB_LDR_DATA;

typedef struct LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    void* BaseAddress;
    void* EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STRING FullDllName;
    UNICODE_STRING BaseDllName;
    ULONG Flags;
    SHORT LoadCount;
    SHORT TlsIndex;
    HANDLE SectionHandle;
    ULONG CheckSum;
    ULONG TimeDateStamp;
}MY_LDR_MODULE, * PLDR_MODULE;

int main()
{
    PEB_LDR_DATA* pEBLDR;
    MY_LDR_MODULE* pLdrMod;
    PLDR_MODULE PLdr;
    LIST_ENTRY* pNext, * pStart;
    _asm
    {
        mov eax, fs: [0x30]
        mov eax, [eax + 0xC]
        mov pEBLDR, eax
    }
    printf("GetModuleHandle Kernel32:0x%08x\n", GetModuleHandle(L"Kernel32"));
    printf("GetModuleHandle ntdll:0x%08x\n", GetModuleHandle(L"ntdll"));
    printf("--------------------------------------------------------------------------\n");
    printf("PEB_LDR_DATA:0x%08x\n", pEBLDR);
    printf("LDR->InLoadOrderModuleList:\t\t0x%08x\n", pEBLDR->InLoadOrderModuleList);
    printf(">>>InLoadOrderModuleList<<<\n");
    printf("BaseAddress\t\t BaseDllName\n================================================\n");
    pNext = (LIST_ENTRY*)&(pEBLDR->InLoadOrderModuleList);
    pStart = pNext;
    do
    {
        pNext = pNext->Flink;
        pLdrMod = (MY_LDR_MODULE*)pNext;
        printf("0x%08x\t\t", pLdrMod->BaseAddress);
        wprintf(L"%s\n", pLdrMod->BaseDllName.Buffer);

    } while (pNext != pStart);

    printf("LDR->InMemoryOrderModuleList:\t\t0x%08x\n", pEBLDR->InMemoryOrderModuleList);
    printf("BaseAddress\t\t BaseDllName\n================================================\n");
    pNext = (LIST_ENTRY*)&(pEBLDR->InMemoryOrderModuleList);
    pStart = pNext;
    do
    {
        pNext = pNext->Flink;
        pLdrMod = CONTAINING_RECORD(pNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderModuleList);
        printf("0x%08x\t\t", pLdrMod->BaseAddress);
        wprintf(L"%s\n", pLdrMod->BaseDllName.Buffer);
    } while (pNext != pStart);

    printf("LDR->InInitializationOrderModuleList:\t0x%08x\n", pEBLDR->InInitializationOrderModuleList);
    printf("BaseAddress\t\t BaseDllName\n================================================\n");
    pNext = (LIST_ENTRY*)&(pEBLDR->InInitializationOrderModuleList);
    pStart = pNext;

    do
    {
        pNext = pNext->Flink;
        pLdrMod = CONTAINING_RECORD(pNext, LDR_DATA_TABLE_ENTRY, InInitializationOrderModuleList);
        printf("0x%08x\t\t", pLdrMod->BaseAddress);
        wprintf(L"%s\n", pLdrMod->BaseDllName.Buffer);
    } while (pNext != pStart);
    getchar();
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

搜索思路

1)xor eax, eax清零,mov eax, fs:[0x30] 获取PEB地址

2)mov eax, [eax + 0x0c] 获取LDR地址,0x30和0x0c上面都有讲的,偏移量。

mov esi, [eax + 0Ch] //则指向InLoadOrderModuleList

mov esi, [eax + 14h] //则指向InMemoryOrderModuleList

4)遍历Flink,找到Kernel32.dll的位置

位置在第3个,这里需要简单计算下。

指向InLoadOrderModuleList 的同时就是第一个了。

再指向一次mov esi, [esi],就是第二个了。

lodsd或者mov esi,[esi];mov eax, esi,就是第三个了

5)获取Kernel地址,这里也需要小小计算一下。

mov eax,[eax+08h] //InLoadOrderModuleList 顺序

mov eax, [eax+18h] //InMemoryOrderModuleList 顺序

6)完成赋值,mov address, eax; 最后输入验证结果。

代码实现

//InLoadOrderModuleList

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

int main()
{
    unsigned int address;
    __asm {
        xor eax, eax
        mov eax, fs: [eax + 30h] ; 指向PEB的指针
        mov eax, [eax + 0ch]; 指向PEB_LDR_DATA的指针
        mov eax, [eax + 0ch]; 根据PEB_LDR_DATA得出InLoadOrderModuleList的Flink字段
        mov esi, [eax];
        lodsd;
        mov eax, [eax + 18h]; Kernel.dll的基地址
        mov address, eax;
    }
    printf("0x:%p\n", address);
    HANDLE kernelA = LoadLibrary(L"kernel32.dll");
    printf("0x:%p\n", kernelA);
    system("pause");
    return 0;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

//InMemoryOrderModuleList

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

int main()
{
    unsigned int address;
    __asm {
        xor eax, eax;
        mov eax, fs: [eax + 30h] ; 指向PEB的指针
        mov eax, [eax + 0ch]; 指向PEB_LDR_DATA的指针
        mov eax, [eax + 14h]; 根据PEB_LDR_DATA得出InMemoryOrderModuleList的Flink字段
        mov esi, [eax];
        lodsd;
        mov eax, [eax + 10h]; Kernel.dll的基地址
        mov address, eax;
    }
    printf("0x:%p\n", address);
    HANDLE kernelA = LoadLibrary(L"kernel32.dll");
    printf("0x:%p\n", kernelA);
    system("pause");
    return 0;
}

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


http://www.kler.cn/a/573671.html

相关文章:

  • Docker小游戏 | 使用Docker部署DOS游戏合集
  • SQL命令详解之常用函数
  • 虚拟网络IP设置
  • Python 面向对象编程-继承与多态
  • C#-泛型
  • 二、Redis 安装与基本配置:全平台安装指南 服务器配置详解
  • c++ cin输入流的使用总结
  • (YOLOv11)基于Vue Flask YOLOv11的水稻病害检测系统【含有数据大屏展示】
  • Logstash:数据搬运工的奇幻漂流
  • 苍穹外卖零碎知识点学习记录
  • 深入解析:使用Java爬虫获取淘宝商品详情高级版API接口
  • java常用注解(持续更新)
  • XS9935 ,4通道模拟复合视频解码芯片,双向音频数据同轴共缆传输
  • 二、QT和驱动模块实现智能家居-----4、编译Qt程序并运行
  • conda 更换镜像究极方法
  • Django实现接口token检测的实现方案
  • 计算机网络数据传输探秘:包裹如何在数字世界旅行?
  • 海康摄像头接入流媒体服务器实现https域名代理播放
  • 优选算法的智慧之光:滑动窗口专题(二)
  • 自然语言转SQL之Vanna.ai:AI集成数据库