鱼C论坛

 找回密码
 立即注册
查看: 2169|回复: 0

关于PEloader release和debug下的问题

[复制链接]
发表于 2014-10-20 18:53:39 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
急急急,编写PELoader时候,用VC6编译时候没有问题,但是运行程序时候出现问题,具体情况请大家自行编译一下,帮忙看看问题出现在哪里,谢谢大牛们了!详情请见下方代码:

为了加深对PE文件结构的理解,写了个简单的模拟PE文件加载过程的程序,不过程序有些地方没处理好(资源节处),也存在这些小bug写的分四个文件写的,如下:


ReadFile.h  :
代码:
#include <stdio.h>
#include <windows.h>
#include <assert.h>
HANDLE OpenFile(char *name);
int ReadFile(HANDLE m_fd, size_t offset, size_t size, void *pBuff );
void CloseFile(HANDLE m_fd);
ReadFile.cpp  :
代码:
#include "ReadFile.h"

HANDLE OpenFile(char *name)
{
  assert(NULL!=name);
  assert(0!=name[0]);
//////////////////////////////////////////////////////////////////////////
  //打开文件;
  HANDLE m_fd;
  m_fd=CreateFileA(name,GENERIC_READ,
          FILE_SHARE_READ,NULL,
          OPEN_EXISTING,
          FILE_ATTRIBUTE_NORMAL,NULL);
  if(m_fd==INVALID_HANDLE_VALUE)
  {
    printf("OPen file %s failed error,error :%d !\n",name,GetLastError());
    return NULL;
  }
  return m_fd;
}

int ReadFile(HANDLE m_fd, size_t offset, size_t size, void *pBuff )
{
  int ret;
  ULONG rdsize;  //作为热ReadFile的参数之一;
  ret = SetFilePointer(m_fd, offset, NULL, FILE_BEGIN);  //从偏移量offset开始读文件;
  if(ret==INVALID_SET_FILE_POINTER)
  {
    ret=GetLastError();
    printf("Seek file failed error : %d !\n",ret);
    return ret;
  }
  if(!ReadFile(m_fd, pBuff, size, &rdsize, NULL))
  {
    ret = GetLastError();
    printf("Read file failed, error: %d\n", ret);
    return ret;
  }
  return 0;
}

void CloseFile(HANDLE m_fd)
{
  if(m_fd!=NULL)
    CloseHandle(m_fd);
}
PELoader.h :
代码:
#include <windows.h>
#include <Psapi.h>
#include <malloc.h>
typedef struct
{
  int sections;    //区块的个数;
  int imageBase;   //程序基址;
  int entryPoint;   //程序入口点;
  int imageSize ;  //程序加载到内存后的大小 ;
  int exportRva;
  int exportSize;
  int importRva;
  int importSize;
  int resourceRva;
  int resourceSize;
  int relocRva;
  int relocSize;
  
  int offsetSection;  //区块的偏移;
  int filetype;
} PeInfo;
PELoader.cpp :
代码:
#include "PELoader.h"
#include "ReadFile.h"
/*把该loader.exe的加载地址设置为0x0f400000,从而可以把0x00400000地址
*预留给将要被加载的程序,从而可以避免因地址重定位而带来的性能损耗;
*/
#pragma comment(linker, "/BASE:0x0f400000")
//////////////////////////////////////////////////////////////////////////
//判断文件是否为PE文件;
BOOL IsPeFile(HANDLE m_fd)
{
  IMAGE_DOS_HEADER dos_header;
  IMAGE_NT_HEADERS nt_header;
  ReadFile(m_fd,0,sizeof(IMAGE_DOS_HEADER),&dos_header);   //读dos首部结构到dos_header;
  if(dos_header.e_magic!=IMAGE_DOS_SIGNATURE)
    return FALSE;
  ReadFile(m_fd,dos_header.e_lfanew,sizeof(IMAGE_NT_HEADERS),&nt_header);
  if(nt_header.Signature!=IMAGE_NT_SIGNATURE)
    return FALSE;
  return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//获取Pe文件的一些结构信息;
BOOL GetPeNtHeader(HANDLE m_fd, PeInfo &PeInformation)
{
  IMAGE_DOS_HEADER dos_header;
  IMAGE_NT_HEADERS nt_header;
  PIMAGE_FILE_HEADER file_header;    //指针的形式;
  PIMAGE_OPTIONAL_HEADER options_header;  //指针的形式;
  ReadFile(m_fd,0,sizeof(IMAGE_DOS_HEADER),&dos_header);
  ReadFile(m_fd, dos_header.e_lfanew, sizeof(IMAGE_NT_HEADERS),&nt_header);
  PeInformation.offsetSection=dos_header.e_lfanew+sizeof(IMAGE_NT_HEADERS);  //区块表的位置的偏移;
  file_header=&nt_header.FileHeader;
  options_header=&nt_header.OptionalHeader;
  assert(file_header->Machine==IMAGE_FILE_MACHINE_I386);   //判断运行平台;
  assert(file_header->SizeOfOptionalHeader==sizeof(IMAGE_OPTIONAL_HEADER32));
  //////////////////////////////////////////////////////////////////////////
  //对PeInfo结构赋值;
  PeInformation.sections=file_header->NumberOfSections;   //区块的数目;
  PeInformation.entryPoint=options_header->AddressOfEntryPoint;  //程序的入口点;
  PeInformation.imageBase=options_header->ImageBase;   //程序默认装入的基址;
  PeInformation.imageSize=options_header->SizeOfImage;  //映像装入内存后的总尺寸;
  //数据目录表相关信息;
  PeInformation.exportRva=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
  PeInformation.exportSize=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size;
  PeInformation.importRva=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress;
  PeInformation.importSize=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Size;
  PeInformation.resourceRva=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress;
  PeInformation.resourceSize=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].Size;
  PeInformation.relocRva=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress;
  PeInformation.relocSize=options_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].Size;
  return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//加载sections区块带内存函数;
BOOL LoadSections(HANDLE m_fd, PeInfo &PeInformation, MODULEINFO &PeExe)
{
  PIMAGE_SECTION_HEADER psec_header,psec_tmp;   //指向区块表结构的指针;
  char section_name[9]="\0";
  psec_header=(PIMAGE_SECTION_HEADER)_alloca(PeInformation.sections*sizeof(IMAGE_SECTION_HEADER));      //_alloca()  函数在堆栈空间申请内存;为区块表申请内存;
  
  if(psec_header==NULL)
  {
    printf("Memory not enough !!!\n");
    return FALSE;
  }
  ReadFile(m_fd,PeInformation.offsetSection,PeInformation.sections*sizeof(IMAGE_SECTION_HEADER),psec_header); //将区块表读到申请的内存空间中;
  psec_tmp=psec_header;
  //将区块内容读到申请的内存中;
  for(int i=0;i<PeInformation.sections;i++,psec_tmp++)
  {
    memcpy(section_name,psec_tmp->Name,8);
    if(psec_tmp->PointerToRawData!=NULL && psec_tmp->SizeOfRawData!=0)
    {
      ReadFile(m_fd,
             psec_tmp->PointerToRawData,
             psec_tmp->SizeOfRawData,
           (void*)((DWORD)PeExe.lpBaseOfDll+psec_tmp->VirtualAddress));   //程序基址 +  在内存中的RVA;
    }
    printf("SectionName--->%s\n",section_name);
  }
  return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//根据重定位表对,内存模块进行重定位;
BOOL RelocPemodule(PIMAGE_BASE_RELOCATION prelocBase, DWORD reImageBase ,int offsetReloc)
{
    DWORD reloca_addr,count, offset, type;
    WORD *item = NULL ;
    while(prelocBase->VirtualAddress!=NULL)
    {
      reloca_addr=prelocBase->VirtualAddress+reImageBase;  //要被重定位的数据的地址部分;
      count=(prelocBase->SizeOfBlock-sizeof(IMAGE_BASE_RELOCATION))>>1;      //数组每项大小两个字节,除以2,即为数组项目个数;
      item= (WORD *)((char*)prelocBase+sizeof(IMAGE_BASE_RELOCATION));     //数组项目的开始地址;
      for(int i=0; i<count;i++)  //每个重定位表中有N项;
      {
        offset = item[0] & 0x0fff ;   //低12位,重定位地址;
        type = item[0] >> 12 ;    //重定位类型;
        if(type==3)
        {
          *(DWORD*)(reloca_addr+offset)+=offsetReloc;   //重定位地址加上  便宜量;
        }
      }
      prelocBase=(PIMAGE_BASE_RELOCATION)(item+count*2); //指针指向下一个重定位结构;
    }
    return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//导出表修正函数;
BOOL FixExport(PIMAGE_EXPORT_DIRECTORY pexport_addr,DWORD reImageBase)
{
  return TRUE;                                     //exe文件一般无导出表;
}
//////////////////////////////////////////////////////////////////////////
//导入表修正函数;
BOOL FixImport(PIMAGE_IMPORT_DESCRIPTOR pimport_addr,DWORD reImageBase)
{
  PIMAGE_THUNK_DATA pOrgThunk, pFirstThunk;
  PIMAGE_IMPORT_BY_NAME pImporBytName;
  printf("\n--------------------------import table info-------------------------------\n");
  while(pimport_addr->OriginalFirstThunk!=NULL)
  {
    pimport_addr->Name+=reImageBase;    //内存中导入表中动态链接库的名字的地址;
    printf("Dll:%s\n",pimport_addr->Name);   //打印动态链接库的名字;
    FARPROC fpFun;         //动态链接库中函数的地址;
    HINSTANCE hInstance = LoadLibraryA((LPCTSTR)pimport_addr->Name);  //加载动态链接库;
    if(hInstance==NULL)
    {
      printf("Load Librabry %sa failed ,error :%d\n",pimport_addr->Name,GetLastError());
      return FALSE;
    }
    pOrgThunk = (PIMAGE_THUNK_DATA)(reImageBase+pimport_addr->OriginalFirstThunk);
    pFirstThunk = (PIMAGE_THUNK_DATA)(reImageBase+pimport_addr->FirstThunk);
    while(*(DWORD*)pOrgThunk!=NULL)
    {
      if(pOrgThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG32)   //最高位为1,则函数以序号的方式输入;
        fpFun = GetProcAddress(hInstance,(LPCSTR)(pOrgThunk->u1.Ordinal & 0x0000ffff));
      else      //函数以名字输入的;
      {
        pImporBytName = (PIMAGE_IMPORT_BY_NAME)(reImageBase+pOrgThunk->u1.AddressOfData);
        fpFun = GetProcAddress(hInstance, (LPCSTR)(pImporBytName->Name));  //得到函数地址;
      }
      pFirstThunk->u1.Ordinal=(LONG)fpFun;    //将的到的函数地址填入到pfirstthunk中;
      pOrgThunk++;
      pFirstThunk++;
    }
  //  FreeLibrary(hInstance);
    pimport_addr++;
   
  }
  
  return TRUE;
}
//////////////////////////////////////////////////////////////////////////
//资源处理函数;
BOOL FixResource(PIMAGE_RESOURCE_DIRECTORY presource_addr,DWORD reImageBase)
{
  PIMAGE_RESOURCE_DIRECTORY_ENTRY presource_entry;
  DWORD nEntrys;    //每个目录结构的资源个数 ById + ByName;
  nEntrys = presource_addr->NumberOfIdEntries + presource_addr->NumberOfNamedEntries ;
  presource_entry = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((DWORD)presource_addr + sizeof(IMAGE_RESOURCE_DIRECTORY));   //第一层目录入口;
  for(int i=0; i<nEntrys; i++, presource_entry++)
  {
    if(presource_entry->OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY)
    {
      PIMAGE_RESOURCE_DIRECTORY presource_addr2;
      PIMAGE_RESOURCE_DIRECTORY_ENTRY presource_entry2;
      DWORD nEntrys2;
      presource_addr2 = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)presource_addr + (~IMAGE_RESOURCE_DATA_IS_DIRECTORY & presource_entry->OffsetToData)); //取低位地址;
      presource_entry2 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY)((DWORD)presource_addr2 + sizeof(IMAGE_RESOURCE_DIRECTORY));
      nEntrys2 = presource_addr2->NumberOfIdEntries + presource_addr2->NumberOfNamedEntries;   //资源个数;
      for(int j=0;j<nEntrys2;j++, presource_entry2++)
      {
        if(presource_entry2->Name & IMAGE_RESOURCE_NAME_IS_STRING)  //判断资源名称是ID号还是字符串;
        {
          PIMAGE_RESOURCE_DIR_STRING_U pNameDirString;  //资源名称字符穿;
          pNameDirString =(PIMAGE_RESOURCE_DIR_STRING_U) ((DWORD)(presource_addr) + ~IMAGE_RESOURCE_NAME_IS_STRING & presource_entry2->Name);
        }
        if(presource_entry2->OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY)
        {
          PIMAGE_RESOURCE_DIRECTORY presource_addr3; //第三层目录地址;
          PIMAGE_RESOURCE_DIRECTORY_ENTRY presource_entry3;
          DWORD nEntrys3;         //第三层资源个数;
          presource_addr3 = (PIMAGE_RESOURCE_DIRECTORY)((DWORD)(presource_addr) + (~IMAGE_RESOURCE_DATA_IS_DIRECTORY & presource_entry2->OffsetToData));
          presource_entry3 = (PIMAGE_RESOURCE_DIRECTORY_ENTRY) ((DWORD)presource_addr3 + sizeof(IMAGE_RESOURCE_DIRECTORY)); //第三层目录入口地址;
          nEntrys3 = presource_addr3->NumberOfIdEntries + presource_addr3->NumberOfNamedEntries;   
            for(int k=0 ;k<nEntrys3;k++)
            {
              PIMAGE_RESOURCE_DATA_ENTRY pDataEntry;  //最终资源的入口地址结构;
              assert(~IMAGE_RESOURCE_DATA_IS_DIRECTORY & presource_entry3->OffsetToData);  //资源结构地址为空,则异常;
              pDataEntry=(PIMAGE_RESOURCE_DATA_ENTRY)((DWORD)presource_addr + (~IMAGE_RESOURCE_DATA_IS_DIRECTORY & presource_entry3->OffsetToData));
              //pDataEntry->OffsetToData += (DWORD)reImageBase;
            }
          }
        }
      }
  }
  return TRUE;
}
//////////////////////////////////////////////////////////////////////////
int LoadPeModule(char *name)
{
  HANDLE m_fd;
  PeInfo PeInformation;   //pe信息结构;
  LPVOID addr;           //为程序申请的地址空间的开始地址;
  MODULEINFO  PeExe;     //镜像的模块信息,加载模块结构;
  m_fd=OpenFile(name);      //打开文件;
  //////////////////////////////////////////////////////////////////////////
  //判断是否为合法的PE文件;
  assert(IsPeFile(m_fd));
  //获取Pe文件的相关信息;
  GetPeNtHeader(m_fd, PeInformation);
  /*为image按照imageBase优先原则分配空间地址。;
   *如该空间地址已被reserved或commit,则重新分配一个可用的空间地址,;
   *但此种情况下需要做基址重定位处理。*/;
  addr=VirtualAlloc((LPVOID)(PeInformation.imageBase),
                     PeInformation.imageSize,
                MEM_RESERVE | MEM_COMMIT,
                PAGE_EXECUTE_READWRITE);     //申请可读可写可执行 内存区域;
  if(addr==NULL)
  {
    printf("VirtualAlloc failed , error :%d \n",GetLastError());
     //申请的起始地址被占用,系统随机分配起始地址;
    addr=VirtualAlloc(NULL,
              PeInformation.imageSize,
              MEM_RESERVE | MEM_COMMIT,
              PAGE_EXECUTE_READWRITE);  
    if(addr==NULL)
    {
      printf("VirtualAlloc failed , error :%d \n",GetLastError());
      return -1;
    }
  }
//////////////////////////////////////////////////////////////////////////
  memset((void*)addr,0,PeInformation.imageSize);  //将申请的内存区域填充0;
  PeExe.lpBaseOfDll=(LPVOID)addr;    //模块在内存中基址;
  PeExe.EntryPoint=(LPVOID)PeInformation.entryPoint;   //入口地址;
  PeExe.SizeOfImage=PeInformation.imageSize;  //镜像在内存中的大小;
  //获取Pe文件的一些基本信息后,将sections区块加载到内存;
  LoadSections(m_fd,PeInformation,PeExe);
  //////////////////////////////////////////////////////////////////////////
  //进行后期处理,重定位,IAT地址表填写;
  //如果实际分配的空间地址和pe文件的基址imagebase不一样,则需要做基址重定位处理 ;
  if((int)PeExe.lpBaseOfDll!=PeInformation.imageBase)
  {
    if(PeInformation.relocSize==0)
    {
      printf("cannot reloc address!!!\n");
      return -1;
    }
    PIMAGE_BASE_RELOCATION prelocBase=(PIMAGE_BASE_RELOCATION)((DWORD)PeExe.lpBaseOfDll+PeInformation.relocRva);
    RelocPemodule(prelocBase,(DWORD)PeExe.lpBaseOfDll,(DWORD)PeExe.lpBaseOfDll-PeInformation.imageBase);
  }
  //////////////////////////////////////////////////////////////////////////
  //如果文件有导出表,则进行处理导出表段;
  if(PeInformation.exportSize!=0)
  {
    PIMAGE_EXPORT_DIRECTORY pexport_addr=(PIMAGE_EXPORT_DIRECTORY)((DWORD)PeExe.lpBaseOfDll+PeInformation.exportRva);  //导出表的起始位置;
    FixExport(pexport_addr,(DWORD)PeExe.lpBaseOfDll);     //修正导出表;
  }
  //如果文件有导入表,则处理导入表;
  if(PeInformation.importSize!=0)
  {
    PIMAGE_IMPORT_DESCRIPTOR pimport_addr=(PIMAGE_IMPORT_DESCRIPTOR)((DWORD)PeExe.lpBaseOfDll+PeInformation.importRva);  //导入表的起始位置;
    FixImport(pimport_addr,(DWORD)PeExe.lpBaseOfDll);  //修正导入表;
  }
  //如果文件有资源表,则处理资源表;
  if(PeInformation.resourceSize!=0)
  {
    PIMAGE_RESOURCE_DIRECTORY presource_addr=(PIMAGE_RESOURCE_DIRECTORY)((DWORD)PeExe.lpBaseOfDll+PeInformation.resourceRva); //资源表的起始位置;
  //  FixResource(presource_addr,(DWORD)PeExe.lpBaseOfDll);
  }
  int entrypoint = (int)PeExe.lpBaseOfDll+PeInformation.entryPoint;
  printf("程序在内存的中起始地址:0x%x\n",(int)PeExe.lpBaseOfDll);
  printf("程序的入口点:0x%x\n\n",entrypoint);
  //程序跳到入口点开始执行程序;
  __asm
  {
    mov eax,entrypoint;
    call eax ;
  }
  return 1;
}

int main()
{
  LoadPeModule("cmd.exe");  //加载cmd程序;
  return 1;
}
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-11-25 04:57

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表