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正常跑到资源加载处正常退出。自用版本这里就不公开了,有人出高价的话,我可以卖给他 :) 看了半天,代码还是有一部分没懂 看来我是技术不够 呵呵呵呵。强呀,来好好学下 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);
2、PE头修正。导出的EXE文件如果不修正的话,可能会跑不起来,例如IDA缺省情况下是不加载资源节的,这时导出的是不完整的EXE文件。我的自用版本可以让不完整的导出EXE正常跑到资源加载处正常退出。
主要原因是导出的PE头方法不好,采用其他方法不需要进行PE头修正。
另自用版本好像也不能不加载资源节吧?
页:
[1]