动态PE模块IAT、调用API信息分析程序[z]

本文介绍如何使用OllyDbg对PE文件进行反汇编,分析其导入表和调用的API信息。包括获取系统进程及模块信息、分析IAT表等步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在用OllyDbg对程序反汇编的时候,经常会看到call指令后直接跟有API名字。比如用OllyDbg反汇编xdos.exe程序,机器码FF1578604000 被翻译成了

        call dword ptr ds:[<&KERNEL32.GetVersion>]

即表示这条指令是在调用kernel32.dll中的GetVersion函数,其实FF1578604000 更加贴近硬件的翻译是

       call dword ptr [00406078]

将00406079这个地址中的数据按双字解释,发现这个地址上存放的是77E6C07F,也就是说上面的call指令实际上相当于

       call  77E6C07F

不错77E6C07F这个地址就是xdos.exe进程空间中kernel32.dll中GetVersion函数的起址。好像有点深度哦,知道研究下。

     下面是通过内存中已经加载模块的分析过程,当然静态的也可以把这些信息分析出来,不过静态的PE文件就得不到77E6C07F这个入口地址,但是函数名这些还是可以获得的。

     以下程序可以对任何可见的进程的可见模块进行分析(是可见的,目前本人水平垃圾,还没有能力发现一些“别具用心”的模块)。

    

     1.系统进程枚举程序

// 进程信息描述结构体
typedef struct tagOneProcessInfo
{
 DWORD         dwProcessID;          //进程ID
 DWORD         dwParentProcessID;    //父进程ID
 DWORD         dwThreadsNo;          //拥有线程数量
 std::string   strProcessName;       //进程名

 tagOneProcessInfo()
 {
  dwProcessID = -1;
  dwParentProcessID = -1;
  dwThreadsNo = -1;
  strProcessName.assign("");
 }
}OneProcessInfo, *POneProcessInfo;

 

// 功    能: 枚举系统进程
// 输入参数: 一个map<DWORD, OneProcessInfo>,用于保存系统的进程信息
// 返回结果: 成功返回TRUE,否则FALSE
// 说    明: map的第一个字段是就进程ID,第二个字段是该进程的详细信息
BOOL GetProcessesInfo(map<DWORD, OneProcessInfo> &mapProInfo)
{
 HANDLE handle = NULL;
 PROCESSENTRY32 proInfo;

 handle = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
 if(INVALID_HANDLE_VALUE == handle)
 {
  return FALSE;
 }

 ZeroMemory(&proInfo, sizeof(proInfo));
 proInfo.dwSize = sizeof(proInfo);


 // 枚举系统的进程
 mapProInfo.clear();
 for(BOOL bHas = Process32First(handle, &proInfo); bHas == TRUE ; bHas = Process32Next(handle, &proInfo))
 {
  OneProcessInfo oneProcessInfo;
  oneProcessInfo.dwProcessID = proInfo.th32ProcessID;
  oneProcessInfo.dwParentProcessID = proInfo.th32ParentProcessID;
  oneProcessInfo.dwThreadsNo = proInfo.cntThreads;
  oneProcessInfo.strProcessName.assign(proInfo.szExeFile);
  
  mapProInfo.insert(pair<DWORD, OneProcessInfo>(proInfo.th32ProcessID, oneProcessInfo));
 }

 // 关闭句柄
 CloseHandle(handle);

 return TRUE;
}

 

    2.某个特定进程的模块枚举程序

// 进程模块信息描述结构体
typedef struct tagOneProcessModulesInfo
{
 DWORD               dwProcessID;       //模块所在进程ID
 DWORD               dwModuleID;        //模块ID
 DWORD               dwGlobleUsage;     //该模块当前被系统引用的次数
 
 unsigned char *     ucpBaseAddress;    //模块在内存中的基地址
 DWORD               dwModuleSize;      //模块的大小

 std::string         strModuleName;     //模块名
 std::string         strModulePath;     //模块路径

 tagOneProcessModulesInfo()
 {
  dwProcessID = -1;
  dwModuleID = -1;
  dwGlobleUsage = -1;

  ucpBaseAddress = NULL;
  dwModuleSize = 0;

  strModuleName.assign("");
  strModulePath.assign("");
 }
}OneProcessModulesInfo, *POneProcessModulesInfo;

// 功    能: 枚举进程的模块信息
// 输入参数: 第一个是进程ID;第二个是map<DWORD, OneProcessModulesInfo>,用于保存进程的模块信息
// 返回结果: 成功返回TRUE,否则FALSE
// 说    明: map的第一个字段是就模块ID,第二个字段是该模块的详细信息
BOOL GetProcessModules(DWORD dwProcessID, map<DWORD, OneProcessModulesInfo> &mapModuleInfo)
{
 if(dwProcessID < 0)
 {
  return FALSE;
 }

 HANDLE  handle = NULL;
 MODULEENTRY32  modInfo;

 handle = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwProcessID);
 if(INVALID_HANDLE_VALUE == handle)
 {
  return FALSE;
 }
 
 ZeroMemory(&modInfo, sizeof(modInfo));
 modInfo.dwSize = sizeof(modInfo);

 // 枚举进程的模块
 mapModuleInfo.clear();
 for(BOOL bHas = Module32First(handle, &modInfo); bHas == TRUE; bHas = Module32Next(handle, &modInfo))
 {
  OneProcessModulesInfo oneModuleInfo;
  oneModuleInfo.dwProcessID = dwProcessID;
  oneModuleInfo.dwModuleID = modInfo.th32ModuleID;
  oneModuleInfo.dwGlobleUsage = modInfo.GlblcntUsage;
  oneModuleInfo.ucpBaseAddress = modInfo.modBaseAddr;
  oneModuleInfo.dwModuleSize = modInfo.modBaseSize;
  oneModuleInfo.strModuleName = modInfo.szModule;
  oneModuleInfo.strModulePath = modInfo.szExePath;

  mapModuleInfo.insert(pair<DWORD, OneProcessModulesInfo>(modInfo.th32ModuleID, oneModuleInfo));
 }

 // 关闭句柄
 CloseHandle(handle);

 return TRUE;
}

 

     3.IAT表分析,API信息分析程序

    这段代码的数据结构没有组织好,对于通过入口地址来查询API信息,效率是地下的。

// 模块调用的API信息结构体
typedef struct tagAPIInfo
{
 DWORD              dwAPIAddress;     //API真实地址在该模块的保存地址
 std::string        strDllNameOfAPI;  //导入这个API的Dll文件
 std::string        strAPIName;       //API名字(可能是空)
 DWORD              dwAPIID;          //导入API的序号

 tagAPIInfo()
 {
  dwAPIAddress = -1;
  strDllNameOfAPI.assign("");
  strAPIName.assign("");
  dwAPIID = -1;
 }
}APIInfo, *PAPIInfo;

// 功    能: 分析给定进程中给定模块的PE导入表和调用API信息
// 输入参数: 第一个是进程ID;第二个是OneProcessModulesInfo,传递模块信息,第三个参数是返回API信息的
// 返回结果: 成功返回TRUE,否则FALSE
// 说    明: 第三个参数的第一个字段是Dll文件名,第二个字段是其导出函数具体信息
BOOL ModulePEIATAnalyse(DWORD dwProcessID, OneProcessModulesInfo moduleInfo, map<string, vector<APIInfo> > &mapAPIInfo)
{
 if(dwProcessID < 0)
 {
  return FALSE;
 }

 mapAPIInfo.clear();
 vector<APIInfo> vecAPIInfo;

 //打开给定的进程对象
 HANDLE proHandle = OpenProcess(PROCESS_VM_READ, FALSE, dwProcessID);
 if(proHandle == NULL)
 {
  return FALSE;
 }

 HGLOBAL mem = GlobalAlloc(GPTR, moduleInfo.dwModuleSize);
 if(mem == NULL)
 {
  CloseHandle(proHandle);
  return FALSE;
 }

 LPTSTR pMem = (LPTSTR)GlobalLock(mem);

 if(0 != ReadProcessMemory(proHandle, moduleInfo.ucpBaseAddress, pMem, moduleInfo.dwModuleSize, NULL))
 {
  //判断模块是不是PE模块
  IMAGE_DOS_HEADER *pDosHeader = (IMAGE_DOS_HEADER *)pMem;
  if(pDosHeader->e_magic == IMAGE_DOS_SIGNATURE)
  {
   IMAGE_NT_HEADERS *pNTHeader = (IMAGE_NT_HEADERS *)(pMem + pDosHeader->e_lfanew);
   if(pNTHeader->Signature == IMAGE_NT_SIGNATURE)
   {
    //是PE模块

    //获得导入表的地址
    long pIATRVA = pNTHeader->OptionalHeader.DataDirectory[sizeof(IMAGE_DATA_DIRECTORY)].VirtualAddress;
    if(pIATRVA != NULL)
    {
     //存在导入表
     
     //获得实际上导入表在内存中的位置
     LPTSTR pIATAddress = pIATRVA + pMem;

     IMAGE_IMPORT_DESCRIPTOR *pImportDescriptor = (IMAGE_IMPORT_DESCRIPTOR *)pIATAddress;
     
     for(; ; pImportDescriptor++)
     {
      //判断导入描述符是否结束
      if(pImportDescriptor->OriginalFirstThunk == 0 && pImportDescriptor->TimeDateStamp == 0 /
       && pImportDescriptor->ForwarderChain == 0 && pImportDescriptor->Name == 0 && /
       pImportDescriptor->FirstThunk == 0)
      {
       break;
      }

      vecAPIInfo.clear();

      //获得DLL文件的名称
      long lDllName = pImportDescriptor->Name;
      LPTSTR lpDllName = pMem + lDllName;
      string strDllName(lpDllName);
      strDllName.substr(0, strDllName.length() - 4);

      long lThunkData = pImportDescriptor->OriginalFirstThunk;
      if(lThunkData == 0)
      {
       //lThunkData = pImportDescriptor->FirstThunk;
       // 释放内存 关闭句柄
       GlobalUnlock(mem);
       GlobalFree(mem);
       CloseHandle(proHandle);
       
       return FALSE;
      }
      
      IMAGE_THUNK_DATA * lpThunkData = (IMAGE_THUNK_DATA *)(pMem + lThunkData);

      //导入的函数
      int nFirstThunkOffset = 0;
      for(long lImageThunkData = lpThunkData->u1.Ordinal; lImageThunkData != 0; lImageThunkData = (++lpThunkData)->u1.Ordinal)
      {
       APIInfo apiInfo;
       apiInfo.strDllNameOfAPI = strDllName;
       
       long lSecrialFuncion = -1;
       unsigned char * cpFunName = NULL;
       if(lImageThunkData & IMAGE_ORDINAL_FLAG32)
       {
        //该函数是以序号导入的
        lSecrialFuncion = lImageThunkData & 0x7fffffff;
       }
       else
       {
        //该函数是以函数名导入的
        IMAGE_IMPORT_BY_NAME importByName = *(IMAGE_IMPORT_BY_NAME*)(pMem + lImageThunkData);
        lSecrialFuncion = importByName.Hint;
        cpFunName = &importByName.Name[0];
       }
       
      
       apiInfo.dwAPIAddress = (long)moduleInfo.ucpBaseAddress + pImportDescriptor->FirstThunk + nFirstThunkOffset;
       apiInfo.dwAPIID = lSecrialFuncion;
       apiInfo.strAPIName.assign((char *)cpFunName);

       vecAPIInfo.push_back(apiInfo);
       nFirstThunkOffset += 4;
      }

      mapAPIInfo.insert(pair<string, vector<APIInfo> >(strDllName, vecAPIInfo));
     }
    }
   }
  }
 }

 // 释放内存 关闭句柄
 GlobalUnlock(mem);
 GlobalFree(mem);
 CloseHandle(proHandle);

 return TRUE;
}



Trackback: http://tb.blog.youkuaiyun.com/TrackBack.aspx?PostId=740114


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值