IDA Plugin 编写基础
生活随笔
收集整理的這篇文章主要介紹了
IDA Plugin 编写基础
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
IDA?Plugin?編寫基礎
IDA是迄今為止最為強大的反匯編器,它有著眾多的功能。但是如果它不具備通過附加的模塊來對標準的函數進行擴展的功能(粗俗點說就是plugin)的話,也就有負此盛名了?,F在我們就來更為詳細地研究一下如何編寫這些plugin。
喏,首要的就是要有這個叫做IDA的反匯編器,同時還要有與之匹配的IDA?SDK。SDK里主要的部分就是include目錄及其下的所有文件,這些文件里包含了編寫plugin所用到的所有的宏定義和函數原型,同時用到它們的還有import?lib?——?ida.lib。我這里使用的編譯器是Intel?C++,與VC++結合起來用。我選擇它的原因就是,在SDK中沒有對它進行內嵌的支持(比如在SDK中就有Borland?C++和VC++編譯器的安裝文件),而這能使我們更好地理解。ida.lib文件是ida.wll(位于IDA目錄下)的import?library。其中所有的函數都是用ordinal導出的,而ordinal與函數名的比較是在ida.lib里進行的。這就是需要使用與IDA版本相匹配的SDK的原因了,因為導出函數的ordinal會因版本的不同而發生變化。喏,好了,萬事具備了,我們開始吧。
plugin到底是個什么東東呢?其實它也沒什么稀奇的,就像一個通常的DLL,只是導出了下面這個名為PLUGIN的結構體:
?struct?plugin_t
?{int?version;????????//?IDA的版本,用于plugin的編寫,通常這個域的值為IDP_INTERFACE_VERSION?-?表示SDK版本的constant。
?????????????????????????如果這個域的值與所用IDA的版本不一致,就不會加載此plugin。
??int?flags???????????//?這個好理解,就是些標志。嫌麻煩添0就行了。但這里要講一下所有可能的取值。
?????????????????????????
?????????????????????????PLUGIN_MOD?-?置位表示plugin會修改database(坦白講,沒有什么plugin會修改它)。如果此位置位,
?????????????????????????而進程模塊卻禁止修改database,則plugin就不會被使用。這個標志我們并不需要,另外的幾個也是一樣。
?????????????????????????PLUGIN_DRAW?-?置位表示IDA在調用plugin后需要重畫(即刷新)所有窗口。
?????????????????????????PLUGIN_SEG?-?plugin只在鼠標所放之處的地址屬于某個segment時才能使用。這里也不需要。
?????????????????????????PLUGIN_UNL?-?若置位,則plugin在使用后會從內存中卸載。這對于調試器來說當然是好了,但對我們來說
?????????????????????????就不太合適了。?
?????????????????????????這些標志可以用邏輯或(我們這里用or,C語言里用“|”)結合起來。但我說過,更為簡單的是把所有這些
?????????????????????????域都填成紅色的NULL。
??void?(idaapi*?init)(void);????????//初始化函數加載plugin的時候調用,如果函數返回了什么不好的東西,則plugin就不會被加載。
??void?(idaapi*?term)(void);????????//卸載plugin時調用。一般不使用。
??void?(idaapi*?run)(int?arg);??????//也許這是最重要的域了?-?working?function,每次調用plugin都要運行,包含著plugin的主要
??????????????????????????????????????代碼。
??char*?comment;????????????????????//注釋,作提示用的。
??char*?help;???????????????????????//help自然就是help了.
??char*?wanted_name;????????????????//首選的plugin名。
??char*?wanted_hotkey;??????????????//首選的熱鍵。一般來說不依賴于plugin作者選用的鍵,實際的熱鍵值記錄在plugin.cfg,這個
??????????????????????????????????????文件后面會講到。
};
一般來說,include里的loader.hpp文件里這個結構體是作為類來聲明的,我想對那些不了解類的讀者說,其實在C++里類和結構體在本質上都是一樣的。喏,好像到了該給出源代碼的時候了。我這里給出一個最簡單的plugin,這個plugin會將database中的無用指令:
бесполезные?группы?инструкций:
pusha
popa
push?eax
push?edx
rdtsc
pop?edx
pop?eax
push?edx
push?eax
rdtsc
pop?eax
pop?edx
替換為“更為明了”的nop指令。下面是代碼:
#pragma?comment(linker,"/NODEFAULTLIB")
#pragma?comment(linker,"/Entry:Dllmain")????//Runtime庫占了40kb字節,卻沒什么有用的東西,特別是對plugin。
??????????????????????????????????????????????然而,如果切斷它,則需要重新標記入口點。
#define?__NT__
#define?__IDP__????//這個一定要有,否則plugin就罷工。
typedef?unsigned?char?BYTE;????//這里沒有包含標準的include文件需要額外定義BYTE類型
#i?nclude?<ida.hpp>
#i?nclude?<idp.hpp>
#i?nclude?<bytes.hpp>
#i?nclude?<loader.hpp>
#i?nclude?<kernwin.hpp>????//"紳士的選擇"?IDA中的頭文件
??//重新標記入口點
bool?Dllmain(void*?hInstDLL,?int?reason,?int?reserved)
{return?true;}
char?comment[]?=?"Experimental?plugin";
char?help[]?=?"Experimental?plugin";
char?wanted_name[]?=?"ExpPlugin";
char?wanted_hotkey[]?=?"F11";
BYTE?rdtsc1[]={0x52,?0x0F,?0x31,?0x5A,?0x58};
BYTE?rdtsc2[]={0x50,?0x0F,?0x31,?0x58,?0x5A};????//就是些opcodes
??//plugin的initialization?procedure
根據不同的返回值,IDA可以加載或者不加載plugin??赡芊祷氐闹涤?#xff1a;
????PLUGIN_SKIP?-?不加載返回值。
????PLUGIN_OK?-?喏,一個OK自然要加載plugin啦。
????PLUGIN_KEEP?-?不但要加載,還要常駐內存。
比如說,要想讓plugin只處理PE格式的文件,需要這樣寫initialization?procedure:
if?(inf.filetype?!=?f_PE)?return?PLUGIN_SKIP;
return?PLUGIN_OK;
這里就不研究那個inf結構體了,詳情請見SDK。我們這里總是要加載plugin的,所以代碼如下:
int?init(void)
{return?PLUGIN_OK;}
????//在unload?procedure里也是什么也不用干,空著它。
void?term(void)
{return;}
????//最后就是主要的run函數了
在研究主函數之前有興趣可以研究一下database的讀寫函數,還有獲取當前地址的函數,也即IDA中鼠標所放之處的地址:
?get_byte
?get_word
?get_long?-?從database中讀取各種長度字節。接受一個參數——listing中的地址。
?patch_byte
?patch_word
?patch_long?-?向database中寫入各種長度字節。兩個參數,第一個——地址,第二個——值。
可以猜到,名字里有byte的函數是用于(即接受或返回)字節的,word——字,而long——雙字。
?get_screen_ea?-?獲取當前地址的函數,當前地址就是它的返回值。
void?run(int?arg)
{BYTE?cb1;
??int?i,?tmp0;
?ea_t?CA?=?get_screen_ea();
?cb1=get_byte(CA);
????//pusha
????//popa
?if?(cb1==0x60?&&?get_byte(CA+1)==0x61)????//?pusha的opcode?-?60h,而popa?-?61h
???{patch_word(CA,?0x9090);
?????return;}
????//rdtsc
?if?(cb1==0x50)????//序列的第一個字節?-?push?eax的opcode
???{tmp0=1;
????for?(i=0;?i<5;?i++)????//數組中的其它字節,使用循環檢驗
????if?(get_byte(CA+i+1)!=rdtsc1[i])?{tmp0=0;?break;}
????if?(tmp0==1)
??????{patch_long(CA,?0x90909090);
????????patch_word(CA+4,?0x9090);
????????return;}}
?if?(cb1==0x52)
???{tmp0=1;
?????for?(i=0;?i<4;?i++)
?????if?(get_byte(CA+i+1)!=rdtsc2[i])?{tmp0=0;?break;}
?????if?(tmp0==1)
???????{patch_long(CA,?0x90909090);
?????????patch_word(CA+4,?0x9090);
?????????return;}}
};
????//現在放入前面講過的導出結構體。
????//一定要寫上extern?"C",使得linker能正確找到導出結構體的名字
extern?"C"?plugin_t?PLUGIN?=?{
IDP_INTERFACE_VERSION,
0,
init,
term,
run,
comment,
help,
wanted_name,
wanted_hotkey
};
????????現在我們來看如何將它編譯。以下是bat文件:
icl?-Gz?/I?<SDK的include目錄的路徑>?plugin.cpp?ida.lib?/link?/subsystem:windows?/DLL?/def:plugin.def?/out:exp.plw
del?"<IDA目錄路徑>\plugins\exp.plw"
copy?exp.plw?"<IDA目錄路徑>\plugins\"
PAUSE
我已經說過,此處是以Intel?C++為例的,如果用VC++的話,源代碼不用改,而編譯用的命令行則需要修改(不過就是把icl換成cl)?,F在來詳細講一下。
-Gz告訴intel?complier,默認使用stdcall規則向函數傳遞參數。
/I?-?IDA?SDK的include目錄的路徑
plugin.cpp?-?plungin源代碼。
ida.lib?-?SDK的import?library,應該和源代碼放入一個目錄。
/link參數的后面是liner?options,就不用多說了。除了def文件。因為這里已經有源代碼和bat文件了,就需要生成def文件。
LIBRARY?exp
EXPORTS?PLUGIN
def文件指明需要導出PLUGIN結構體。一般來說,到這里就都完成了,plugin已經可以使用了,剩下的只是把它加到IDA里了??梢园阉截惖絀DA的plugins目錄里。但這還不夠,還需要配置一下。所有plugins的配置都保存在plugins目錄下plugins.cfg文件里。通常文件的起始處是注釋,后面就是plugin的配置了。為了將我們的exp.plw(Windows下的plugin的擴展名都是plw)加入進去,需要在文件末尾加入四個參數,順序?如下:
????plugin名??文件名??熱鍵??參數。
這里的參數就是傳給run函數的,我個人覺得沒什么用,所以就設為0。熱鍵嘛,可以設為F11,好像IDA用不到這個鍵。這樣,我們所添加的這一行就是下面這個樣子的:
????????expplugin????exp????F11????0
不主張在前面寫一個句點加一個逗號,因為那樣就成了注釋。喏,一般來說可以認為,mission?accomplished。剩下的就是啟動IDA,打開某個文件,那個文件里要有我們前面給出的指令(比如,可以自己立馬用ASM寫一個),然后進行檢查。如果沒有問題,就按下F11鍵或從菜單里選擇我們的plugin,這時那些指令就應該都被替換成nop了。當然,鼠標應該放在這些scrambled的指令(如果可以這樣說的話)的前面,當然這些都是些垃圾指令了。到這里就真的都完成了。
????????
[C]?dragon
www.wasm.ru/article.php?article=idaplugin
鄙人拙譯
http://greatdong.blog.edu.cn
IDA是迄今為止最為強大的反匯編器,它有著眾多的功能。但是如果它不具備通過附加的模塊來對標準的函數進行擴展的功能(粗俗點說就是plugin)的話,也就有負此盛名了?,F在我們就來更為詳細地研究一下如何編寫這些plugin。
喏,首要的就是要有這個叫做IDA的反匯編器,同時還要有與之匹配的IDA?SDK。SDK里主要的部分就是include目錄及其下的所有文件,這些文件里包含了編寫plugin所用到的所有的宏定義和函數原型,同時用到它們的還有import?lib?——?ida.lib。我這里使用的編譯器是Intel?C++,與VC++結合起來用。我選擇它的原因就是,在SDK中沒有對它進行內嵌的支持(比如在SDK中就有Borland?C++和VC++編譯器的安裝文件),而這能使我們更好地理解。ida.lib文件是ida.wll(位于IDA目錄下)的import?library。其中所有的函數都是用ordinal導出的,而ordinal與函數名的比較是在ida.lib里進行的。這就是需要使用與IDA版本相匹配的SDK的原因了,因為導出函數的ordinal會因版本的不同而發生變化。喏,好了,萬事具備了,我們開始吧。
plugin到底是個什么東東呢?其實它也沒什么稀奇的,就像一個通常的DLL,只是導出了下面這個名為PLUGIN的結構體:
?struct?plugin_t
?{int?version;????????//?IDA的版本,用于plugin的編寫,通常這個域的值為IDP_INTERFACE_VERSION?-?表示SDK版本的constant。
?????????????????????????如果這個域的值與所用IDA的版本不一致,就不會加載此plugin。
??int?flags???????????//?這個好理解,就是些標志。嫌麻煩添0就行了。但這里要講一下所有可能的取值。
?????????????????????????
?????????????????????????PLUGIN_MOD?-?置位表示plugin會修改database(坦白講,沒有什么plugin會修改它)。如果此位置位,
?????????????????????????而進程模塊卻禁止修改database,則plugin就不會被使用。這個標志我們并不需要,另外的幾個也是一樣。
?????????????????????????PLUGIN_DRAW?-?置位表示IDA在調用plugin后需要重畫(即刷新)所有窗口。
?????????????????????????PLUGIN_SEG?-?plugin只在鼠標所放之處的地址屬于某個segment時才能使用。這里也不需要。
?????????????????????????PLUGIN_UNL?-?若置位,則plugin在使用后會從內存中卸載。這對于調試器來說當然是好了,但對我們來說
?????????????????????????就不太合適了。?
?????????????????????????這些標志可以用邏輯或(我們這里用or,C語言里用“|”)結合起來。但我說過,更為簡單的是把所有這些
?????????????????????????域都填成紅色的NULL。
??void?(idaapi*?init)(void);????????//初始化函數加載plugin的時候調用,如果函數返回了什么不好的東西,則plugin就不會被加載。
??void?(idaapi*?term)(void);????????//卸載plugin時調用。一般不使用。
??void?(idaapi*?run)(int?arg);??????//也許這是最重要的域了?-?working?function,每次調用plugin都要運行,包含著plugin的主要
??????????????????????????????????????代碼。
??char*?comment;????????????????????//注釋,作提示用的。
??char*?help;???????????????????????//help自然就是help了.
??char*?wanted_name;????????????????//首選的plugin名。
??char*?wanted_hotkey;??????????????//首選的熱鍵。一般來說不依賴于plugin作者選用的鍵,實際的熱鍵值記錄在plugin.cfg,這個
??????????????????????????????????????文件后面會講到。
};
一般來說,include里的loader.hpp文件里這個結構體是作為類來聲明的,我想對那些不了解類的讀者說,其實在C++里類和結構體在本質上都是一樣的。喏,好像到了該給出源代碼的時候了。我這里給出一個最簡單的plugin,這個plugin會將database中的無用指令:
бесполезные?группы?инструкций:
pusha
popa
push?eax
push?edx
rdtsc
pop?edx
pop?eax
push?edx
push?eax
rdtsc
pop?eax
pop?edx
替換為“更為明了”的nop指令。下面是代碼:
#pragma?comment(linker,"/NODEFAULTLIB")
#pragma?comment(linker,"/Entry:Dllmain")????//Runtime庫占了40kb字節,卻沒什么有用的東西,特別是對plugin。
??????????????????????????????????????????????然而,如果切斷它,則需要重新標記入口點。
#define?__NT__
#define?__IDP__????//這個一定要有,否則plugin就罷工。
typedef?unsigned?char?BYTE;????//這里沒有包含標準的include文件需要額外定義BYTE類型
#i?nclude?<ida.hpp>
#i?nclude?<idp.hpp>
#i?nclude?<bytes.hpp>
#i?nclude?<loader.hpp>
#i?nclude?<kernwin.hpp>????//"紳士的選擇"?IDA中的頭文件
??//重新標記入口點
bool?Dllmain(void*?hInstDLL,?int?reason,?int?reserved)
{return?true;}
char?comment[]?=?"Experimental?plugin";
char?help[]?=?"Experimental?plugin";
char?wanted_name[]?=?"ExpPlugin";
char?wanted_hotkey[]?=?"F11";
BYTE?rdtsc1[]={0x52,?0x0F,?0x31,?0x5A,?0x58};
BYTE?rdtsc2[]={0x50,?0x0F,?0x31,?0x58,?0x5A};????//就是些opcodes
??//plugin的initialization?procedure
根據不同的返回值,IDA可以加載或者不加載plugin??赡芊祷氐闹涤?#xff1a;
????PLUGIN_SKIP?-?不加載返回值。
????PLUGIN_OK?-?喏,一個OK自然要加載plugin啦。
????PLUGIN_KEEP?-?不但要加載,還要常駐內存。
比如說,要想讓plugin只處理PE格式的文件,需要這樣寫initialization?procedure:
if?(inf.filetype?!=?f_PE)?return?PLUGIN_SKIP;
return?PLUGIN_OK;
這里就不研究那個inf結構體了,詳情請見SDK。我們這里總是要加載plugin的,所以代碼如下:
int?init(void)
{return?PLUGIN_OK;}
????//在unload?procedure里也是什么也不用干,空著它。
void?term(void)
{return;}
????//最后就是主要的run函數了
在研究主函數之前有興趣可以研究一下database的讀寫函數,還有獲取當前地址的函數,也即IDA中鼠標所放之處的地址:
?get_byte
?get_word
?get_long?-?從database中讀取各種長度字節。接受一個參數——listing中的地址。
?patch_byte
?patch_word
?patch_long?-?向database中寫入各種長度字節。兩個參數,第一個——地址,第二個——值。
可以猜到,名字里有byte的函數是用于(即接受或返回)字節的,word——字,而long——雙字。
?get_screen_ea?-?獲取當前地址的函數,當前地址就是它的返回值。
void?run(int?arg)
{BYTE?cb1;
??int?i,?tmp0;
?ea_t?CA?=?get_screen_ea();
?cb1=get_byte(CA);
????//pusha
????//popa
?if?(cb1==0x60?&&?get_byte(CA+1)==0x61)????//?pusha的opcode?-?60h,而popa?-?61h
???{patch_word(CA,?0x9090);
?????return;}
????//rdtsc
?if?(cb1==0x50)????//序列的第一個字節?-?push?eax的opcode
???{tmp0=1;
????for?(i=0;?i<5;?i++)????//數組中的其它字節,使用循環檢驗
????if?(get_byte(CA+i+1)!=rdtsc1[i])?{tmp0=0;?break;}
????if?(tmp0==1)
??????{patch_long(CA,?0x90909090);
????????patch_word(CA+4,?0x9090);
????????return;}}
?if?(cb1==0x52)
???{tmp0=1;
?????for?(i=0;?i<4;?i++)
?????if?(get_byte(CA+i+1)!=rdtsc2[i])?{tmp0=0;?break;}
?????if?(tmp0==1)
???????{patch_long(CA,?0x90909090);
?????????patch_word(CA+4,?0x9090);
?????????return;}}
};
????//現在放入前面講過的導出結構體。
????//一定要寫上extern?"C",使得linker能正確找到導出結構體的名字
extern?"C"?plugin_t?PLUGIN?=?{
IDP_INTERFACE_VERSION,
0,
init,
term,
run,
comment,
help,
wanted_name,
wanted_hotkey
};
????????現在我們來看如何將它編譯。以下是bat文件:
icl?-Gz?/I?<SDK的include目錄的路徑>?plugin.cpp?ida.lib?/link?/subsystem:windows?/DLL?/def:plugin.def?/out:exp.plw
del?"<IDA目錄路徑>\plugins\exp.plw"
copy?exp.plw?"<IDA目錄路徑>\plugins\"
PAUSE
我已經說過,此處是以Intel?C++為例的,如果用VC++的話,源代碼不用改,而編譯用的命令行則需要修改(不過就是把icl換成cl)?,F在來詳細講一下。
-Gz告訴intel?complier,默認使用stdcall規則向函數傳遞參數。
/I?-?IDA?SDK的include目錄的路徑
plugin.cpp?-?plungin源代碼。
ida.lib?-?SDK的import?library,應該和源代碼放入一個目錄。
/link參數的后面是liner?options,就不用多說了。除了def文件。因為這里已經有源代碼和bat文件了,就需要生成def文件。
LIBRARY?exp
EXPORTS?PLUGIN
def文件指明需要導出PLUGIN結構體。一般來說,到這里就都完成了,plugin已經可以使用了,剩下的只是把它加到IDA里了??梢园阉截惖絀DA的plugins目錄里。但這還不夠,還需要配置一下。所有plugins的配置都保存在plugins目錄下plugins.cfg文件里。通常文件的起始處是注釋,后面就是plugin的配置了。為了將我們的exp.plw(Windows下的plugin的擴展名都是plw)加入進去,需要在文件末尾加入四個參數,順序?如下:
????plugin名??文件名??熱鍵??參數。
這里的參數就是傳給run函數的,我個人覺得沒什么用,所以就設為0。熱鍵嘛,可以設為F11,好像IDA用不到這個鍵。這樣,我們所添加的這一行就是下面這個樣子的:
????????expplugin????exp????F11????0
不主張在前面寫一個句點加一個逗號,因為那樣就成了注釋。喏,一般來說可以認為,mission?accomplished。剩下的就是啟動IDA,打開某個文件,那個文件里要有我們前面給出的指令(比如,可以自己立馬用ASM寫一個),然后進行檢查。如果沒有問題,就按下F11鍵或從菜單里選擇我們的plugin,這時那些指令就應該都被替換成nop了。當然,鼠標應該放在這些scrambled的指令(如果可以這樣說的話)的前面,當然這些都是些垃圾指令了。到這里就真的都完成了。
????????
[C]?dragon
www.wasm.ru/article.php?article=idaplugin
鄙人拙譯
http://greatdong.blog.edu.cn
總結
以上是生活随笔為你收集整理的IDA Plugin 编写基础的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用IDA的调试器
- 下一篇: 基于 ida 的反汇编转换 Obj 的可