我想向現有的二進製文件添加一些功能。二進製文件是使用 gcc
創建的。
- 即使我足夠了解程序的功能,我還是需要首先對二進製文件進行反編譯嗎?
- 我應該如何添加必要的代碼?
- 我需要任何工具來做到這一點嗎?
我想向現有的二進製文件添加一些功能。二進製文件是使用 gcc
創建的。
您可以通過多種方法進行此操作。
動態檢測
諸如 PIN, Valgrind或 DynamoRIO允許您動態更改程序的行為。例如,您可以在特定地址添加對新函數的調用,攔截庫調用並對其進行更改,等等。
缺點是動態檢測通常具有高開銷。
靜態檢測
您也可以嘗試靜態修改程序以添加所需的行為。挑戰之一是您經常需要弄混可執行文件格式。為此,存在一些工具,例如 ERESI項目中的 elfsh
,但是我發現它們有故障並且難以使用。
另一種靜態策略檢測是為了“重新編譯”。您可以通過反編譯程序,修改源代碼並重新編譯來實現。從理論上講,您也可以使用 BAP之類的工具將程序提升為IL,進行修改,然後使用LLVM重新編譯。但是,當前版本可能還不夠成熟。
動態加載
您可以使用 LD_PRELOAD
覆蓋功能將被動態鏈接。當您想更改庫函數的行為時,這是一個不錯的選擇。自然,它不適用於靜態鏈接的二進製文件或靜態函數。
二進制修補程序
您通常可以使用十六進制編輯器。例如,如果您要跳過一個函數調用或分支,則通常可以用 nop
指令替換它。如果您需要添加大量新代碼,則可能需要使用 ERESI項目中的 elfsh
之類的內容來幫助您調整二進製文件的大小。
通常,您可以通過仔細地鉤入程序來更改其行為。是否可以通過這種方式添加所需的功能取決於程序的構造方式。如果該程序以一個主要可執行文件和幾個庫的形式出現,將有幫助。
您可以通過首先使用 LD_PRELOAD鏈接自己的庫,來掛接該程序對共享庫的任何調用。
。編寫一個定義函數 foo
的庫,並在啟動時將環境變量 LD_PRELOAD
設置為已編譯( .so
)庫的路徑程序:然後該程序將調用您的 foo
而不是它想要的。您可以通過使用 dlsym()
獲取指向其替換的原始 foo
函數。
以下是一些示例和教程:
一些使用 LD_PRELOAD
的程序示例:
LD_PRELOAD 的局限性在於,您只能攔截在運行時解析的函數調用(動態鏈接)。如果要攔截內部呼叫,則必須採用較重的技術(在磁盤上或使用 ptrace
在內存中修改可執行文件)。
我想在現有的二進製文件中添加一些功能。
因此,通常,這四個更大的問題適用於修改Executeable:
提出的第一個基本問題:程序是否警惕代碼修改(自我檢查,反調試技巧,複製保護...)?
如果這樣:
第二個問題是:
您能找出使用哪個編譯器/語言生成可執行文件嗎?
更多詳細信息會更好,但是大多數基本結構( if
和其他控制結構)在各種編譯器上的映射應非常相似。
這與先前關於RE-Stackexchange的問題。
第三個問題是:
如何實現用戶界面(CLI,Win32-Window控件,自定義...)?
如果已知:
您能否找出常見的HLL構造(菜單,下拉菜單,複選框等)與要修改的二手編譯器/語言的映射?
第四個最大的問題是:
如何在程序中創建所需的功能?
從本質上講,這可能需要很多反向操作
中心點:如何利用現有的內部API達到目標,而又不會破壞Stuff(例如CRTL + Z,版本控制,恢復)功能)?
示例項目: b>
關於包裝的代碼&反編譯器: 我不會談論與VM /解釋器(Py2Exe,Java 2 Exe,...)打包在一起或使用已安裝的代碼(JVM,C#)打包的其他語言的包裝代碼。在某些情況下,有相當不錯的反編譯器。成功進行反編譯後,它幾乎可以歸結為克服“代碼混淆”(如果有的話)。
關於C / C ++-反編譯器:
我不能談論C / C ++-Decompilers,儘管可以歸結為盡力而為的HLL-Remapping(對於Decompiler沒得到的東西)和Code-Deobfuscation(如果它是在沒有符號的情況下編譯的),前提是在可執行文件中沒有進一步的保護。 p>
有關HLL映射的建議:
本質上,該問題的很大一部分涉及的“ HLL映射”(高級語言映射(在機器代碼中))和的修改。這些結構在相應的機器代碼中。
我在此處(binary-auditing.com)上找到了一個非常好的可下載的入門課程,該課程使用“ IDA Free”。
(略微過時,但正如之前在本線程中未提到的那樣)
很久以前,我花了幾個月的時間來擴展僅包含二進製文件的軟件。
然後,因為它是x86 PE二進製文件,所以我使用了Tasm和Iczelion的代碼段創建器 >:它不再是一個著名的工具,但是它允許透明地使用Tasm並重新註入代碼,以及進行PE轉換等。
它在EntryPoint上添加了代碼,所以我手動製作了自己的補丁,然後跳到原始的EntryPoint。
現在有點老套了-這些天我可能會注入DLL-但它確實起作用了。
並且至少,它通過ASM給予您完全控制,同時通過自動修補來保持可維護性。
您不需要反編譯二進製文件。如果您了解要進行哪些更改,並且僅通過修改二進製文件或其依賴項就可以進行更改,則只需在磁盤或內存中進行這些更改即可。
關於如何實現修改本身,您有幾種選擇。
您可以使用LD_PRELOAD使鏈接器在二進製文件運行之前加載共享對象。然後,您根本不需要修改磁盤上的二進製文件。這是valgrind的工作方式,它作為共享對象加載,然後開始動態二進制檢測。
您可以使用 valgrind。 Valgrind將允許您動態地重新編寫程序並任意修改其行為。 Valgrind是一個動態的二進制檢測程序,允許其工具在執行時編輯該程序。如果您只想更改程序行為,則可能會起作用,但是valgrind也會導致全局變慢,並且如果您想修補和重新分發程序,則可能不理想。
您還可以使用 elfsh / eresi之類的工具將新代碼插入程序。這些工具應注意將代碼插入與ELF程序標頭之類的東西有關的行為。您可以在Google上搜索“ ELF感染器”的概念,在該概念中,注入的代碼將成為新程序的入口點,執行某些操作,然後跳轉到舊程序的入口點。
儘管此問題集中在Linux上,我個人將使用其他答案中概述的簡單的 LD_PRELOAD
方法,但Windows知道類似的機制,實際上,它在最近的一段時間內已經被濫用(另請參見下面的替代方法)。我用那種方法“破解”了一個加密狗系統。事實證明,該方法是最近才發現的,方法是將DLL放在遠程共享上,然後導航到共享,例如媒體播放器,這將導致媒體播放器加載遠程DLL而不是本地版本。這是設計使然。現在更改它會破壞成百上千的應用程序。
這已經由Microsoft 以某些方式解決,儘管唯一真正的解決方案是在應用程序端正確實現。但是自從Windows 2000成為第一個基於NT平台的消費者操作系統以來,即使我們不得不處理NT安全,許多開發人員甚至都沒有掌握NT安全。
添加功能不一定意味著您在磁盤上修補了可執行文件。您還可以在內存中執行此操作。
只要應用程序使用DLL,並且您可以使用 Dependency Walker告訴加載順序 h> a>或在調試器下,您可以選擇它導入的DLL之一併替換(在其當前位置),或在加載順序中將另一個DLL放置在現有DLL之前的路徑中。
另一種方法是更改導入的DLL的名稱。在極少數情況下(例如眾所周知的DLL),這是加載備用DLL的唯一可行方法,在某些特殊情況下仍可能失敗。
如果所用的DLL位於DLL搜索順序的第一個位置,則必須從字面上替換磁盤上的文件,除非您如上所述重命名了導入。
只有很少幾個導出符號的DLL可以使用手動方法。最簡單的方法是從DLL創建模塊定義文件,然後從該文件創建僅具有函數轉發器的DLL。這樣,您放置的DLL將已經加載,並且只需通過調用即可。
但是,這種方法對於導出的變量(相對於函數)將失敗。
這是一個簡單的方法我基於 pefile
編寫的Python腳本,它是在StackOverflow上的另一個答案:
import osimport sysimport redef main(pename):從pefile導入PE打印“ Parsing%s”%pename pe = PE(pename)modname = os.path.basename(pename)libname = re.sub(r“(?i)^。*?( [^ \\ /] +)\。(?: dll | exe | sys | ocx)$“,r” \ 1.lib“,modname)defname = libname.replace(”。lib“,” .def“)打印“為%s編寫模塊定義文件%s”%(defname,modname)f = open(defname,“ w”)#希望它拋出,這裡沒有復雜的錯誤處理f.write(“庫%s \ n \ n“%modname)f.write(” EXPORTS \ n“)numexp = 0,用於pe.DIRECTORY_ENTRY_EXPORT.symbols:if exp.name:numexp + = 1 f.write(” \ t%s \ n“%exp .name)打印“寫%帶有%d導出的s“%(defname,numexp)打印” \ n \ n使用此命令創建導出lib:\ n \ tlib / def:%s / out:%s“%(defname,libname)如果__name__ == '__main__':如果len(sys.argv)!= 2:sys.stderr.write(“ ERROR:\ n \ t語法:fakelib <dllfile> \ n”)sys.exit(1)sys.exit(main(sys.argv [1]))
您可以對其進行調整以創建功能轉發器,而不是使用導出名稱的簡單模塊定義。
因此您可以將代碼穿梭到目標應用程序中並從那裡運行的方法。
已經提到了儀器和掛鉤。 Detours(彎路)是經常被提及的示例,該鉤子出於大多數實際目的而使用了不方便的EULA。請參閱有關這種方法的現有答案。
您還可以使用 AppInit_DLL
註冊表值儘早插入DLL。或者,您可以編寫一個帶有調試器循環的啟動器,並使用 Image File Execution Options
使目標首先啟動調試器。調試器還可以影響DLL的加載,也可以方便地在可執行文件和DLL之間的邊界處攔截(方便地)調用。當您在Process Explorer中選擇選項時,將替換Task Manager。
您將注意到如何將這些方法歸類為 Ed McMan在其答案中提到的。但是,我將其留給讀者練習:)
我在Windows上使用Notepad.exe完成了此操作。我想添加一個頂級菜單項來打開calc.exe只是為了好玩(我知道您的問題被標記為Linux和gcc編譯器,但是想法可能是相同的。)
所以我使用了 Resource Hacker工具添加Calc菜單,並在 Immunity Debugger上打開notepad.exe,在代碼中尋找一些空間,以便放置 WinExec shellcode。最初我沒有更改可執行文件,我不得不查看內存中的程序以找到一些空間,可以在其中粘貼我的彙編指令而不會導致記事本崩潰。
一旦我找到足夠的空間(通過更改原始代碼,消除了一些不需要的彙編指令甚至優化它們)我在 XVI Hex Editor上打開了notepad.exe,並蒐索了在Immunity上運行的操作碼。我的意思是,調試器正在運行一些操作碼,對嗎?我只是搜索了一系列操作碼,以確保我位於要更改的軟件的正確位置,並用我的shellcode(現在不是彙編代碼,而是“已編譯”的彙編-機器代碼)對其進行了封裝再次:我知道您的問題被標記為Linux和gcc編譯器,但也許有人可以指出Linux中的一些工具來實現與Windows相同的功能。這個想法可能是相同的。
“ ptrace()”是Giles偶然提到的。但是我認為它本身應該值得一整節。 “ ptrace()”是OS提供的系統調用API(Linux和所有UNIX都有,Windows也是如此),以對另一個進程施加調試控制。當您使用PTRACE_ATTACH(作為ptrace()的一部分)附加到另一個進程時,內核將完全暫停運行該進程的CPU,從而允許您對進程的任何部分進行更改:CPU,任何寄存器,該部分的任何部分進程內存等。這就是動態內聯掛鉤的工作方式。 (附加ptrace(),修改內存中的二進製文件,然後附加ptrace())。據我所知,對另一個進程的所有動態修改都必須使用ptrace(),因為這是內核提供的唯一機制,可以通過系統調用來保證系統調用的完整性。 utrace()彈出了,因此理論上也可以進行內聯掛鉤:
http://landley.net/kdocs/ols/2007/ols2007v1-pages-215-224.pdf
對於內核掛鉤,有許多方法:syscall,中斷和內聯掛鉤。這是用於中斷掛鉤的: