1产舞、背景
在CTF比賽中, CTF逆向題目除了需要分析程序工作原理, 還要根據(jù)分析結(jié)果進(jìn)一步求出FLAG。逆向在解題賽制中單獨(dú)占一類題型, 同時(shí)也是PWN題的前置技能音榜。在攻防賽制中常與PWN題結(jié)合庞瘸。CTF逆向主要涉及到逆向分析和破解技巧,這也要求有較強(qiáng)的反匯編赠叼、反編譯擦囊、加解密的功底。
CTF中的逆向題目一般常見考點(diǎn)
1嘴办、常見算法與數(shù)據(jù)結(jié)構(gòu)瞬场。
2、各種排序算法, 樹, 圖等數(shù)據(jù)結(jié)構(gòu)涧郊。
3贯被、識(shí)別加密算法與哈希算法代碼特征,識(shí)別算法中魔改的部分。
4妆艘、代碼混淆, 代碼虛擬化, 修改代碼流程, 反調(diào)試等彤灶。
5、軟件加密殼是軟件保護(hù)技術(shù)的集中應(yīng)用批旺。
CTF逆向題目兩大主題:暴力破解幌陕、算法分析破解
1、暴力破解:通過修改匯編代碼而跳過程序內(nèi)部校驗(yàn)部分汽煮,從而改變程序的正常邏輯搏熄,最后滿足題目要求來獲取flag。這就考驗(yàn)在逆向分析樣本時(shí)候暇赤,對樣本驗(yàn)證代碼定位能力田藐。
2罢吃、算法破解:這主要需要分析樣本中的加密部分的匯編代碼郊供,將其加密算法進(jìn)行還原钠至,并根據(jù)分析結(jié)果寫出對于的解密程序,最后算出flag溜腐。這就考驗(yàn)了對樣本分析過程中的耐心和扎實(shí)的逆向能力和一定的代碼開發(fā)能力坯门。
逆向解題常見技巧
1、逆向分析與功能猜測結(jié)合, 通過逆向分析進(jìn)行縮小猜測范圍, 猜測為逆向指出方向, 逆向再驗(yàn)證猜測的思路逗扒。
2古戴、結(jié)合樣本中匯編代碼上下文與整體程序功能, 關(guān)注程序中給出的文字提示信息。
3矩肩、實(shí)際比賽的逆向題目多數(shù)是為出題而出題, 有目的性強(qiáng), 功能結(jié) 構(gòu)單一, 無關(guān)代碼少等特點(diǎn)现恼。
4肃续、遇到程序代碼量極大時(shí), 可以先判斷是否引用了較多的開源的代碼, 而程序的主邏輯相對簡單。
2叉袍、逆向基礎(chǔ)
CTF逆向常用工具
Ollydbg始锚、IDA、PCHunter喳逛、Exeinfo PE(PeTool)瞧捌、CFF Explore、exeScope润文、ApiMonitorTrial姐呐、winhex。
CTF逆向需涉及知識(shí)點(diǎn)
匯編知識(shí):window下的X86和X64; android下的ARM和ARM64典蝌。
文件結(jié)構(gòu):windox下的PE文件; android下的dex文件和ELF文件曙砂。
反調(diào)試技術(shù): window和android下的調(diào)試和反調(diào)試對抗技術(shù)。
加殼和脫殼:PE的加殼和脫殼骏掀、 ELF和dex的加殼和脫殼鸠澈。
開發(fā)能力:C、C++截驮、python等語言的開發(fā)能力笑陈。
CTF樣本逆向解題流程
1、突破保護(hù):
獲得樣本程序時(shí)先用ExeInfoPePE工具進(jìn)行查看程序?qū)儆谀钠脚_(tái)下的葵袭,例如windows X86/X64新锈、android、linux等眶熬,是否采用代碼保護(hù)措施,例如:代碼混淆块请、保護(hù)殼娜氏、各種反調(diào)試等,如果有那么在進(jìn)行分析樣本之前墩新,需要先過掉樣本混淆贸弥、脫殼、反反調(diào)試等技術(shù)來去除或繞過這些保護(hù)措施海渊。
2绵疲、定位關(guān)鍵代碼:
我們需要將目標(biāo)軟件進(jìn)行反匯編,然后結(jié)合IDA和OD快速定位到關(guān)鍵代碼(例如驗(yàn)證函數(shù)臣疑、關(guān)鍵字符串信息盔憨、程序?qū)氡恚?/p>
3、動(dòng)靜結(jié)合:
我們找到程序的關(guān)鍵代碼之后讯沈,就要對其進(jìn)行詳細(xì)的逆向分析郁岩。如果程序在IDA中F5可以生成偽代碼,那么我們就先根據(jù)偽代碼進(jìn)行靜態(tài)分析,然后模糊不清的地方可以結(jié)合ollydbg工具進(jìn)行動(dòng)態(tài)調(diào)試问慎,觀察來驗(yàn)證自己的猜想萍摊。
4、破解驗(yàn)證算法:
通過詳細(xì)逆向分析完如叼,程序的關(guān)鍵代碼(例如:驗(yàn)證算法)之后冰木,接著就要根據(jù)分析出的結(jié)果,進(jìn)行暴力破解或者進(jìn)行算法解密代碼的編寫以此來獲取或生成flag笼恰。
CTF中常見的驗(yàn)證算法
1踊沸、 直接比較驗(yàn)證:
密鑰一般沒有經(jīng)過加密,直接跟內(nèi)置程序中的key進(jìn)行比較(也就是硬編碼方式比較)挖腰,此類題型比較簡單雕沿。
2、加密比較驗(yàn)證:
密鑰一般會(huì)進(jìn)行如異或猴仑、base64审轮、MD5、RC4等形式的加密辽俗,此類題型需要識(shí)別出其加密方式疾渣,然后再根據(jù)其算法特點(diǎn)還原出相應(yīng)的key值,現(xiàn)實(shí)CTF比賽中可能出現(xiàn)密鑰用不同加密算法分段加密或嵌套驗(yàn)證等多種加密組合起來的方式崖飘,也需要去深入識(shí)別分辨榴捡。
3、 逆向自定義實(shí)現(xiàn)的算法:
這類題目就需要去逆向題目作者自己編寫實(shí)現(xiàn)的算法了朱浴,這樣的算法難度相對比較難吊圾,在逆向過程中需要識(shí)別出其是算法問題題目,然后分析出其每個(gè)函數(shù)代表什么操作來進(jìn)一步解題翰蠢,需要逆向算法的題目一般都比較難项乒,需要理清算法實(shí)現(xiàn)思路,跟蹤自己輸入的數(shù)據(jù)使用算法進(jìn)行了哪些處理梁沧,最后變成了什么和什么比較檀何,需要有一定的耐心和細(xì)心,建議多加練習(xí)廷支。
4频鉴、其他類型的加密題目:
實(shí)在解不出來的,也可以嘗試是否可以繞過或暴力破解窮舉等方式恋拍。
定位關(guān)鍵代碼的方法
1垛孔、順序跟蹤法:
如果拿到的樣本程序相對較小,代碼量不多且主函數(shù)入口好找施敢,即可使用順序跟蹤法似炎,從程序主函數(shù)入口順序跟蹤辛萍,一步步分析完整的程序執(zhí)行過程,基本就能知道程序的驗(yàn)證部分了羡藐,至于各個(gè)類型程序主函數(shù)的查找方法大家可以自行百度贩毕,這里還需要分清楚程序入口點(diǎn)和main函數(shù)的區(qū)別,大家一般需要找main函數(shù)仆嗦,但也不全是找main函數(shù)辉阶,如果遇到了MFC的程序大家還需要根據(jù)具體情況具體分析,如果有條件大家也可以編碼實(shí)現(xiàn)相應(yīng)程序瘩扼,然后反匯編來進(jìn)行練習(xí)查找程序的主函數(shù)谆甜,這個(gè)需要平時(shí)多練習(xí)、多積累集绰、多總結(jié)规辱。
2、 字符串查找:
如果給定的樣本程序沒有做混淆處理栽燕,而且還有比較明顯的字符串信息提示罕袋,那么就可以根據(jù)程序運(yùn)行的提示,使用字符串查找功能查找程序所提示字符串的方式來反向查找其被引用的地址碍岔。例如IDA中shift+F12的字符串窗口浴讯,OD中查找->所有參考文本字串,字符串搜索的優(yōu)先級(jí)很高蔼啦,很多情況下對我們解題很有奇效榆纽,所以拿到程序后可以優(yōu)先字符串查找嘗試。
3捏肢、系統(tǒng)函數(shù)斷點(diǎn):
如果程序非常大而且也沒有啥字符串提示信息可以利用奈籽,那么我們就可以猜測根據(jù)樣本程序所使用的函數(shù)來定位關(guān)鍵驗(yàn)證代碼,這需要掌握C語言或C++語言還有windows核心編程的知識(shí)鸵赫,熟悉一些通用函數(shù)具有的功能衣屏,例如:程序出現(xiàn)了一個(gè)彈窗,那么程序就有可能調(diào)用了MessageBox這個(gè)函數(shù)奉瘤,如果程序出現(xiàn)了輸出,那么程序就有可能調(diào)用了printf這個(gè)函數(shù)等等煮甥,所以可以通過程序所表現(xiàn)出的狀態(tài)來下相應(yīng)的函數(shù)斷點(diǎn)盗温,然后棧回溯反向查找其引用位置成肘,進(jìn)而找到關(guān)鍵代碼卖局。
3、匯編基礎(chǔ)
(以下知識(shí)點(diǎn)只起到拋磚引玉的作用)
X86匯編
32位CPU有16個(gè)寄存器,32位寄存器存放的是4個(gè)字節(jié)的數(shù)據(jù)双霍,它們名稱分別為:
4個(gè)數(shù)據(jù)寄存器(EAX砚偶、EBX批销、ECX和EDX);
2個(gè)變址和指針寄存器(ESI和EDI);
2個(gè)指針寄存器(ESP和EBP);
6個(gè)段寄存器(ES、CS染坯、SS均芽、DS、FS和GS);
1個(gè)指令指針寄存器(EIP);
1個(gè)標(biāo)志寄存器(EFlags)单鹿。
X64匯編
64位CPU有16個(gè)通用寄存器掀宋,寄存器存放8個(gè)字節(jié)數(shù)據(jù),它們名稱分別為:
rax,rbx,rcx,rdx,rsi,rdi,rsp,rbp
r8,r9,r10,r11,r12,r13,r14,r15
32位使用棧幀來作為傳遞的參數(shù)的保存位置仲锄,而64位使用寄存器劲妙,分別用rdi,rsi,rdx,rcx,r8,r9作為第1-6個(gè)參數(shù)。rax作為返回值儒喊。
64位沒有棧幀的指針镣奋,32位用ebp作為棧幀指針,64位取消了這個(gè)設(shè)定怀愧,rbp作為通用寄存器使用侨颈。
rax 作為函數(shù)返回值使用。
rsp 棧指針寄存器掸驱,指向棧頂
rdi肛搬,rsi,rdx毕贼,rcx温赔,r8,r9 用作函數(shù)參數(shù)鬼癣,依次對應(yīng)第1參數(shù)陶贼,第2參數(shù)。待秃。拜秧。
rbx,rbp章郁,r12枉氮,r13,r14暖庄,r15 用作數(shù)據(jù)存儲(chǔ)聊替,遵循被調(diào)用者使用規(guī)則,簡單說就是隨便用培廓,調(diào)用子函數(shù)之前要備份它惹悄,以防他被修改。
r10肩钠,r11 用作數(shù)據(jù)存儲(chǔ)泣港,遵循調(diào)用者使用規(guī)則暂殖,簡單說就是使用之前要先保存原值。
通用匯編知識(shí)
[圖片上傳失敗...(image-2b90a1-1644822266789)]
[圖片上傳失敗...(image-145cfb-1644822266789)]
函數(shù)(Call)的3種調(diào)用約定:_cdecl,_stdcall当纱、_fastcall
_cdecl:是C語言的默認(rèn)的函數(shù)調(diào)用方法呛每,所有參數(shù)從右到左依次入棧,這些參數(shù)由調(diào)用者去清除惫东。堆椑蚋恢復(fù)常用指令add,esp,x ,x表示參數(shù)占用的字節(jié)數(shù)
_stdcall:是C++標(biāo)準(zhǔn)的函數(shù)調(diào)用方式廉沮,所有參數(shù)從右到左依次入棧颓遏,如果是調(diào)用類成員的話,最后一個(gè)入棧的是this指針滞时。
這些堆棧中的參數(shù)由被調(diào)用的函數(shù)在返回后清除叁幢,使用的指令是ret x,其中x表示參數(shù)占用的字節(jié)數(shù)坪稽。
_fastcall:是編譯器指定的函數(shù)快速調(diào)用方式曼玩。由于大多數(shù)函數(shù)參數(shù)個(gè)數(shù)很少,使用堆棧傳遞比較費(fèi)時(shí)窒百。因此_fastcall通常規(guī)定前兩個(gè)參數(shù)由寄存器傳遞黍判,其余參數(shù)還是通過堆棧傳遞。但是不同的編譯器編譯的程序規(guī)定的寄存器不同篙梢,返回方式一般是ret x顷帖。
總結(jié)三個(gè)調(diào)用約定:
[圖片上傳失敗...(image-ee83a2-1644822266788)]
函數(shù)參數(shù)和局部變量區(qū)分
函數(shù)的局部變量的存在形式:mov eax, dword ptr[ebp -4]
函數(shù)參數(shù)表示法:mov eax, [esp+arg_0]
Arm32匯編(Android)
ARM微處理器共有37個(gè)32位寄存器渤滞,其中31個(gè)為通用寄存器贬墩,6個(gè)為狀態(tài)寄存器。但是這些寄存器不能被同時(shí)訪問妄呕,具體哪些寄存器是可以訪問的陶舞,取決ARM處理器的工作狀態(tài)及具體的運(yùn)行模式。但在任何時(shí)候绪励,通用寄存器R14~R0肿孵、程序計(jì)數(shù)器PC、一個(gè)狀態(tài)寄存器都是可訪問的疏魏。
未分組寄存器 R0 ~ R7停做,共8個(gè);
分組寄存器 R8 ~ R12蠢护,R13 ~ R14 R8 ~ R12:其中FIQ模式下有單獨(dú)的一組 R8 ~ R12雅宾,共5個(gè)养涮;
另外 6種模式共用一組R8 ~ R12葵硕,共5個(gè)眉抬;總共10個(gè);
R13 ~ R14:其中USR和SYS模式(表格的第一列)共用一組R13 ~ R14共2個(gè)懈凹,另外5種模式下各有獨(dú)自的一組R13 ~ R14共10個(gè)蜀变;總 共12個(gè);
程序計(jì)數(shù)器 PC 即R15介评,共1個(gè)库北;
分組寄存器R13、R14
寄存器R13通常做堆棧指針SP
寄存器R14用作子程序鏈接寄存器(Link Register-LR)们陆,也稱為LR寒瓦,指向函數(shù)的返回地址。
Arm64匯編(Android)
匯編中共有34個(gè)寄存器坪仇。其中包括31個(gè)通用寄存器杂腰、SP寄存器、PC寄存器椅文,CPSR寄存器喂很。
31個(gè)通用寄存器中:
X0-X30:表示是64位的寄存器。
W0-W30:表示是32位的寄存器皆刺。
X31 : 也稱為零寄存器(它一般用于變量的初始化)少辣,它也有兩表現(xiàn)形式:XZR:表示是64位的零寄存器, 它在內(nèi)存中是用8個(gè)字節(jié)存儲(chǔ)羡蛾。
WZR:表示是32位的零寄存器漓帅,它在內(nèi)存中是用4個(gè)字節(jié)存儲(chǔ)。
SP : 保存棧指針(棧頂指針)林说,使用SP或WSP來進(jìn)行對SP寄存器的訪問,也就是用于操作局部變量地址煎殷。
PC:程序計(jì)數(shù)器(PC指針寄存器),它用于指向即將要執(zhí)行的下一條指令腿箩。
CPSR:狀態(tài)寄存器
FP(X29):保存棧幀地址(棧底指針)
LP(X30):通常稱X30為程序的鏈接寄存器豪直,保存子程序結(jié)束后需要執(zhí)行的下一條指令。
[圖片上傳失敗...(image-2ca357-1644822266788)]
掌握ARM匯編中基本結(jié)構(gòu)前需要回顧和并熟記以下的條件指令
[圖片上傳失敗...(image-ee1cc6-1644822266788)]
怎樣在ARM匯編中去識(shí)別和定位出函數(shù)
1.1珠移、 B 跳轉(zhuǎn)指令
1.2弓乙、 BL 帶返回的跳轉(zhuǎn)指令
1.3、 BLX 帶返回和狀態(tài)切換的跳轉(zhuǎn)指令
1.4钧惧、 BX 帶狀態(tài)切換的跳轉(zhuǎn)指令
2.直接向程序計(jì)數(shù)器 PC 寫入跳轉(zhuǎn)地址值暇韧。
通過向程序計(jì)數(shù)器 PC寫入跳轉(zhuǎn)地址值,可以實(shí)現(xiàn)在 4GB 的地址空間中的任意跳轉(zhuǎn),在跳轉(zhuǎn)之前結(jié)合使用MOV LR,PC
總結(jié):識(shí)別函數(shù)的方法就是匯編指令中是否有包含:B浓瞪、BL懈玻、BLX、BX乾颁、PC的匯編指令涂乌。
ARM函數(shù)調(diào)用約定采用的是:ATPCS
ATPCS的英文全稱是ARM-THUMB procedure call standard(ARM-Thumb過程調(diào)用標(biāo)準(zhǔn))
總結(jié):參數(shù)1~參數(shù)4 分別保存到 R0~R3 寄存器中 艺栈,剩下的參數(shù)從右往左一次入棧,被調(diào)用者實(shí)現(xiàn)棧平衡湾盒,返回值存放在 R0 中湿右。
函數(shù)參數(shù):
當(dāng)參數(shù)個(gè)數(shù)小于等于4個(gè)的時(shí)候,使用r0到r3這4個(gè)寄存器進(jìn)行參數(shù)傳遞罚勾;如果參數(shù)個(gè)數(shù)大于4個(gè)毅人,余下的參數(shù)就通過sp所指向的數(shù)據(jù)棧進(jìn)行參數(shù)傳遞。
比如有3個(gè)參數(shù)的話尖殃,那么r0代表函數(shù)的第一個(gè)參數(shù)丈莺,r1代表函數(shù)的第二個(gè)參數(shù),r2代表函數(shù)的第三個(gè)參數(shù)送丰。
比如有6個(gè)參數(shù)的話场刑,那么r0-r3表示前面4個(gè)參數(shù),然后余下的兩個(gè)參數(shù)通過在棧上開辟8字節(jié)的空間進(jìn)行參數(shù)傳遞蚪战。
r0–r3:存儲(chǔ)傳遞給函數(shù)的參數(shù)值牵现,多余的參數(shù)通過壓棧傳遞。
r4 -r11:存儲(chǔ)函數(shù)的局部變量邀桑,Thumb模式不會(huì)使用r8以后的寄存器
r12:是內(nèi)部過程調(diào)用暫時(shí)寄存器(intra-procedure-call scratch register)瞎疼。
r13:存儲(chǔ)棧指針(sp)。在計(jì)算機(jī)中壁畸,棧非常重要贼急。這個(gè)寄存器保存著棧頂?shù)闹羔槨_@里可以看到更多關(guān)于棧的信息捏萍。
r14:鏈接寄存器(link register)太抓。存儲(chǔ)著當(dāng)被調(diào)用函數(shù)返回時(shí),將要執(zhí)行的下一條指令的地址令杈。
r15:用作程序計(jì)數(shù)器(program counter)走敌。存儲(chǔ)著當(dāng)前執(zhí)行指令的地址。每條執(zhí)行被執(zhí)行后逗噩,該計(jì)數(shù)器會(huì)進(jìn)行自增(+1)掉丽。
函數(shù)的返回值放到r0中。
fp叫做frame pointer寄存器异雁,即棧幀指針寄存器捶障;sp叫做stack pointer寄存器,即棧指針寄存器纲刀。
在ARM指令系統(tǒng)中是地址遞減棧项炼,入棧操作的參數(shù)入棧順序是從右到左依次入棧,而參數(shù)的出棧順序則是從左到右的你操作。包括push/pop和LDMFD/STMFD等锭部。
函數(shù)返回值
1.結(jié)果為一個(gè)32位的整數(shù)時(shí),可以通過寄存器R0返回驱闷。
2.結(jié)果為一個(gè)64位整數(shù)時(shí),可以通過R0和R1返回,依此類推空免。
3.結(jié)果為一個(gè)浮點(diǎn)數(shù)時(shí),可以通過浮點(diǎn)運(yùn)算部件的寄存器f0,d0或者s0來返回。
4.結(jié)果為一個(gè)復(fù)合的浮點(diǎn)數(shù)時(shí),可以通過寄存器f0-fN或者d0~dN來返回盆耽。
5.對于位數(shù)更多的結(jié)果,需要通過調(diào)用內(nèi)存來傳遞蹋砚。
4、反調(diào)試
Window反調(diào)試技術(shù)
應(yīng)用程序通過利用反調(diào)試技術(shù)可以進(jìn)行識(shí)別判斷自身是否被調(diào)試摄杂,當(dāng)識(shí)別到被調(diào)試就會(huì)改變執(zhí)行的流程或修改自身自銷毀坝咐。從而增加逆向調(diào)試的時(shí)間和復(fù)雜度。
反調(diào)試實(shí)現(xiàn)的技術(shù)
1.IsDebuggerPresent函數(shù)
實(shí)現(xiàn)原理:只能用于自身進(jìn)程的檢測析恢,通過查詢進(jìn)程環(huán)境塊(PEB)中的IsDebugged標(biāo)志墨坚,如果被調(diào)試狀態(tài)那么返回非0,調(diào)試沒有被調(diào)試狀態(tài)返回0
2.NeQueryInfomationProcess函數(shù)
原理:用于提取一個(gè)給定進(jìn)程的信息映挂,函數(shù)參數(shù)1表示進(jìn)程句柄泽篮,參數(shù)2表示信息類型,第二個(gè)參數(shù)ProcessDebugPort的值如果設(shè)置為0x7柑船,就可以進(jìn)行返回句柄標(biāo)識(shí)的進(jìn)程是否被調(diào)試帽撑,如果處于調(diào)試狀態(tài)那么就會(huì)返回調(diào)試的端口,非調(diào)試狀態(tài)則返回0鞍时。
3.CheckRemoteDebuggerPresent函數(shù)
實(shí)現(xiàn)原理:它可以用于自身進(jìn)程和其他進(jìn)程亏拉,通過查詢進(jìn)程環(huán)境塊(PEB)中的ISDebugged標(biāo)志,如果被調(diào)試狀態(tài)逆巍,那么返回值返回非0及塘,沒調(diào)試狀態(tài)返回0。
4.FindWindowA锐极、EnumWindows
實(shí)現(xiàn)原理:通過檢測運(yùn)行環(huán)境的調(diào)試器的窗口信息笙僚。
5.OutputDebigString函數(shù)
實(shí)現(xiàn)原理:調(diào)試器調(diào)試應(yīng)用程序的時(shí)候是通過觸發(fā)異常方式進(jìn)行執(zhí)行調(diào)試功能的,通過利用SetLastError函數(shù)設(shè)置錯(cuò)誤碼方式灵再,并用OutputDebugString函數(shù)進(jìn)行打印出來味咳,如果程序再被附加調(diào)試狀態(tài)那么GetLastError獲取到的錯(cuò)誤碼是前面用
SetLastError的錯(cuò)誤碼一致,如果沒有被調(diào)試檬嘀,那么錯(cuò)誤碼可能是任意值槽驶。
6.注冊表檢測
實(shí)現(xiàn)原理:通過查找調(diào)試器引用的注冊表信息進(jìn)行判斷,如果當(dāng)前環(huán)境下的注冊表有存在調(diào)試器的信息鸳兽,
下面是調(diào)試器在注冊表中的一個(gè)常用位置掂铐。
SOFTWARE\Microsoft\Windows NT\CurrentVersion\AeDebug(32位系統(tǒng))
SOFTWARE\Wow6432Node\Microsoft\WindowsNT\CurrentVersion\AeDebug(64位系統(tǒng))
該注冊表項(xiàng)指定當(dāng)應(yīng)用程序發(fā)生錯(cuò)誤時(shí),觸發(fā)哪一個(gè)調(diào)試器。默認(rèn)情況下全陨,它被設(shè)置為Dr.Watson爆班。如果該這冊表的鍵值被修改為OllyDbg(其他的調(diào)試器X64dbg、Windbg辱姨、ollyIce也一樣)柿菩,則應(yīng)用程序就可能確定它正在被調(diào)試。
7.BeginDebugged標(biāo)志檢測
實(shí)現(xiàn)原理:當(dāng)應(yīng)用程序運(yùn)行時(shí)候雨涛,fs:[30h]指向PEB基地址枢舶,如果指向的BeginDebugged標(biāo)志位0的情況下,那么應(yīng)用程序沒有被調(diào)試替久,反之被調(diào)試凉泄。
[圖片上傳失敗...(image-166512-1644822266788)]
8.檢測ProcessHead標(biāo)志
實(shí)現(xiàn)原理:在PEB結(jié)構(gòu)中的Reserved數(shù)組中有一個(gè)未公開的位置ProcessHeap,它位于PEB結(jié)構(gòu)的0x18處蚯根,ProcessHeap中包含F(xiàn)orceFlags標(biāo)志后众,可以通過該標(biāo)志進(jìn)行判斷是否處于調(diào)試狀態(tài)。
9.檢測NTGlobalFlag標(biāo)志
實(shí)現(xiàn)原理:通過調(diào)試器啟動(dòng)的進(jìn)程和正常創(chuàng)建啟動(dòng)的進(jìn)程是有差別的蒂誉,所以他們創(chuàng)建內(nèi)存推的方式也不一樣。NTGlobaFlag標(biāo)志它是微軟未公開的距帅,在PEB偏移0x68位置拗盒,如果值為0x70,那么表示程序是調(diào)試器啟動(dòng)的锥债。
[圖片上傳失敗...(image-bc305c-1644822266788)]
10.檢測父進(jìn)程是否是explorer.exe
實(shí)現(xiàn)原理:正常啟動(dòng)的應(yīng)用陡蝇,他的父進(jìn)程是explorer.exe,如果進(jìn)程被調(diào)試狀態(tài)那么它的父進(jìn)程就是調(diào)試器進(jìn)程哮肚。所以只要父進(jìn)程不是explorer.exe進(jìn)程就可以認(rèn)定為調(diào)試狀態(tài)登夫。
反調(diào)試檢測小結(jié)
以上的反調(diào)試技術(shù)是相對應(yīng)用比較多的技術(shù),對于反調(diào)試技術(shù)一般是通過多種方案結(jié)合的允趟。反調(diào)試技術(shù)除了以上的方案恼策,還有程序中執(zhí)行代碼段的校驗(yàn),運(yùn)行的調(diào)試器檢測潮剪,調(diào)試器的特征碼檢測涣楷,斷點(diǎn)檢測等等。反調(diào)試是逆向的第一個(gè)門檻抗碰,只有跨過這個(gè)門檻狮斗,逆向才能真正開始。
下面針對調(diào)試反調(diào)試的技術(shù)總結(jié)方案可以借鑒學(xué)習(xí)弧蝇。
參考鏈接:https://github.com/LordNoteworthy/al-khaser
java層檢測
1.isDebuggerConnected函數(shù)
檢測原理:當(dāng)app被調(diào)試的時(shí)候碳褒,調(diào)用android.os.Debug.isDebuggerConnected() 返回值為true折砸,否則返回值為false。
2.android:debuggable標(biāo)簽
檢測原理:Android中的AndroidManifest.xml文件中如果debuggable屬性值設(shè)置為true沙峻,那么久表示可以調(diào)試睦授,屬性值為false就表示不可調(diào)試。
3.包簽名校驗(yàn)
檢測原理:通過檢測app的包簽名信息摔寨,如果包簽名不一致那么就是重新打包的去枷。
native層檢測
1.檢測自身進(jìn)程的TracerPid值
檢測原理:可以通過/proc/pid/status或/proc/pid/task/pid/status獲取到TracerPid值,默認(rèn)下是0是复,被附加調(diào)試會(huì)變成調(diào)試的PID值删顶。
2.基于IDA調(diào)試器檢測
檢測原理:android_server端口號(hào)(通過/proc/net/tcp 檢測23946端口)、android_server文件信息佑笋、調(diào)試器進(jìn)程名、
3.檢測自身進(jìn)程maps
檢測原理:通過檢測/proc/pid/maps檢測自身進(jìn)程是否有保護(hù)敏感模塊信息來判斷是否被調(diào)試斑鼻。
4.檢測父進(jìn)程zygote進(jìn)程
檢測原理:因?yàn)閦ygote是所有程序的父進(jìn)程蒋纬,所有應(yīng)用app也是通過fork方式創(chuàng)建出來的,通過/proc/pid/cmdline獲取坚弱,如果當(dāng)前父進(jìn)程不是zygote進(jìn)程那么久表示被調(diào)試了蜀备。
5.檢測自身狀態(tài)
檢測原理: app在被附加調(diào)試過程中會(huì)被掛起暫停,所以通過/proc/pid/stat或/proc/pid/task/stat獲取荒叶,當(dāng)?shù)谌齻€(gè)字段屬性為t時(shí)候碾阁,那么表示app在被調(diào)試暫停掛起。
6.搶占Ptrace
檢測原理:在android系統(tǒng)中一個(gè)app只能被ptrace一次些楣。所以先自己ptrace自己脂凶。
android反調(diào)試小結(jié)
由于android系統(tǒng)是開源的,所以對于以上利用系統(tǒng)屬性及函數(shù)用于反調(diào)試的愁茁,都可以通過修改系統(tǒng)源碼然后重新編譯方式進(jìn)行過檢測蚕钦。對于這種修改系統(tǒng)方式其實(shí)可以通過自己ptrace自身方式,如果自身ptrace后鹅很,tracePid值還是為0嘶居,那么表示該系統(tǒng)是重新修改編譯過的。
攻防是個(gè)不斷持續(xù)的過程促煮,同樣反調(diào)試和過反調(diào)試也是如此邮屁。就在于點(diǎn)高點(diǎn)低了。以上羅列的只是簡單的一些方案菠齿。
5佑吝、加殼和脫殼
Window加殼方案
專門負(fù)責(zé)保護(hù)軟件被非法修改或編譯的程序。它附加在原始程序上绳匀,通過window加載器載入內(nèi)存后迹蛤,先于原始程序執(zhí)行民珍,以得到控制權(quán),在執(zhí)行過程中對原始程序進(jìn)行解密盗飒、還原嚷量,還原后把控制權(quán)還給原始程序,執(zhí)行原來代碼逆趣。
[圖片上傳失敗...(image-927905-1644822266788)]
它的作用:可以有效防止破解者對程序文件進(jìn)行非法修改蝶溶,也可以防止程序被靜態(tài)反編譯。不同外殼的側(cè)重方面是不一樣的宣渗,有的側(cè)重壓縮抖所,有的側(cè)重加密。例如壓縮殼的特點(diǎn)就是減少軟件的體積痕囱,加密保護(hù)就不是它的重點(diǎn)田轧。
常見殼的加載過程:
1.保存入口參數(shù)
2.獲取殼本身需要使用的API地址
3.解密原程序各個(gè)區(qū)塊的數(shù)據(jù)
4.IAT的初始化
5.重定位項(xiàng)的處理
6.Hook API
7.跳轉(zhuǎn)到程序原入口點(diǎn)(OEP)
殼的種類:
壓縮殼有:UPX、ASPack鞍恢、PECompact等等
UPX是開源:https://upx.github.io/
ASPack殼的官網(wǎng):http://www.aspack.com
加密殼:AsProtect傻粘、Armadillo、ExeCryptor等等
虛擬機(jī)殼:Themida帮掉、Winlicense弦悉、VMProtect等等
以上這些殼都可以用ExeInfo PE或PEID工具通過殼特征方式識(shí)別出什么類型的殼。
下圖中是PEID工具識(shí)別殼的特征碼Signs文件里面的特征蟆炊。
[圖片上傳失敗...(image-2f0e93-1644822266788)]
window脫殼方案
手動(dòng)脫殼的步驟:
1.查找真正的程序入口點(diǎn)
2.抓取內(nèi)存映像文件
3.重建PE文件
定位程序OEP的方法:
1.根據(jù)跨段指令進(jìn)行尋找OEP
[圖片上傳失敗...(image-1665e5-1644822266788)]
2.用內(nèi)存訪問斷點(diǎn)尋找OEP
方法:先打開內(nèi)存模塊(Alt+M),接著直接對代碼段(例如.text區(qū)段)進(jìn)行下內(nèi)存訪問斷點(diǎn)(F2)涩搓,這個(gè)斷點(diǎn)是一次性斷點(diǎn)污秆,當(dāng)所在段讀取或執(zhí)行時(shí)就中斷,中斷發(fā)生后昧甘,斷點(diǎn)將自動(dòng)刪除混狠。
3.根據(jù)棧平衡原理尋找OEP
在編寫加殼軟件時(shí),必須保證外殼初始化的各寄存器值和原程序的各寄存器值是相同的疾层。通常用pushad/popad,pushfd/popfd指令來保存和恢復(fù)現(xiàn)場環(huán)境将饺。
4.根據(jù)編譯語言特點(diǎn)尋找OEP
各類語言編譯的文件入口點(diǎn)都有自己的特點(diǎn)。使用同一種編譯器編譯的程序痛黎,其入口代碼都很類似予弧,都有一段啟動(dòng)代碼,編譯器在編譯程序時(shí)會(huì)自動(dòng)和程序連接湖饱。在完成必須的初始化工作后掖蛤,調(diào)用WinMain函數(shù)。
Android加殼方案
目前市面上加固產(chǎn)品種類:360加固井厌、愛加密加固蚓庭、梆梆加固致讥、騰訊樂固、網(wǎng)易易盾器赞、幾維加固垢袱、頂象加固。
一般App源程序加密之后就存放在那幾個(gè)目錄下港柜,一般是:dex文件尾部请契,libs目錄,assets目錄夏醉。
加固后可以通過app包里面的lib文件夾下的特有文件進(jìn)行識(shí)別加固廠商爽锥。
網(wǎng)易易盾:libnesec.so、libbugrpt.so
愛加密:libexec.so,libexecmain.so畔柔,ijiami.dat
梆梆:libsecexe.so,libsecmain.so , libDexHelper.so
阿里聚安全:aliprotect.dat氯夷,libsgmain.so,libsgsecuritybody.so
騰訊安全:libtosprotection.armeabi.so靶擦,libtosprotection.armeabi-v7a.so腮考,libtosprotection.x86.so
娜迦:libchaosvmp.so, libddog.so,libfdog.so
360:libprotectClass.so,libjiagu.so奢啥, libjiagu_art.so秸仙,libjiagu_x86.so
通付盾:libegis.so嘴拢,libNSaferOnly.so
網(wǎng)秦:libnqshield.so
百度:libbaiduprotect.so
騰訊:libshellx-2.10.6.0.so桩盲,libBugly.so,libtup.so, libexec.so席吴,libshell.so
APKProtect:libAPKProtect.so
幾維安全:libkwscmm.so, libkwscr.so, libkwslinker.so
加固的特點(diǎn)
[圖片上傳失敗...(image-9e38ce-1644822266787)]
Android脫殼方案
脫殼的主要流程:就是在App程序運(yùn)行起來后赌结,將源App的數(shù)據(jù)內(nèi)存釋放出來后,并將釋放出來的數(shù)據(jù)拷貝出來孝冒。
1.內(nèi)存dump法
通過利用frida框架結(jié)合脫殼腳本dex-dump
https://github.com/hluwa/FRIDA-DEXDump
通過在app內(nèi)存中暴力搜索 dex.035或者dex.036 柬姚。
通過讀取/proc/pid/maps查找后,進(jìn)行dump數(shù)據(jù)庄涡。
2.hook關(guān)鍵函數(shù)法
主要利用frida框架進(jìn)行腳本開發(fā)量承。
通過hook關(guān)鍵函數(shù)InMemoryDexClassLoader、dvmDexFileOpenPartial穴店、DexClassLoader,dexFileParse撕捍、memcmp然后去dump解密后的dex數(shù)據(jù)
3.動(dòng)態(tài)調(diào)試法
通過動(dòng)態(tài)調(diào)試app下mmap函數(shù)斷點(diǎn),然后去dump出源dex數(shù)據(jù)泣洞。
4.定制系統(tǒng)法
通過修改重編譯android系統(tǒng)并刷機(jī)忧风。
Hook Dalvik_dalvik_system_DexFile_defineClassNative。
枚舉所有DexClassDef球凰,對所有的class狮腿,調(diào)用dvmDefineClass進(jìn)行強(qiáng)制加載
5.dex2oat法
ART模式下腿宰,dex2oat生成oat時(shí),內(nèi)存中的DEX是完整的缘厢。
如何學(xué)習(xí)呢吃度?有沒有免費(fèi)資料?
今天免費(fèi)分享 免費(fèi)分享昧绣!
以上大型互聯(lián)網(wǎng)技術(shù)點(diǎn)學(xué)習(xí)資料獲取方式:點(diǎn)擊下方傳送門