IDC Script實戰

看雪資料發表於2003-08-03

在研究一個Borland C++寫的軟體時,遇到以下問題:在主EXE檔案和一個DLL中包含有大量的字串資源,包括各種提示及錯誤資訊。程式碼中使用這些資源的方式基本一致,即構造一個OWL的TStringIDS物件,把資源ID作為引數傳入。在IDA中,為以下格式:
   
  push    nResourceID

  一開始,遇到這樣的程式碼,我用ResHacker手工查詢,把資源字串作為相應程式碼的註釋。這樣做效率實在太低,轉念一想,幹嘛不試試IDC Script,要是能把所有的push nResourceID自動做上註釋,無疑能大大增加程式碼的可讀性。

  注意:這樣做出的註釋,可能有一些錯誤,把與資源訪問無關的程式碼也進行解釋。對於Resource ID在10以下的,最好不要用。比如 push 1 的意思可能是 push true。還有一些地方,是在呼叫new運算子分配記憶體,也會push一個立即數。不過這種註釋錯誤一般能看出來,還是利大於弊。

  我的IDB檔案為9MB多,在AMD 1700+上跑1次約5分鐘。

  幾句說明:

1. 用ResHacker開啟該DLL,將字串資源另存到單獨的.RC檔案中。用Notepad刪除資源描
 述語句。得到的資料格式如下(test.rc):

  0, "test1"
  1, "test2"
  2, "test3"
   ...

2. 用FindBinary直接查詢push指令的Opcode。當運算元為立即數時,對應的程式碼有
  0x6A,0x68兩個。所以寫成的Script在執行一次後,修改一下查詢字串再跑一次。

3.從RC檔案中取出資源ID,轉換為long進行比較,匹配即作註釋。

程式碼如下:

#include <idc.idc>

static main()
{
auto nStartAddr; //起始地址=0x401000
auto nResult;    //找到的地址
auto szFile;     //
auto hFile;          

auto szData;          //RC檔案的1行
auto nPos;   //  
auto szOperandInFile; //RC檔案1行的ResourceID部分
auto szComment;       //RC檔案1行的字串部分
auto bFound;

auto nOperand;   //當前地址的第1個operand
auto nOperandInDataFile; //RC檔案1行的ResourceID部分轉換出的long值
auto nOperandType;   //當前operand型別,5為Immediate


//選擇RC檔案

szFile=AskFile(0,"*.rc","Select a data file");
 
hFile=fopen(szFile,"r");

if(0==hFile)
{
   Message("Failed to open the file");
   return;
}

nStartAddr=0x401000;

do //掃描IDB的CODE段
{
    nResult=FindBinary(nStartAddr,SEARCH_NEXT|SEARCH_DOWN,"6A");  //
                                                      //用"68"再做一遍
 
    if(nResult!=BADADDR)
    {
       nOperandType=GetOpType(nResult,0); //測試operand型別
 
       if(nOperandType==5) //Immediate
       {
          nOperand=GetOperandvalue(nResult,0);

          //在RC檔案中搜尋

          fseek(hFile,0,0);  
          bFound=0;

          do
          {
             szData=readstr(hFile);
             if(szData != -1) //檔案尾
             {
               nPos=strstr(szData,",");  //分成2部分
               szOperandInFile=substr(szData,0,nPos);
               szComment=substr(szData,nPos+3,-1);

               nOperandInDataFile=atol(szOperandInFile);

               if(nOperand==nOperandInDataFile) //匹配?
               {
                    Message(form("\nFound at %X",nResult));
                    MakeComm(nResult,szComment); //註釋
 
                    bFound=1; //找到則退出do-while
               }
            }
           }while((szData!= -1)&&(bFound==0));
        }
     }

     nStartAddr=NextAddr(nResult);  //更新地址

}while((nStartAddr<0x48D000)&&(nResult!=BADADDR));  //在DATA段前結束

fclose(hFile);

Message("\n");
Message(form("%s processed successfully!",szFile));
}


相關文章