设为首页 加入收藏

TOP

1.15 自实现GetProcAddress(二)
2023-09-09 10:25:34 】 浏览:170
Tags:1.15 GetProcAddress
LDR_DATA_TABLE_ENTRY;

根据如上流程,想要得到kernel32.dll模块的入口地址,我们可以进行这几步,首先得到TEB地址,并在该地址中寻找PEB线程环境块,并在该环境块内得到LDR结构,在该结构中获取第二条链表地址,输出该链表中的0x10以及0x20即可得到当前模块的基地址,以及完整的模块路径信息,该功能的实现分为32位于64位,如下代码则是实现代码。

#include <iostream>
#include <Windows.h>

// 将不同的节压缩为单一的节
#pragma comment(linker, "/merge:.data=.text") 
#pragma comment(linker, "/merge:.rdata=.text")
#pragma comment(linker, "/section:.text,RWE")

// 得到32位模式下kernel32.dll地址
DWORD GetModuleKernel32()
{
  DWORD *PEB = NULL, *Ldr = NULL, *Flink = NULL, *p = NULL;
  DWORD *BaseAddress = NULL, *FullDllName = NULL;

  __asm
  {
    mov eax, fs:[0x30]      // FS保存着TEB
    mov PEB, eax            // +30定位到PEB
  }

  // 得到LDR
  Ldr = *((DWORD **)((unsigned char *)PEB + 0x0c));

  // 在LDR基础上找到第二条链表
  Flink = *((DWORD **)((unsigned char *)Ldr + 0x14));
  p = Flink;

  p = *((DWORD **)p);

  // 计数器
  int count = 0;

  while (Flink != p)
  {
    BaseAddress = *((DWORD **)((unsigned char *)p + 0x10));
    FullDllName = *((DWORD **)((unsigned char *)p + 0x20));

    if (BaseAddress == 0)
      break;

    // printf("镜像基址 = %08x \r\n 模块路径 = %S \r\n", BaseAddress, (unsigned char *)FullDllName);
    // 第二个模块是kernel32.dll
    if (count == 1)
    {
      // printf("address =%x \n", BaseAddress);
      return reinterpret_cast<DWORD>(BaseAddress);
    }

    p = *((DWORD **)p);
    count = count + 1;
  }

  // 未找到Kernel32模块
  return 0;
}

// 获取64位模式下的kernel32.dll基址
ULONGLONG GetModuleKernel64()
{
  ULONGLONG dwKernel32Addr = 0;

  // 获取TEB的地址
  _TEB* pTeb = NtCurrentTeb();
  // 获取PEB的地址
  PULONGLONG pPeb = (PULONGLONG)*(PULONGLONG)((ULONGLONG)pTeb + 0x60);
  // 获取PEB_LDR_DATA结构的地址
  PULONGLONG pLdr = (PULONGLONG)*(PULONGLONG)((ULONGLONG)pPeb + 0x18);
  // 模块初始化链表的头指针InInitializationOrderModuleList
  PULONGLONG pInLoadOrderModuleList = (PULONGLONG)((ULONGLONG)pLdr + 0x10);

  // 获取链表中第一个模块信息,exe模块
  PULONGLONG pModuleExe = (PULONGLONG)*pInLoadOrderModuleList;
  //printf("EXE Base = > %X \n", pModuleExe[6]);

  // 获取链表中第二个模块信息,ntdll模块
  PULONGLONG pModuleNtdll = (PULONGLONG)*pModuleExe;
  //printf("Ntdll Base = > %X \n", pModuleNtdll[6]);

  // 获取链表中第三个模块信息,Kernel32模块
  PULONGLONG pModuleKernel32 = (PULONGLONG)*pModuleNtdll;
  //printf("Kernel32 Base = > %X \n", pModuleKernel32[6]);

  // 获取kernel32基址
  dwKernel32Addr = pModuleKernel32[6];
  return dwKernel32Addr;
}

int main(int argc, char *argv[])
{
  // 输出32位kernel32
  DWORD kernel32BaseAddress = GetModuleKernel32();
  std::cout << "kernel32 = " << std::hex << kernel32BaseAddress << std::endl;

  // 输出64位kernel32
  ULONGLONG kernel64BaseAddress = GetModuleKernel64();
  std::cout << "kernel64 = " << std::hex << kernel32BaseAddress << std::endl;

  system("pause");
  return 0;
}

如上代码中分别实现了32位于64位两种获取内存模块基址GetModuleKernel32用于获取32位模式,GetModuleKernel64则用于获取64位内存基址,读者可自行调用两种模式,输出如下图所示;

我们通过调用GetModuleKernel32()函数读入kernel32.dll模块入口地址后,则下一步就可以通过循环,遍历该模块的导出表并寻找到GetProcAddress导出函数地址,找到该导出函数内存地址后,则可以通过kernel32模块基址加上dwFunAddrOffset相对偏移,获取到该函数的内存地址,此时通过函数指针就可以将该函数地址读入到内存指针内。

// 封装基地址获取功能
ULONGLONG MyGetProcAddress()
{
  // 获取32位基址
  ULONGLONG dwBase = GetModuleKernel32();
  
  // 获取64位基址
  // ULONGLONG dwBase = GetModuleKernel64();
  
  // 获取DOS头
  PIMAGE_DOS_HEADER pDos = (PIMAGE_DOS_HEADER)dwBase;
  
  // 获取32位NT头
  PI
首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇C++算法之旅、05 基础篇 | 第二章.. 下一篇C++系列三:QT初识2

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目