Windows下反反調(diào)試技術(shù)匯總

本文轉(zhuǎn)載自:http://www.freebuf.com/articles/others-articles/181085.html

一咖驮、前言

對(duì)于安全研究人員來說,調(diào)試過程中經(jīng)常會(huì)碰到反調(diào)試技術(shù),原因很簡單:調(diào)試可以窺視程序的運(yùn)行“秘密”奶栖,而程序作者想要通過反調(diào)試手段隱藏他們的“秘密”,普通程序需要防止核心代碼被調(diào)試逆向贯底,惡意代碼需要隱藏自己的惡意行為防止被跟蹤伴奥。就像病毒和殺軟的關(guān)系一樣写烤,為了順利的逆向分析,有反調(diào)試手段就有對(duì)應(yīng)的破解方法-反反調(diào)試拾徙。對(duì)此洲炊,天融信阿爾法實(shí)驗(yàn)室研究人員總結(jié)了各類常見反調(diào)試手段以及對(duì)應(yīng)的繞過方案,作為總結(jié)希望與君共勉尼啡。

關(guān)于各種反調(diào)試手段暂衡,網(wǎng)絡(luò)上和各種安全書籍上都有對(duì)應(yīng)的介紹、各種調(diào)試工具插件已經(jīng)集成了反調(diào)試功能崖瞭,但有的只是介紹了反調(diào)試方法狂巢,并沒有對(duì)應(yīng)的破解方式,有的反調(diào)試手段已經(jīng)失效书聚。本文并不是研究新的反調(diào)試方法唧领,而是對(duì)windows平臺(tái)的反調(diào)試技術(shù)進(jìn)行分類總結(jié)均牢,并介紹其原理和應(yīng)對(duì)方法笨篷。

合理的分類有助于學(xué)習(xí)和理解各種反調(diào)試技術(shù)的原理,由于理解不同乓搬,分類也不盡相同西雀。當(dāng)你掌握了這些技術(shù)后萨驶,可以按照自己的理解重新給它們分類。本文按照靜態(tài)反調(diào)試方法與動(dòng)態(tài)反調(diào)試方法將反調(diào)試技術(shù)分為兩大類艇肴,其中靜態(tài)反調(diào)試技術(shù)的分類原理為:程序啟動(dòng)時(shí)腔呜,系統(tǒng)會(huì)根據(jù)正常運(yùn)行和調(diào)試運(yùn)行分配不同的進(jìn)程環(huán)境,通過檢測(cè)進(jìn)程環(huán)境來檢測(cè)進(jìn)程是否處于調(diào)試狀態(tài);根據(jù)逆向人員的工作環(huán)境和程序的正常運(yùn)行環(huán)境不同再悼,可以通過檢測(cè)調(diào)試器或逆向分析工具實(shí)現(xiàn)反調(diào)試核畴。動(dòng)態(tài)反調(diào)試技術(shù)的分類原理是:進(jìn)程運(yùn)行時(shí)的執(zhí)行流程是否正常、執(zhí)行狀態(tài)是否正常冲九。

本文測(cè)試環(huán)境:

測(cè)試代碼: https://github.com/alphaSeclab/anti-debug

調(diào)試器為原版OllyDbg1.1(http://www.ollydbg.de/odbg110.zip) 因?yàn)楦鞣N魔改帶插件的OD已經(jīng)自帶許多反反調(diào)試功能谤草,會(huì)影響測(cè)試效果跟束。

測(cè)試系統(tǒng)為win7 SP1 32位旗艦版

PE編輯器:LordPE

二、靜態(tài)反調(diào)試技術(shù)

許多靜態(tài)反調(diào)試技術(shù)對(duì)系統(tǒng)有較強(qiáng)的依賴丑孩,本文測(cè)試的靜態(tài)反調(diào)試技術(shù)時(shí)使用的是win 7 SP1 32位旗艦版冀宴。像PEB中ProcessHeap的Flags與ForceFlags等類似反調(diào)試手段針對(duì)的是NT5.X以下版本,win7 下檢測(cè)就會(huì)失效温学,所以本文未做介紹略贮。

2.1 進(jìn)程狀態(tài)檢測(cè)

我們不一樣:正常啟動(dòng)的進(jìn)程和調(diào)試啟動(dòng)的進(jìn)程的某些初始信息是不同的,比如進(jìn)程環(huán)境塊PEB仗岖、STARTUPINFO等逃延,這些信息使用方便,經(jīng)常被廣泛應(yīng)用于反調(diào)試技術(shù)轧拄。

2.1.1 BeingDebugged

PEB結(jié)構(gòu)體的內(nèi)容如下(部分省略):

上表中列出了Windows 7 32位SP1中PEB的結(jié)構(gòu)體成員揽祥,其中我們將會(huì)介紹到的與反調(diào)試相關(guān)的成員有:偏移為0×002的BeingDebugged和偏移為0×068的NtGlobalFlag。BeingDebugged是一個(gè)標(biāo)志位檩电,就像它的名字一樣拄丰,用來表示進(jìn)程是否處于被調(diào)試狀態(tài),NtGlobalFlag用來表示進(jìn)程的堆內(nèi)存特性是嗜,調(diào)試和非調(diào)試時(shí)其值不同愈案。所以我們只需要獲取這兩個(gè)值與正常狀態(tài)的值比較,就能知道進(jìn)程是否處于調(diào)試狀態(tài)鹅搪。

利用fs段寄存器指向的TEB結(jié)構(gòu)可以得到PEB結(jié)構(gòu)體的地址:

mov EAX,DWORD PTR FS:[0x30]

調(diào)試時(shí)該值為TRUE站绪,非調(diào)試時(shí)該值為FALSE。所以我們可以這樣判斷進(jìn)程是否被調(diào)試:

正常運(yùn)行時(shí)丽柿,該函數(shù)返回FALSE恢准,用OD打開調(diào)試運(yùn)行時(shí),該函數(shù)返回TRUE甫题。

其實(shí)有一個(gè)API可以直接獲取BeingDebugged的值:IsDebuggerPresent();我們看下這個(gè)API的實(shí)現(xiàn)代碼:在OD反匯編窗口按下快捷鍵CTRL+G馁筐,輸入函數(shù)名跳轉(zhuǎn)到函數(shù)地址

image.png
image.png

可以看到該函數(shù)其實(shí)也是讀取的PEB中的BeingDebugged標(biāo)志位的值。和我們的代碼相比坠非,它多了一行MOV EAX,DWORD PTRFS:[18]敏沉,這行代碼的作用是獲取TEB的地址,我們上面說了炎码,PEB的地址在TEB中盟迟,所以可以先獲取TEB的地址,再獲取PEB的地址潦闲。

破解方法:調(diào)試時(shí)手動(dòng)將其置0攒菠。

OD打開被調(diào)試程序,在內(nèi)存窗口CTRL+G歉闰,輸入FS:[30],內(nèi)存窗口將跳轉(zhuǎn)到PEB的地址:

image.png

選中偏移為2的位置CTRL+E辖众,將1改為0即可卓起。

2.1.2 NtGlobalFlags

當(dāng)進(jìn)程處于調(diào)試狀態(tài)時(shí),PEB結(jié)構(gòu)體偏移為0×68的NtGlobalFlag的值會(huì)被設(shè)置為0×70凹炸。所以和BeingDebugged成員檢查一樣戏阅,我們只需要檢查其值是否為0×70就能判斷程序是否被調(diào)試。

image.png

破解方法:和修改BeingDebugged的值一樣啤它,內(nèi)存窗口CTRL+G輸入fs:[30]跳轉(zhuǎn)到PEB的地址饲握,選中偏移0×68的地方,CTRL+E將其改為0即可蚕键。

image.png

2.1.3 NtQueryInformationProcess()

除了利用PEB查詢進(jìn)程相關(guān)的調(diào)試信息外,還可以利用NtQueryInformationProcess()API來獲取其他各種與進(jìn)程調(diào)試相關(guān)的信息衰粹。

image.png

其中第1個(gè)參數(shù)是要查詢的進(jìn)程的句柄值锣光,第2個(gè)參數(shù)是要查詢進(jìn)程的哪些信息,該參數(shù)是一個(gè)枚舉類型的值铝耻,可以查詢的信息如下:

image.png

第2個(gè)參數(shù)不同的值對(duì)應(yīng)于第3個(gè)參數(shù)指向不同類型的結(jié)構(gòu)體誊爹,比如第2個(gè)參數(shù)填0(ProcessBasicInformation),就表示你想獲取PEB的信息瓢捉,然后就可以判斷BeingDebugged频丘,NtGlobalFlag的值了,和我們前面講到的一樣泡态,但是這種實(shí)現(xiàn)方式稍顯復(fù)雜搂漠。

這節(jié)我們主要使用該函數(shù)查詢2個(gè)值:ProcessDebugPort、ProcessDebugObjectHandle某弦,對(duì)應(yīng)的枚舉值為7和0x1E桐汤。

進(jìn)程處于調(diào)試狀態(tài)時(shí),系統(tǒng)會(huì)為它分配一個(gè)調(diào)試端口靶壮,第2個(gè)參數(shù)傳7時(shí)怔毛,NtQueryInformationProcess()就能獲取調(diào)試端口的值,調(diào)試狀態(tài)該值為0xFFFFFFFF腾降,正常運(yùn)行時(shí)該值為0拣度。

同樣,有一個(gè)API也可以檢測(cè)調(diào)試端口的值:CheckRemoteDebuggerPresent()螃壤。CTRL+G定位到該函數(shù)實(shí)現(xiàn)部分抗果,發(fā)現(xiàn)該函數(shù)其實(shí)就是內(nèi)部調(diào)用了一下第2個(gè)參數(shù)為7的NtQueryInformationProcess:(實(shí)際調(diào)用的Zw版的函數(shù),功能相同)

image.png

進(jìn)程被調(diào)試時(shí)會(huì)生成調(diào)試對(duì)象映穗,當(dāng)函數(shù)的第2個(gè)參數(shù)值為0x1E(ProcessDebugObjectHandle)時(shí)就能獲取調(diào)試對(duì)象句柄窖张,正常運(yùn)行時(shí)該值為0,調(diào)試運(yùn)行時(shí)該值為非0值蚁滋。

image.png

破解方法:如果被調(diào)試程序只是調(diào)用幾次API宿接,而不是在多個(gè)地方反復(fù)調(diào)用赘淮,我們可以在調(diào)用的時(shí)候手動(dòng)修改特定參數(shù)下的調(diào)用結(jié)果:修改返回值或函數(shù)參數(shù)指向的內(nèi)存。像上面的例子一樣睦霎,我們可以在該函數(shù)調(diào)用結(jié)束的后手動(dòng)修改參數(shù)指向的內(nèi)存梢卸,修改調(diào)用結(jié)果。當(dāng)函數(shù)調(diào)用傳參為7/0x1E的時(shí)候副女,修改其第3個(gè)參數(shù)獲取的值:

image.png

在call處下斷蛤高,命中斷點(diǎn)后,棧區(qū)右鍵,定位到第3個(gè)參數(shù)的緩沖區(qū):

image.png

F8步過等函數(shù)調(diào)用完畢碑幅,修改第3個(gè)參數(shù)緩沖區(qū)內(nèi)的值

image.png

CTRL+E戴陡,修改為0。

如果該函數(shù)在多個(gè)地方被反復(fù)調(diào)用沟涨,那么我們可以采取API HOOK的技術(shù)恤批,修改函數(shù)內(nèi)部執(zhí)行代碼的流程。

OD重新運(yùn)行程序裹赴,反匯編區(qū)CTRL+G喜庞,輸入ZwQueryInformationProcess(實(shí)際調(diào)用的是該函數(shù)),跳轉(zhuǎn)到函數(shù)反匯編指令處棋返。

image.png

第1條指令正好5個(gè)字節(jié)延都,我們從此處HOOK。

ALT+M查看進(jìn)程的內(nèi)存映射表睛竣,找到其代碼區(qū)范圍:

image.png

代碼起始地址:0×1281000晰房,大小為0×5000。

回到反匯編窗口ALT+C:CTRL+G跳轉(zhuǎn)到0×1281000+0×4000的位置

image.png

這段空間是原程序代碼區(qū)對(duì)齊用的酵颁,沒有實(shí)際的代碼嫉你,我們?cè)谶@里實(shí)現(xiàn)我們的HOOK代碼,將ZwQueryInformationProcess的第1條指令修改為jmp 1285000躏惋,執(zhí)行HOOK代碼幽污,先判斷第2個(gè)參數(shù)是不是7或0x1E,是則把第3個(gè)參數(shù)指向的緩沖區(qū)填0,直接返回簿姨,不是則執(zhí)行ZwQueryInformationProcess的第1條指令mov eax,0xEA,然后跳轉(zhuǎn)到ZwQueryInformationProcess第2條指令處繼續(xù)執(zhí)行原函數(shù)功能距误。

image.png

ZwQueryInformationProcess函數(shù)的兩個(gè)MOV和后面的CALL RETN指令均可作為HOOK點(diǎn),這里選擇的第1條指令HOOK扁位,對(duì)于這種反反調(diào)試的手段准潭,程序可以檢查API的第1個(gè)指令是不是原指令來判斷是不是有HOOK代碼來繼續(xù)實(shí)現(xiàn)反調(diào)試,和動(dòng)態(tài)反調(diào)試的API斷點(diǎn)相似域仇,后面的動(dòng)態(tài)反調(diào)試會(huì)介紹怎么應(yīng)對(duì)HOOK檢測(cè)刑然。

2.1.4 STARTUPINFO

程序正常啟動(dòng)(雙擊啟動(dòng))時(shí),實(shí)際是資源瀏覽器通過CreateProcess()函數(shù)創(chuàng)建進(jìn)程啟動(dòng)的:

image.png

explorer啟動(dòng)程序時(shí)暇务,會(huì)把倒數(shù)第2個(gè)參數(shù)STARTUPINFO結(jié)構(gòu)體中的值設(shè)置為0泼掠,但調(diào)試器啟動(dòng)程序的時(shí)候不會(huì)怔软,所以我們可以通過判斷該結(jié)構(gòu)體中的某些值是否為0來判斷是否被調(diào)試:

image.png

這里我們選擇判斷結(jié)構(gòu)體中窗口坐標(biāo)和大小這幾個(gè)值(也可以多選幾個(gè))是否為0來實(shí)現(xiàn)反調(diào)試檢測(cè):

image.png

破解方法:同NtQueryInformationProcess一樣,當(dāng)函數(shù)調(diào)用次數(shù)較少時(shí)择镇,可以直接修改函數(shù)調(diào)用結(jié)束后的參數(shù)值:

image.png

函數(shù)調(diào)用處下斷挡逼,命中斷點(diǎn)后,數(shù)據(jù)區(qū)定位到第1個(gè)參數(shù)指向的結(jié)構(gòu)體地址腻豌,操作方法同2.1.3中修改NtQueryInfomationProcess調(diào)用結(jié)果相同家坎。F8步過,CTRL+E修改結(jié)構(gòu)體中對(duì)應(yīng)的值:

image.png

當(dāng)函數(shù)調(diào)用次數(shù)較多時(shí)-像NtQueryInformationProcess吝梅,我們采用的是API HOOK虱疏,那么這個(gè)函數(shù)能否使用API HOOK技術(shù)呢?我們只是修改該結(jié)構(gòu)體中的部分值苏携,而不是全部值订框,所以修改結(jié)構(gòu)體內(nèi)容要發(fā)生在函數(shù)執(zhí)行完畢,所以HOOK點(diǎn)要選擇函數(shù)將要返回時(shí)兜叨,一般這種情況是不容易HOOK的,反匯編區(qū)CTRL+G 輸入GetStartupInfoW,看下GetStartupInfoW的函數(shù)實(shí)現(xiàn)尾部:
image.png

尾部POP EBP和RETN 4共4個(gè)字節(jié)衩侥,不符合我們的要求国旷,而且返回前還有JNZ跳轉(zhuǎn)另一個(gè)返回分支,不是我們期望的HOOK點(diǎn)茫死。該函數(shù)實(shí)際需要填充參數(shù)指向的結(jié)構(gòu)體的值跪但,我們看下函數(shù)體中它填充的值來自哪里:

image.png

由匯編代碼實(shí)現(xiàn),我們可以發(fā)現(xiàn)峦萎,該函數(shù)實(shí)際是先定位到PEB中偏移為0×10的_RTL_USER_PROCESS_PARAMETERS結(jié)構(gòu)體(參考2.1.1中的PEB信息)地址處屡久,然后獲取參數(shù)STARTUPINFO的地址,分別將兩個(gè)地址值賦值給ECX和EAX爱榔,以這兩個(gè)寄存器為偏移基址被环,分別獲取_RTL_USER_PROCESS_PARAMETERS結(jié)構(gòu)體內(nèi)的值,并賦值給STARTUPINFO結(jié)構(gòu)體详幽。所以,我們只需要將結(jié)構(gòu)體_RTL_USER_PROCESS_PARAMETERS中的相應(yīng)值置0即可實(shí)現(xiàn)類似API HOOK的效果唇聘。_RTL_USER_PROCESS_PARAMETERS結(jié)構(gòu)體內(nèi)容如下:(win7 32位SP1)

image.png

STARTUPINFO的dwX版姑、dwY、dwXSize迟郎、dwYSize分別對(duì)應(yīng)該結(jié)構(gòu)體中偏移為0x4C剥险、0×50精刷、0×54睬棚、0×58處的值膨处,下面我們找到結(jié)構(gòu)體_RTL_USER_PROCESS_PARAMETERS在內(nèi)存中的位置:OD數(shù)據(jù)區(qū)CTRL+G輸入FS:[0x30]定位到PEB地址埋泵,找到偏移0×10處的_RTL_USER_PROCESS_PARAMETERS地址:
image.png

0x4E1298就是我們要找的地址,數(shù)據(jù)窗口CTRL+G跳轉(zhuǎn)到該地址處:
image.png

為方便我們找到那幾個(gè)偏移值夫凸,數(shù)據(jù)窗口右鍵把數(shù)據(jù)以地址方式顯示:
image.png
image.png

找到對(duì)應(yīng)的偏移處的值浑劳,CTRL+E將其置為0即可。我們這里只比較了4個(gè)值夭拌,你也可以把結(jié)構(gòu)體中其他的值置0魔熏。

2.1.5 SedebugPrivilege

一般程序正常啟動(dòng)時(shí)是不具備調(diào)試權(quán)限(SedebugPrivilege)的,除非自己有提權(quán)的需要主動(dòng)開啟鸽扁,但是調(diào)試器啟動(dòng)程序的時(shí)候蒜绽,由于調(diào)試器本身會(huì)開啟調(diào)試權(quán)限,所以被調(diào)試進(jìn)程會(huì)繼承調(diào)試權(quán)限桶现,因此我們可以通過檢查進(jìn)程是否具有調(diào)試權(quán)限來進(jìn)行反調(diào)試躲雅。

檢查進(jìn)程是否具有調(diào)試權(quán)限的方式很簡單,系統(tǒng)啟動(dòng)的時(shí)候會(huì)啟動(dòng)一個(gè)核心進(jìn)程csrss.exe骡和,我們可以通過判斷能否使用OpenProcess打開該進(jìn)程來檢查當(dāng)前進(jìn)程是否具有調(diào)試權(quán)限相赁,因?yàn)橹挥袚碛泄芾韱T權(quán)限+調(diào)試權(quán)限的進(jìn)程才能打開csrss.exe的句柄。嚴(yán)格來說這種檢查方法是不太嚴(yán)格的慰于,因?yàn)楫?dāng)進(jìn)程有調(diào)試權(quán)限無管理員權(quán)限的時(shí)候也不能打開csrss.exe的句柄钮科,幸運(yùn)的是,大多數(shù)調(diào)試器都會(huì)要求提供管理員權(quán)限婆赠,所以被調(diào)試程序也會(huì)同時(shí)擁有管理員權(quán)限+調(diào)試權(quán)限绵脯。

通過CsrGetProcessId函數(shù)可以獲取csrss.exe的PID,然后通過OpenProcess嘗試打開csrss.exe的句柄:

image.png

破解方法:在OpenProcess函數(shù)調(diào)用上或函數(shù)起始地址下斷休里,判斷PID的值是不是csrss.exe的PID蛆挫,注意:有兩個(gè)csrss.exe的進(jìn)程,通過任務(wù)管理器可以查看其PID的值妙黍,不要只關(guān)心一個(gè)PID悴侵。如果是這兩個(gè)中的一個(gè),直接執(zhí)行到函數(shù)返回拭嫁,并修改EAX的值為0即可(EAX就是函數(shù)的返回值):如下圖所示(0×228 == 552)
image.png
image.png
image.png

除了修改函數(shù)返回結(jié)果其實(shí)也可以直接修改查找的字符串畜挨。有的反調(diào)試程序會(huì)使用遍歷進(jìn)程的方式查找csrss.exe的PID值,但最終還是會(huì)調(diào)用OpenProcess,破解方式與上面相同噩凹,這里介紹另外一種破解方式:直接搜索“csrss.exe”字符串巴元,將其緩沖區(qū)直接置0即可,這樣它就找不到csrss.exe進(jìn)程了:

遍歷進(jìn)程查找csrss.exe:

image.png

破解方式:ALT+M轉(zhuǎn)到內(nèi)存窗口驮宴,右鍵search或快捷鍵ctrl+B:
image.png

在unicode欄輸入csrss(當(dāng)然也有可能是asc字符串逮刨,示例代碼用的是unicode)
image.png

成功找到csrss.exe的字符串地址,ctrl+L繼續(xù)找還有沒其他地址,發(fā)現(xiàn)只有這一處修己,CTRL+E將其置0即可恢总。
image.png

2.2 調(diào)試環(huán)境檢測(cè)

在2.1中介紹了各種進(jìn)程本身狀態(tài)檢測(cè)的反調(diào)試技術(shù),其原理是調(diào)試狀態(tài)下的進(jìn)程和正常運(yùn)行時(shí)進(jìn)程本身不同睬愤,這些檢查是固定不變的片仿,當(dāng)所有的檢查項(xiàng)都被我們偽裝通過了之后,這些反調(diào)試手段就失效了尤辱。本節(jié)將介紹針對(duì)調(diào)試環(huán)境檢測(cè)的反調(diào)試手段及反反調(diào)試手段砂豌。安全研究員要調(diào)試程序,他的工作環(huán)境和正常的工作環(huán)境是不同的光督,像各種分析工具阳距、虛擬機(jī)環(huán)境等等,這些工作環(huán)境的檢測(cè)就成為除進(jìn)程檢測(cè)外的另一種反調(diào)試手段结借。這時(shí)候不管進(jìn)程有沒有被調(diào)試,只要發(fā)現(xiàn)程序所處環(huán)境不正常,就認(rèn)為自己被調(diào)試分析了.

2.2.1 注冊(cè)表檢測(cè)

當(dāng)程序運(yùn)行發(fā)生錯(cuò)誤的時(shí)候筐摘,會(huì)有一個(gè)錯(cuò)誤彈框,讓用戶選擇關(guān)閉程序還是調(diào)試程序:
image.png

正常用戶只能選擇關(guān)閉程序船老,因?yàn)樗麤]有調(diào)試器咖熟,對(duì)于安全研究人員則不同,他們的工作環(huán)境上搭配有對(duì)應(yīng)的調(diào)試器柳畔,當(dāng)程序崩潰時(shí)球恤,他們可以選擇設(shè)置好的JIT調(diào)試器調(diào)試程序。以O(shè)D舉例荸镊,將OD設(shè)置為JIT調(diào)試器:Options->Just-in-time debugging:
image.png

一旦設(shè)置了JIT調(diào)試器,系統(tǒng)就會(huì)更改注冊(cè)表項(xiàng)中對(duì)應(yīng)JIT的值:
image.png

這個(gè)注冊(cè)表的位置為:

image.png

我們可以通過查找該注冊(cè)表項(xiàng)對(duì)應(yīng)的值是否包含常用調(diào)試器的字符串來檢測(cè)調(diào)試器:

image.png

破解方法:搜索字符串AeDebug,步驟和2.1.5相同堪置,只是這里會(huì)搜到兩個(gè)結(jié)果(一個(gè)32位路徑躬存,一個(gè)64位路徑),將其全部置0即可舀锨。
image.png

2.2.2 窗口檢測(cè)

窗口檢測(cè)即通過查找當(dāng)前系統(tǒng)中運(yùn)行的程序窗口名稱是否包含敏感程序來進(jìn)行反調(diào)試岭洲。常用的函數(shù)有FindWindow、EnumWindows坎匿。FindWindow可以查找符合指定類名或窗口名的窗口句柄盾剩,EnumWindows可以枚舉所有頂層窗口的句柄值,并傳給回調(diào)函數(shù)替蔬。

image.png

破解方法:搜索字符串OllyDbg或OLLYDBG(你使用的調(diào)試器的類名或窗口名)告私,將其置0即可。
image.png

2.2.3 父進(jìn)程檢測(cè)

程序正常啟動(dòng)(雙擊)時(shí)承桥,其父進(jìn)程為exeplorer.exe,調(diào)試啟動(dòng)時(shí)其父進(jìn)程為調(diào)試器驻粟,所以檢查其父進(jìn)程是否為explorer.exe也可以作為一個(gè)反調(diào)試手段。但是這種檢測(cè)方式容易誤判凶异,因?yàn)槌绦蛞部赡苁怯善渌_M(jìn)程通過CreateProcess正常啟動(dòng)蜀撑。NtQueryInformationProcess獲取當(dāng)前進(jìn)程的父進(jìn)程PID挤巡,通過遍歷進(jìn)程來獲取該P(yáng)ID對(duì)應(yīng)的進(jìn)程名是否explorer.exe來實(shí)現(xiàn)反調(diào)試。這里還有另一種選擇:獲取exeplorer.exe的PID和當(dāng)前進(jìn)程的父進(jìn)程PID比較酷麦,但是這種檢測(cè)遇到一種情況會(huì)失效-系統(tǒng)啟動(dòng)兩個(gè)explorer.exe,這時(shí)候會(huì)有兩個(gè)explorer的PID矿卑。所以示例代碼只演示比較進(jìn)程名而不是比較PID。

image.png

破解方法:除了字符串搜索法外沃饶,可以選擇把自己的調(diào)試器exe文件改成explorer.exe再運(yùn)行母廷,這樣檢測(cè)父進(jìn)程名稱時(shí)調(diào)試器的名稱也變成explorer.exe了,另一種方法就是修改_wcsicmp/_stricmp的比較結(jié)果绍坝,讓其返回0:
image.png

2.2.4 進(jìn)程掃描

進(jìn)程掃描是比父進(jìn)程檢測(cè)更暴力的檢測(cè)方式:只要系統(tǒng)進(jìn)程列表內(nèi)有敏感進(jìn)程徘意,就執(zhí)行反調(diào)試手段。

image.png

破解方法:最直接的還是更改調(diào)試器名稱轩褐,隨便換個(gè)名字即可椎咧,另一種方法就是查找字符串”O(jiān)llyDbg.exe”,將其置0即可把介。
image.png

2.2.5 內(nèi)核對(duì)象掃描

在2.1.3中我們介紹了查找調(diào)試對(duì)象句柄的方式實(shí)現(xiàn)反調(diào)試勤讽,其實(shí)也可以通過查找這個(gè)調(diào)試對(duì)象來實(shí)現(xiàn)反調(diào)試。當(dāng)調(diào)試會(huì)話建立時(shí)會(huì)創(chuàng)建一個(gè)“DebugObject”類型的內(nèi)核對(duì)象拗踢,通過遍歷內(nèi)核對(duì)象鏈表查找是否包含該類型的內(nèi)核對(duì)象即可實(shí)現(xiàn)反調(diào)試:

image.png

破解方法:查找字符串“DebugObject”,將其置0即可脚牍。
image.png

2.2.6 調(diào)試模式檢測(cè)

安全研究工作一般是在虛擬機(jī)環(huán)境中進(jìn)行的,當(dāng)進(jìn)行內(nèi)核調(diào)試時(shí)巢墅,虛擬機(jī)系統(tǒng)會(huì)處于被調(diào)試狀態(tài)诸狭,VirtualKD (http://virtualkd.sysprogs.org)可以輕松實(shí)現(xiàn)內(nèi)核調(diào)試。

image.png

當(dāng)系統(tǒng)處于被調(diào)試狀態(tài)時(shí)君纫,我們可以通過檢測(cè)該狀態(tài)來實(shí)現(xiàn)反調(diào)試:

image.png

破解方法:系統(tǒng)正常啟動(dòng)驯遇,不開啟調(diào)試模式,或開啟調(diào)試模式但是不建立內(nèi)核調(diào)試會(huì)話蓄髓。如果有內(nèi)核調(diào)試的需要叉庐,可以函數(shù)調(diào)用處下斷,修改函數(shù)執(zhí)行結(jié)果会喝,或HOOK API陡叠。

2.2.7 調(diào)試器攻擊

這里說的調(diào)試器攻擊并不是指對(duì)調(diào)試器造成破壞,而是指讓調(diào)試器無法調(diào)試程序肢执。比如ZwSetInformationThread()函數(shù)傳參ThreadHideFromDebugger時(shí)枉阵,如果程序正常運(yùn)行,那么該函數(shù)就相當(dāng)于什么都沒做预茄,但如果程序處于被調(diào)試狀態(tài)岭妖,那么該函數(shù)就可以使當(dāng)前線程(一般是主線程)脫離調(diào)試器,使調(diào)試器無法繼續(xù)接收該線程的調(diào)試事件,效果就像是調(diào)試器崩潰了一樣:

image.png

破解方式:API HOOK昵慌,當(dāng)ZwSetInformationThread第2個(gè)參數(shù)為17的時(shí)候假夺,函數(shù)直接返回。HOOK步驟參考2.1.3斋攀,HOOK代碼如下:

image.png

另一種調(diào)試器攻擊方式就是利用OD加載進(jìn)程時(shí)的一個(gè)BUG已卷,在PE文件格式中,數(shù)據(jù)目錄表項(xiàng)的個(gè)數(shù)一般是0×10個(gè)淳蔼,如果大于這個(gè)值侧蘸,雙擊運(yùn)行時(shí),系統(tǒng)加載器會(huì)忽略這個(gè)值鹉梨,但是OD加載進(jìn)程時(shí)會(huì)認(rèn)為PE文件格式錯(cuò)誤讳癌,拒絕加載。
image.png
image.png

其次存皂,雙擊運(yùn)行系統(tǒng)加載器加載進(jìn)程區(qū)段時(shí)晌坤,采用的是區(qū)段信息表中區(qū)段在虛擬內(nèi)存中的大小和區(qū)段在文件中大小的較小值,而OD則固定采用文件大小旦袋,如果把區(qū)段在文件中的大小值改的很大骤菠,則系統(tǒng)加載器可以正常運(yùn)行程序,而OD則會(huì)認(rèn)為PE文件格式錯(cuò)誤疤孕,拒絕加載商乎。
image.png
image.png

破解方法:把數(shù)據(jù)目錄表改回0×10,RSize改成VSize為0×200的整數(shù)倍即可祭阀。

三鹉戚、動(dòng)態(tài)反調(diào)試技術(shù)

在第二章中介紹了基于進(jìn)程狀態(tài)和運(yùn)行環(huán)境檢測(cè)的靜態(tài)反調(diào)試技術(shù),靜態(tài)反調(diào)試技術(shù)其實(shí)是對(duì)整個(gè)程序的保護(hù)专控,反調(diào)試手段相對(duì)“死板”抹凳,破解方式也比較簡單。其實(shí)程序開發(fā)者只是想對(duì)程序的關(guān)鍵算法和核心數(shù)據(jù)進(jìn)行保護(hù)踩官、避免逆向分析。所以他們大多利用的是動(dòng)態(tài)反調(diào)試手段:調(diào)試行為檢測(cè)境输。

動(dòng)態(tài)反調(diào)試的原理基于調(diào)試行為本身蔗牡,比如,調(diào)試運(yùn)行的程序比正常運(yùn)行的程序執(zhí)行速度慢嗅剖,所以可以用時(shí)鐘檢測(cè)來進(jìn)行反調(diào)試;遇到異常時(shí)辩越,調(diào)試器會(huì)先接收異常事件而不是直接傳遞給進(jìn)程本身,所以可以利用異常處理機(jī)制來實(shí)現(xiàn)反調(diào)試;逆向分析核心代碼時(shí)需要下斷點(diǎn)信粮、單步跟蹤等黔攒,可以檢測(cè)斷點(diǎn)、單步等實(shí)現(xiàn)反調(diào)試。

動(dòng)態(tài)反調(diào)試的主要目的是干擾調(diào)試器督惰,使其無法正常跟蹤程序的執(zhí)行流程或加大其逆向分析難度不傅。相比靜態(tài)反調(diào)試,其隱蔽性較強(qiáng)赏胚,技術(shù)難度更高访娶,破解難度也更大。

3.1 時(shí)鐘檢測(cè)

程序被調(diào)試時(shí)什么事都得向調(diào)試器通知報(bào)備一下觉阅,就像是調(diào)試器的提線木偶崖疤,調(diào)試器還要和用戶交互,等待用戶命令再下發(fā)給程序典勇,哪有完全自主來的爽劫哼。所以在調(diào)試器中跟蹤程序代碼比程序正常運(yùn)行耗費(fèi)的時(shí)間要多很多。時(shí)鐘檢測(cè)技術(shù)就是通過計(jì)算關(guān)鍵內(nèi)容運(yùn)行時(shí)間差異來判斷進(jìn)程是否處于被調(diào)試狀態(tài)割笙。同時(shí)程序在虛擬機(jī)中的運(yùn)行速度也比正常速度慢权烧,所以時(shí)鐘檢測(cè)技術(shù)一般也用于反虛擬機(jī)/反模擬器技術(shù)。計(jì)算運(yùn)行時(shí)差的方式一般有兩種:讀取CPU時(shí)鐘計(jì)數(shù)器咳蔚、時(shí)間計(jì)數(shù)相關(guān)API豪嚎。

3.1.1 CPU記數(shù)器

x86 CPU中存在一個(gè)名為TSC(Time Stamp Counter)的64位寄存器。CPU對(duì)每個(gè)時(shí)鐘周期計(jì)數(shù)谈火,然后保存到TSC侈询。RDTSC是一條匯編指令,用來將TSC的值讀入EDX:EAX寄存器(有的程序也會(huì)使用RDTSCP匯編指令糯耍,用法相同)扔字。

image.png

破解方法:F9運(yùn)行步過這段檢測(cè)代碼,如果有單步調(diào)試的需要?jiǎng)t修改RDTSC的執(zhí)行結(jié)果温技,把EAX,EDX置0或其他值革为,只要使兩次RDTSC結(jié)果相同即可。
image.png

3.1.2 時(shí)間API

時(shí)間計(jì)數(shù)相關(guān)的API有很多舵鳞,在時(shí)鐘檢測(cè)上RDTSC的效率和準(zhǔn)確度相比這些API是最高的震檩。但這些API也是一種反調(diào)試手段,這些API有:QueryPerformanceCounter蜓堕、GetTickCount抛虏、GetSystemTime、GetLocalTime等套才,具體參考MSDN(https://docs.microsoft.com/zh-cn/windows/desktop/SysInfo/time-functions)迂猴。

這些API的使用方式大同小異,這里只介紹QueryPerformanceCounter的使用:

image.png

破解方法:改變函數(shù)調(diào)用結(jié)果背伴,使兩次獲取時(shí)間相同沸毁,注意:一般這些時(shí)間API也會(huì)用于其他用途豆赏,所以除非明確知道所有的該API調(diào)用都是用來反調(diào)試冤荆,否則不要隨便HOOK。
image.png

3.2 異常處理

異常常用于動(dòng)態(tài)反調(diào)試技術(shù)。正常運(yùn)行的進(jìn)程發(fā)生異常時(shí)向抢,在SEH(Structured Exception Handling)機(jī)制的作用下转晰,OS會(huì)接收異常啊奄,然后調(diào)用進(jìn)程中注冊(cè)的SEH處理咕别。但是,若進(jìn)程正被調(diào)試器調(diào)試勒葱,那么調(diào)試器就會(huì)先于SEH接收處理浪汪。利用該特征可判斷進(jìn)程是正常運(yùn)行還是調(diào)試運(yùn)行,然后根據(jù)不同的結(jié)果執(zhí)行不同的操作凛虽,這就是利用異常處理機(jī)制不同的反調(diào)試原理死遭。

3.2.1 SEH

Windows中的一些典型異常如下:

https://msdn.microsoft.com/zh-tw/library/aa915076.aspx

image.png

以INT3異常EXCEPTION_BREAKPOINT為例,當(dāng)該異常觸發(fā)時(shí)凯旋,若程序處于正常運(yùn)行狀態(tài)呀潭,則自動(dòng)調(diào)用已經(jīng)注冊(cè)過的SEH;若程序處于調(diào)試運(yùn)行狀態(tài),則系統(tǒng)會(huì)停止運(yùn)行程序?qū)⒖刂茩?quán)轉(zhuǎn)給調(diào)試器至非。反調(diào)試程序一般會(huì)在SEH中更改程序的執(zhí)行流程钠署,若控制權(quán)交給調(diào)試器,而調(diào)試器又沒有執(zhí)行SEH代碼荒椭,程序流程就會(huì)走向未知谐鼎。
image.png

破解方法:OD調(diào)試選項(xiàng)中,設(shè)置忽略指定異常并傳遞給程序即可趣惠,意思就是這個(gè)異常不是OD設(shè)置的狸棍,是程序自己觸發(fā)的,OD不處理味悄,程序自己處理吧草戈。分析明白異常處理邏輯后,找到異常處理更改后(如果異常處理程序更改了)的EIP侍瑟,繼續(xù)分析唐片。
image.png

這里只是用INT3斷點(diǎn)舉例,要想主動(dòng)觸發(fā)其他類型的異痴茄眨可以使用RaiseException()函數(shù)费韭。這種反調(diào)試手段看似有點(diǎn)雞肋,其實(shí)它的主要目的是在異常處理中改變程序的執(zhí)行流程咐低,增加逆向分析難度揽思,除非把異常處理代碼分析明白袜腥,否則就搞不懂異常處理完畢程序的新EIP在哪见擦,從何處繼續(xù)分析钉汗。而且這種把忽略異常把異常交給程序自己處理的方法并不是萬能的,后面的INT 2D會(huì)“教OD做人”鲤屡。

3.2.2 SetUnhandledExceptionFilter()

主動(dòng)觸發(fā)的異乘鹛担可以被調(diào)試器交給程序自己處理,那么當(dāng)SEH不存在酒来,或SEH處理不了該異常的時(shí)候怎么辦呢卢未?這時(shí)候系統(tǒng)會(huì)調(diào)用UnhandledExceptionFilter()函數(shù),該函數(shù)會(huì)檢查進(jìn)程是否處于調(diào)試狀態(tài)堰汉,若是辽社,就把異常傳遞給調(diào)試器,否則就彈個(gè)錯(cuò)誤對(duì)話框翘鸭,然后結(jié)束程序:
image.png

SetUnhandledExceptionFilter()可以添加一個(gè)異常處理函數(shù)來替換彈框行為滴铅,沒有調(diào)試器的時(shí)候就會(huì)把異常傳遞給該異常處理函數(shù)去處理:
image.png

破解方法:首先像3.2.1一樣要讓OD把這些程序主動(dòng)觸發(fā)的異常忽略并交給程序處理,然后異常處理流程就走到UnhandledExceptionFilter就乓,因?yàn)閁nhandledExceptionFilter檢測(cè)到?jīng)]有調(diào)試器就會(huì)把異常交給SetUnhandledExceptionFilter注冊(cè)的函數(shù)汉匙,而它檢查有無調(diào)試器的方式就是2.1.3中提到的NtQueryInformationProcess函數(shù)第2個(gè)參數(shù)傳7(ProcessDebugPort),查找調(diào)試端口。后面的處理方式就和2.1.3中一樣生蚁,把NtQueryInformationProcess()函數(shù) HOOK掉即可噩翠。

3.2.3 INT 2D

INT 2D是一個(gè)特殊的指令,原為內(nèi)核模式中用來觸發(fā)斷點(diǎn)異常的邦投,也可以在用戶模式下正常運(yùn)行時(shí)觸發(fā)異常伤锚。但程序在OD中調(diào)試運(yùn)行時(shí)有以下特點(diǎn):

① 不會(huì)觸發(fā)異常,只是忽略

② INT 2D的下一條指令的第一個(gè)字節(jié)會(huì)被忽略尼摹。

③ F7/F8單步命令跟蹤INT 2D時(shí)见芹,程序不會(huì)停在下條指令開始的地方,而是一直運(yùn)行蠢涝,直到遇到斷點(diǎn)玄呛。

對(duì)于OD來說,忽略異常設(shè)置也沒用和二,因?yàn)镺D并沒有把INT 2D當(dāng)作異常徘铝,而是直接忽略。

image.png

破解方式:因?yàn)镺D直接忽略INT 2D異常惯吕,所以設(shè)置忽略異常傳遞給程序自身處理無效惕它,那么我們直接把INT 2D改變成OD不會(huì)忽略的異常即可,比如把它改成INT3異常废登。這樣OD就能把異常正常傳遞給異常處理程序了淹魄。

3.3 0xCC探測(cè)

在程序調(diào)試過程中,我們一般會(huì)設(shè)置許多軟件斷點(diǎn)堡距。軟件斷點(diǎn)的原理其實(shí)就是調(diào)試器在對(duì)應(yīng)設(shè)置斷點(diǎn)的位置上修改該地址的字節(jié)為0xCC甲锡,0xCC就是x86指令中的INT 3兆蕉。若是關(guān)鍵位置檢測(cè)到該指令,即可判斷進(jìn)程處于調(diào)試狀態(tài)缤沦。檢測(cè)時(shí)要注意不是所有的位置都可以虎韵,因?yàn)?xCC既可以是INT 3指令,也可以是其他指令的操作數(shù)缸废“叮基于這種原理的反調(diào)試技術(shù)稱為“0xCC探測(cè)技術(shù)”。

3.3.1 API斷點(diǎn)

在2.1.3中我們介紹了API HOOK企量,其中提到HOOK代碼可以通過檢查HOOK點(diǎn)是否原API指令來檢測(cè)HOOK實(shí)現(xiàn)反調(diào)試测萎。API斷點(diǎn)的檢測(cè)原理和檢測(cè)HOOK代碼相同,API斷點(diǎn)一般下在API的首地址處或函數(shù)返回地址處届巩。所以檢測(cè)這些易被下斷的地址首字節(jié)是否為0xCC即可判斷程序是否正在被調(diào)試绳泉。

image.png

破解方式:把斷點(diǎn)下在函數(shù)中間,或使用硬件斷點(diǎn)下斷姆泻。

3.3.2 校驗(yàn)和

為防止調(diào)試時(shí)把斷點(diǎn)下在函數(shù)中間零酪,反調(diào)試程序除探測(cè)0xCC外,通常采用比較特殊代碼區(qū)域(易被下斷點(diǎn)的區(qū)域)的校驗(yàn)和的值拇勃。這樣四苇,在該代碼區(qū)域內(nèi)調(diào)試時(shí),只要調(diào)試器在該區(qū)域內(nèi)設(shè)置一些0xCC斷點(diǎn)方咆,如此一來月腋,新的校驗(yàn)值就和原來的不一樣了,這樣就能判斷出進(jìn)程是否被調(diào)試了瓣赂。

image.png

破解方法:遇到校驗(yàn)和檢測(cè)的時(shí)候榆骚,首選F9運(yùn)行過被檢測(cè)代碼區(qū),需要單步分析的時(shí)候則使用F7單步煌集,需要步過使用硬件斷點(diǎn)步過妓肢。也可以通過下內(nèi)存訪問斷點(diǎn),找到計(jì)算校驗(yàn)和代碼的地方苫纤,改變比較跳轉(zhuǎn)碉钠。
image.png
image.png
image.png

3.4 硬件斷點(diǎn)檢測(cè)

在3.3中我們知道0xCC斷點(diǎn)容易被檢測(cè)到,這時(shí)需要硬件斷點(diǎn)來臨時(shí)過渡卷拘。硬件斷點(diǎn)的實(shí)現(xiàn)實(shí)際依賴于幾個(gè)調(diào)試寄存器:Dr0Dr7喊废。Dr0Dr3保存硬件斷點(diǎn)的地址,Dr4栗弟、Dr5保留污筷,Dr6、Dr7用于說明哪個(gè)硬件斷點(diǎn)觸發(fā)的相關(guān)屬性乍赫。所以同時(shí)最多能設(shè)置4個(gè)硬件斷點(diǎn)瓣蛀。只要檢查Dr0~Dr4這幾個(gè)寄存器的值是否為0就知道有沒有沒下硬件斷點(diǎn)斤寂、有幾個(gè)硬件斷點(diǎn)。查詢調(diào)試寄存器的方式一般有兩種:API直接查詢寄存器的值揪惦、主動(dòng)觸發(fā)異常查詢寄存器的值。

3.4.1 API直接查詢

直接API查詢寄存器的值:
image.png

破解方式:修改參數(shù)中context.ContextFlags的值罗侯,或HOOK GetThreadContext的參數(shù)值器腋,把context.ContextFlags的值改為CONTEXT_INTEGER(0×10002)值,不讓該函數(shù)查詢調(diào)試寄存器的值即可钩杰。
image.png

或HOOK GetThreadContext,HOOK API首地址:

image.png

注意纫塌,GetThreadContext會(huì)被系統(tǒng)函數(shù)調(diào)用,所以一般不要HOOK該函數(shù)讲弄,或判斷調(diào)用是否來自用戶代碼再?zèng)Q定是否執(zhí)行HOOK代碼措左。

3.4.2 異常間接查詢

主動(dòng)觸發(fā)異常檢查寄存器的值:
image.png

破解方法:這種反調(diào)試技術(shù)沒有直接調(diào)用API而是調(diào)用SEH根據(jù)異常CONTEXT查詢寄存器的值不容易被發(fā)現(xiàn),特別是這個(gè)異常處理函數(shù)需要OD主動(dòng)忽略異常才能傳遞給程序處理避除,這時(shí)候如果逆向分析人員沒有追蹤分析異常處理函數(shù)怎披,就很容易“被反調(diào)試”。這種反調(diào)試就只能跟蹤異常處理函數(shù)本身瓶摆,然后在查詢調(diào)試寄存器的地方改變其查詢比較結(jié)果即可凉逛。

3.5 單步檢測(cè)

逆向分析人員在分析關(guān)鍵代碼區(qū)的時(shí)候除了在API下斷,還需要不斷的單步分析群井,OD的F7單步步入和F8單步步過的原理就是設(shè)置單步異匙捶桑或0xCC斷點(diǎn)。反調(diào)試程序可以針對(duì)這一點(diǎn)設(shè)置陷阱书斜,檢測(cè)TF或0xCC實(shí)現(xiàn)反調(diào)試诬辈。

3.5.1 rep/call步過

當(dāng)遇到call指令或rep指令的時(shí)候,逆向人員經(jīng)常會(huì)采用F8步過的方式快速步過指令執(zhí)行序列荐吉。這時(shí)候焙糟,call指令或rep指令的下一條指令起始字節(jié)就會(huì)被設(shè)置一個(gè)軟件斷點(diǎn)(0xCC)。反調(diào)試程序可以在執(zhí)行call指令或rep指令的時(shí)候檢測(cè)下條指令是不是被下0xCC斷點(diǎn)或直接把下條指令改成NOP指令使0xCC斷點(diǎn)失效來實(shí)現(xiàn)反調(diào)試:

使0xCC斷點(diǎn)失效原理:
image.png

當(dāng)12F207B遇到rep或call習(xí)慣性F8步過的時(shí)候样屠,12F207D的0x8B就會(huì)變成0xCC(INT3)酬荞,這時(shí)rep指令或call指令會(huì)把12F207D重新恢復(fù)成原指令字節(jié)8B。這樣rep或call步過后瞧哟,本應(yīng)斷下的0xCC斷點(diǎn)并不會(huì)斷下混巧,調(diào)試器沒有重新得到控制權(quán),程序繼續(xù)執(zhí)行勤揩,又得重新分析咧党。當(dāng)然這種反調(diào)試手段麻煩的一點(diǎn)在于重新把原字節(jié)(0x8B)寫回去需要把代碼段屬性修改為可寫。不過這種反調(diào)試手段一般在惡意代碼中比較常見陨亡,而惡意代碼經(jīng)常申請(qǐng)堆內(nèi)存執(zhí)行惡意行為傍衡,而申請(qǐng)的堆內(nèi)存時(shí)的屬性若是可讀可寫可執(zhí)行的深员,就不存在這個(gè)麻煩。

檢測(cè)是否存在因調(diào)試步過而設(shè)置的0xCC斷點(diǎn):
image.png

這種檢測(cè)rep/call步過的0xCC斷點(diǎn)的反調(diào)試技術(shù)可以應(yīng)用在任何地方蛙埂。這里的例子只是為了說明其原理倦畅,實(shí)際應(yīng)用實(shí)可以有多種變化。

破解方法:碰到rep或call的時(shí)候要多注意esi/edi這些敏感寄存器實(shí)際指向的地址绣的,而且這種步過檢測(cè)會(huì)有讀取/寫入代碼區(qū)字節(jié)的行為叠赐,逆向分析遇到代碼區(qū)有讀取/寫入行為的時(shí)候要多使用內(nèi)存斷點(diǎn)和硬件斷點(diǎn),盡量不用軟件斷點(diǎn)屡江。

3.5.2 TF檢測(cè)

當(dāng)EFLAGS的TF標(biāo)志位被置1時(shí)芭概,CPU將進(jìn)入單步執(zhí)行模式。單步執(zhí)行模式中惩嘉,CPU執(zhí)行1條指令后即觸發(fā)一個(gè)EXCEPTION_SINGLE_STEP異常罢洲,然后TF會(huì)自動(dòng)清零。TF檢測(cè)一般有兩種和方式文黎,第1種:主動(dòng)觸發(fā)TF異常惹苗、與SEH結(jié)合使用探測(cè)調(diào)試器。
image.png

破解方法:這種先pushfd修改后再popfd的方式是最常見的TF標(biāo)志反調(diào)試手段耸峭,破解方法和3.2中異常處理一樣鸽粉,OD調(diào)試設(shè)置項(xiàng)就可以解決,不過需要仔細(xì)分析異常處理函數(shù)過程抓艳。
image.png

第二種檢測(cè)TF的方式相反触机,不主動(dòng)設(shè)置TF標(biāo)記位,檢測(cè)是否關(guān)鍵區(qū)有代碼被單步調(diào)試:檢測(cè)調(diào)試器是否在設(shè)置TF標(biāo)記位玷或。檢測(cè)原理是:
image.png

但前面已經(jīng)說過儡首,當(dāng)F7單步執(zhí)行pushfd時(shí),TF標(biāo)志位會(huì)被自動(dòng)清零偏友,所以后面的test結(jié)果永遠(yuǎn)為0蔬胯。這時(shí)候需要用到一條特殊的指令:pop ss。pop ss會(huì)將異常和中斷掛起位他,直到下一條指令執(zhí)行完畢:
image.png

單步執(zhí)行pop ss時(shí)氛濒,TF異常會(huì)被掛起,執(zhí)行完下一條指令才會(huì)處理TF異常鹅髓,所以程序不會(huì)停在pushfd這條指令處舞竿,而是停在test指令處。
image.png

破解方法:直接運(yùn)行跳過這段代碼窿冯,或修改test比較結(jié)果骗奖。
image.png

3.6自調(diào)試

同一個(gè)進(jìn)程不允許同時(shí)被兩個(gè)調(diào)試器調(diào)試,利用這一點(diǎn)可以自己先調(diào)試運(yùn)行自己,防止被另一個(gè)調(diào)試器繼續(xù)調(diào)試执桌。一般這種反調(diào)試程序會(huì)創(chuàng)建一個(gè)用于同步的內(nèi)核對(duì)象鄙皇,用第1次打開的進(jìn)程主動(dòng)第2次打開進(jìn)程,并調(diào)試第2個(gè)進(jìn)程仰挣。

3.6.1 CreateProcess

進(jìn)程第1次運(yùn)行時(shí)會(huì)嘗試訪問同步內(nèi)核對(duì)象伴逸,如果不存在,則說明當(dāng)前進(jìn)程第1次運(yùn)行膘壶,創(chuàng)建一個(gè)內(nèi)核對(duì)象错蝴,并以調(diào)試方式創(chuàng)建進(jìn)程打開“自己”。這時(shí)若調(diào)試器首次調(diào)試運(yùn)行進(jìn)程則相當(dāng)于在調(diào)試一個(gè)調(diào)試器香椎,由于第2次運(yùn)行的進(jìn)程是被第1次運(yùn)行的調(diào)試打開的,所以調(diào)試器也無法繼續(xù)調(diào)試第2次運(yùn)行的進(jìn)程禽篱。

image.png

破解方式:

1畜伐、 調(diào)試打開程序時(shí),修改OpenXXX等打開同步對(duì)象的API返回值躺率,使程序走正常流程玛界。

2、 主動(dòng)創(chuàng)建一個(gè)同名內(nèi)核對(duì)象CreateXXX悼吱,使OpenXXX等打開同步對(duì)象的操作成功慎框,走向正常流程。

這里只是簡單演示這種自調(diào)試的原理后添,在實(shí)際逆向中笨枯,被逆向程序可能是個(gè)加密、壓縮遇西、代碼被偷取等等不完整的進(jìn)程馅精,需要在第2次被自身調(diào)試打開時(shí)在調(diào)試循環(huán)中完成自修補(bǔ)。這樣的反調(diào)試才是難對(duì)付的粱檀,大大增加了逆向分析難度洲敢。需要逆向分析人員程序的調(diào)試循環(huán)進(jìn)行跟蹤分析,把修補(bǔ)后的完整程序從內(nèi)存中DUMP出來再逆向分析茄蚯。

3.6.2 DebugActiveProcess

自調(diào)試除上節(jié)講的CreateProcess()以調(diào)試方式打開進(jìn)程外压彭,還可以選擇正常創(chuàng)建自身,然后馬上附加創(chuàng)建進(jìn)程的操作來實(shí)現(xiàn)渗常。DebugActiveProcess()就可以做到這一點(diǎn)壮不。
image.png

破解方式:由于這時(shí)是先創(chuàng)建進(jìn)程再馬上附加的方式實(shí)現(xiàn)自調(diào)試,所以除3.5.1的破解方式外皱碘,我們還可以讓程序在創(chuàng)建自身進(jìn)程后直接退出忆畅,不讓其附加創(chuàng)建的進(jìn)程,由調(diào)試器去附加。而面臨的問題和3.5.1相同家凯,自調(diào)試只是個(gè)手段缓醋,自修補(bǔ)才是核心,不讓它自調(diào)試程序就不能補(bǔ)全自身绊诲,給逆向分析帶來麻煩送粱。

四、總結(jié)

通過本文總結(jié)的這些反調(diào)試技術(shù)可以看出掂之,靜態(tài)反調(diào)試技術(shù)偏于“大格局”抗俄,像進(jìn)程本身的一些標(biāo)志位查詢、調(diào)試環(huán)境檢查等世舰,動(dòng)態(tài)反調(diào)試偏于“小細(xì)節(jié)”动雹,像異常處理早龟、斷點(diǎn)檢測(cè)等凫海。這些技術(shù)如果只單純應(yīng)用其中的一種,其實(shí)是不難發(fā)現(xiàn)與破解的亩码,但如果他們綜合利用震蒋,互相隱蔽茸塞,那就大大增加了發(fā)現(xiàn)和破解的難度。這些反調(diào)試技術(shù)綜合來說還是“死的”查剖,弄明白原理就不難破解钾虐,所以現(xiàn)在的優(yōu)秀反調(diào)試技術(shù)已經(jīng)不局限于用這些“死板的”技術(shù)去調(diào)戲逆向分析人員了,他們開始使用像代碼混淆笋庄、花指令效扫、VMP等手段在精神上給逆向分析人員造成魔法傷害,而這種反調(diào)試手段的破解方式就是:老實(shí)逆向分析直砂,同樣的加花荡短、混淆、VMP保護(hù)手段分析出原理了哆键,以后就能增加魔抗掘托、快速破解反調(diào)試手段了。所以籍嘹,終級(jí)的反反調(diào)試手段還是增加自己的基本功-“他強(qiáng)任他強(qiáng) 清風(fēng)拂山崗”闪盔。

參考資料:

1.《逆向工程核心原理》

  1. The “Ultimate” Anti-debugging Reference

  2. 反調(diào)試技術(shù)總結(jié)

  3. An Anti-Reverse EngineeringGuide

  4. Anti Debugging ProtectionTechniques With Examples

  5. Anti-debugging Techniques CheatSheet

  6. OpenRCE

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市辱士,隨后出現(xiàn)的幾起案子泪掀,更是在濱河造成了極大的恐慌,老刑警劉巖颂碘,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件异赫,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)塔拳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門鼠证,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人靠抑,你說我怎么就攤上這事量九。” “怎么了颂碧?”我有些...
    開封第一講書人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵荠列,是天一觀的道長。 經(jīng)常有香客問我载城,道長肌似,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任诉瓦,我火速辦了婚禮川队,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘垦搬。我一直安慰自己呼寸,他們只是感情好艳汽,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開白布猴贰。 她就那樣靜靜地躺著,像睡著了一般河狐。 火紅的嫁衣襯著肌膚如雪米绕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,985評(píng)論 1 291
  • 那天馋艺,我揣著相機(jī)與錄音栅干,去河邊找鬼。 笑死捐祠,一個(gè)胖子當(dāng)著我的面吹牛碱鳞,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播踱蛀,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼窿给,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了率拒?” 一聲冷哼從身側(cè)響起崩泡,我...
    開封第一講書人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎猬膨,沒想到半個(gè)月后角撞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年谒所,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了热康。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡百炬,死狀恐怖褐隆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情剖踊,我是刑警寧澤庶弃,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站德澈,受9級(jí)特大地震影響歇攻,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜梆造,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一缴守、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧镇辉,春花似錦屡穗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至屹逛,卻和暖如春础废,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背罕模。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來泰國打工评腺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人淑掌。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓蒿讥,卻偏偏與公主長得像,于是被迫代替她去往敵國和親抛腕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子芋绸,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容