Hmily 发表于 2010-1-7 16:38

IDB2EXE.idc 一个从IDB文件生成EXE文件的idc脚本示例

标 题: 【原创】IDB2EXE.idc 一个从IDB文件生成EXE文件的idc脚本示例
作 者: nkspark
时 间: 2010-01-06,16:15
链 接: http://bbs.pediy.com/showthread.php?t=104632

#include <idc.idc>

//
//+++++++++++++++++++++++++++++++++++++++++++++
//
// IDB2EXE.idc
//
// Written by Spark.   nkspark_at_gmail.com
//
//          2010.01.06
//+++++++++++++++++++++++++++++++++++++++++++++
//

#define E_MAGIC               0x5a4d
#define E_LFANEW            0xe0
#define E_LFANEW_OFFSET      0x3c
#define SIGNATURE             0x00004550
#define MACHINE                0x014c
#define SECTIONNUMBER_OFFSET0x06
#define IAT_OFFSET            0x80
#define PEHEADERLENGTH      0xf8
#define SECTIONTABLE_OFFSET    E_LFANEW + PEHEADERLENGTH
#define SECTIONENTRYLENGTH    0x28


static GetIDBFilePath()
{
auto strIDBFilePath;//idb full path name;
auto strEXEFileName;
auto iNameEnd;
auto iPathEnd;
auto strPath;
auto pFile;

strPath = GetInputFilePath();
strEXEFileName = GetInputFile();
iPathEnd = strstr(strPath,strEXEFileName);
iNameEnd = strstr(strEXEFileName,".");
strIDBFilePath = substr(strPath,0,iPathEnd) + substr(strEXEFileName,0,iNameEnd) + ".idb";

//Message("idb file lies in %s\n",strIDBFilePath);

pFile = fopen(strIDBFilePath,"r");
if(!pFile)
{
    strIDBFilePath = AskStr(strIDBFilePath,"Please provide the path of IDB file:");
    return strIDBFilePath;
}
fclose(pFile);
return   strIDBFilePath;
}

//从IDB文件中获取IAT的偏移地址
static GetIATOffset(strIDBFilePath, PEHeaderOffset)
{
    auto pIDB;
    auto IATOffset;
   
    pIDB =fopen(strIDBFilePath,"rb");

    if(!pIDB)
    {
      Message("Can not open idb file!\n");
      return -1;
    }
   
    fseek(pIDB,PEHeaderOffset + IAT_OFFSET,0);
    IATOffset = readlong(pIDB,0);

    fclose(pIDB);
    return IATOffset;

}

//从IDB文件中获取PE头的起始地址
static SearchPEHeader(pIDB)
{
    auto PEHeaderOffset;
    auto i;
    auto iFileSize;
   
   
    fseek(pIDB,0x0,2);
    iFileSize = ftell(pIDB);
   
    if( iFileSize == -1)
      return -1;
   
    for(i=0;i<iFileSize;i++)
    {
      fseek(pIDB,i,0);
      //Signature "PE"
      if(readlong(pIDB,0) == SIGNATURE)
      //Machine 0x014c
      if(readshort(pIDB,0) == MACHINE)
      {
          PEHeaderOffset=ftell(pIDB);
          PEHeaderOffset = PEHeaderOffset -0x06;
          return PEHeaderOffset;         
      }
    }   
    return -1;
}

//IDB中的FirstThrunk已经被IDA自动填充,需要清理回原状
static PatchIAT(addrImageBase, addrIAT)
{   
    auto addrOriginalFirstThunk;
    auto addrFirstThunk;
    auto ValueOfIAT;
   
    Message("Start patching IAT at %x\n",addrIAT);
   
    if(Dword(addrIAT) == BADADDR)
    {
      Message("IAT data was not loaded into IDB file!\n");
      return -1;
    }
   
    do
    {
      addrOriginalFirstThunk = addrImageBase + Dword(addrIAT);
      addrFirstThunk = addrImageBase + Dword(addrIAT+0x10);
      
      do
      {
      PatchDword(addrFirstThunk,Dword(addrOriginalFirstThunk));
      addrOriginalFirstThunk = addrOriginalFirstThunk +0x04;
      addrFirstThunk = addrFirstThunk +0x04;
                  
      }while(Dword(addrOriginalFirstThunk) != 0);
      
      addrIAT = addrIAT + 0x14;
      
    }while(Dword(addrIAT) != 0);
   
}

//为创建的EXE文件生成一个PE头
static BuildHeader(pFile,strIDBFilePath)
{
    auto i,j;
    auto pIDB;
    auto addrPE;
    auto iSectionNumber;
    auto lByte;

    pIDB =fopen(strIDBFilePath,"rb");
    if(!pIDB)
    {
      Message("Can not open idb file!\n");
      return -1;
    }
   
    fseek(pFile,0x0,0);
    writeshort(pFile,E_MAGIC,0);
    fseek(pFile,E_LFANEW_OFFSET,0);
    writeshort(pFile,E_LFANEW,0);
   
   
    addrPE = SearchPEHeader(pIDB);
    if(addrPE == -1)
    {
      Message("Can not find PE header in IDB file!\n");
      return -1;
    }
    Message("PEHeaderOffset is %x\n",addrPE);
   
    fseek(pIDB,addrPE,0);
    fseek(pFile,E_LFANEW,0);
   
    //Copy PE header
    //IAT should be set to 0 later!!!
    for(i=0;i<PEHEADERLENGTH;i++)
    {
      lByte = fgetc(pIDB);
      fputc(lByte,pFile);
    }

    fseek(pIDB,addrPE+SECTIONNUMBER_OFFSET,0);
   
    iSectionNumber = fgetc(pIDB);
         
    Message("there are %d sections!\n",iSectionNumber);
   
    //Prepare to write section table
    fseek(pFile,SECTIONTABLE_OFFSET,0);
    fseek(pIDB,addrPE+SECTIONTABLE_OFFSET-E_LFANEW,0);
   
    for(j=0;j<iSectionNumber;j++)
    {
      
      //每个表项有6字节偏移;
      fseek(pIDB,6,1);
      //最后一个表项有多余4字节偏移;
      if(j == (iSectionNumber-1))
      fseek(pIDB,4,1);
      
      
      //section table should be corrected later!!!!   
      for(i=0;i<SECTIONENTRYLENGTH;i++)
      {
          lByte = fgetc(pIDB);
          fputc(lByte,pFile);
      }
      Message("write %d section table!\n",j);
    }
    fclose(pIDB);
   
    //返回addrPE以备后用 :)
    return addrPE;
   
}

//内存地址转换为文件地址
static RVA2FA(pFile)
{
    auto iSectionNumber;
    auto iSectionBase;
    auto i;
   
    fseek(pFile,E_LFANEW + SECTIONNUMBER_OFFSET,0);
    iSectionNumber = readshort(pFile,0);

    fseek(pFile,SECTIONTABLE_OFFSET + (iSectionNumber -1) * SECTIONENTRYLENGTH + 0x14,0);
    iSectionBase = readlong(pFile,0);      
    Message("Section base in exe file is %x\n",iSectionBase);

    return iSectionBase;
   
}
//修正PE头信息
static PEHeaderCorrect(pFile)
{
    //关键处理,暂不外传!
    return 0;
}

static main()
{
auto temp,i;
auto startAddr, endAddr;
auto countOR;
auto pFile;
auto strOutFileName;
auto segStart;
auto segEnd;
auto iImageBase;
auto iSectionAlignment;
auto PEHeaderOffset;
auto IATOffset;
auto strIDBFilePath;
auto iSectionBase;

startAddr = MinEA();
endAddr = MaxEA();
iImageBase = FirstSeg() & 0xffff0000;
iSectionAlignment = FirstSeg() & 0xffff;

Message("Base:%x ,EP:%x , Min:%x, Max:%x\n",iImageBase,BeginEA(),MinEA(),MaxEA());

strIDBFilePath = GetIDBFilePath();
Message("idb file lies in %s\n",strIDBFilePath);

strOutFileName = strIDBFilePath + "_dump.exe";

pFile=fopen(strOutFileName,"w+b");

PEHeaderOffset = BuildHeader(pFile,strIDBFilePath);
if (PEHeaderOffset == -1)
    return -1;
   
IATOffset = GetIATOffset(strIDBFilePath,PEHeaderOffset);
if (IATOffset == -1)
    return -1;
   
iSectionBase = RVA2FA(pFile);

PatchIAT(iImageBase,iImageBase + IATOffset);

for(i=startAddr;i<endAddr;)
{
    segStart = GetSegmentAttr(i,SEGATTR_START);
    if(segStart == BADADDR) break;
    segEnd = GetSegmentAttr(i,SEGATTR_END);
    if(segEnd == BADADDR) break;   
    savefile(pFile, iSectionBase + segStart - iImageBase -iSectionAlignment,segStart,segEnd-segStart);
    Message("seg %x : %x : %x \n",GetSegmentAttr(i,SEGATTR_ALIGN),segStart,segEnd);
    i = segEnd + 1;
}

PEHeaderCorrect(pFile);

fclose(pFile);

return 0;
}


关键两点:
1、建立一个PE头。这个可以直接从IDB的原始文件中提取,可能好多人没想到。知道这一点后,基本上就可以自己写这个脚本了。
2、PE头修正。导出的EXE文件如果不修正的话,可能会跑不起来,例如IDA缺省情况下是不加载资源节的,这时导出的是不完整的EXE文件。我的自用版本可以让不完整的导出EXE正常跑到资源加载处正常退出。自用版本这里就不公开了,有人出高价的话,我可以卖给他 :)

avzhongjiezhe 发表于 2010-2-1 15:05

看了半天,代码还是有一部分没懂

avzhongjiezhe 发表于 2010-2-3 09:50

看来我是技术不够

kghong 发表于 2010-2-13 13:54

呵呵呵呵。强呀,来好好学下

ty154w 发表于 2011-2-28 11:09

190.    for(j=0;j<iSectionNumber;j++)

191.    {

192.      

193.      //每个表项有6字节偏移;

194.      fseek(pIDB,6,1);

195.      //最后一个表项有多余4字节偏移;不同的ida偏移字节不一样v5.5为102字节

196.      if(j == (iSectionNumber-1))

197.      fseek(pIDB,4,1);

ty154w 发表于 2011-2-28 11:18

2、PE头修正。导出的EXE文件如果不修正的话,可能会跑不起来,例如IDA缺省情况下是不加载资源节的,这时导出的是不完整的EXE文件。我的自用版本可以让不完整的导出EXE正常跑到资源加载处正常退出。
主要原因是导出的PE头方法不好,采用其他方法不需要进行PE头修正。
另自用版本好像也不能不加载资源节吧?
页: [1]
查看完整版本: IDB2EXE.idc 一个从IDB文件生成EXE文件的idc脚本示例