日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

程序调试器原理

發(fā)布時間:2023/12/20 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 程序调试器原理 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

調(diào)試器原理:

?

?? 調(diào)試器是一個程序,在開發(fā)工具中也是調(diào)用一個程序,在運(yùn)行時就是一個進(jìn)程,這個進(jìn)程與普通進(jìn)程沒有區(qū)別,只是這個進(jìn)程調(diào)用了內(nèi)核的一些特殊函數(shù)(系統(tǒng)調(diào)用)來操縱內(nèi)核數(shù)據(jù),這些數(shù)據(jù)就是被調(diào)試進(jìn)程的內(nèi)存數(shù)據(jù)。

?

而對操作系統(tǒng)調(diào)試的調(diào)試器則不同,因為沒有操作系統(tǒng)的支持,調(diào)試器本身就不再需要調(diào)用操作系統(tǒng)內(nèi)核的程序來支持,但是此時的調(diào)試非常特殊,因為操作系統(tǒng)自己有中斷處理程序,調(diào)試器對中斷服務(wù)程序的劫持會讓操作系統(tǒng)的操作變得有些不同,調(diào)試器首先啟動先設(shè)置CPU為單步執(zhí)行狀態(tài),然后啟動操作系統(tǒng)的代碼,每執(zhí)行一條指令,CPU均產(chǎn)生中斷進(jìn)入調(diào)試器程序(中斷服務(wù)程序)。

通常內(nèi)核調(diào)試緊緊是通過輸出信息(如printk)來調(diào)試,這其實已經(jīng)不是調(diào)試,而是嵌入代碼進(jìn)行測試,即使是kgdb也是在內(nèi)核中插入了代碼來實現(xiàn)調(diào)試,如讓windows運(yùn)行時啟動調(diào)試模式,那么其內(nèi)核中的調(diào)試代碼就起作用了,這些調(diào)試是內(nèi)核設(shè)計者預(yù)先設(shè)計好的。

?

在操作系統(tǒng)下調(diào)試程序,只是調(diào)試用戶態(tài)代碼,而用戶態(tài)代碼是出于操作系統(tǒng)控制之下的,因此調(diào)試就是借助操作系統(tǒng)來操作目標(biāo)進(jìn)程。

? 無論windows還是unix中,都是通過對系統(tǒng)調(diào)用的劫持來實現(xiàn)對目標(biāo)程序的調(diào)試的,如果操作系統(tǒng)內(nèi)核不實現(xiàn)對程序的調(diào)試功能,那么調(diào)試器是不可能實現(xiàn)對另一個進(jìn)程控制的。調(diào)試器通過創(chuàng)建子進(jìn)程,并告訴操作系統(tǒng)(創(chuàng)建進(jìn)程的狀態(tài)設(shè)置)自己要對子進(jìn)程進(jìn)行調(diào)試,那么操作系統(tǒng)裝載目標(biāo)程序時,如果發(fā)生了規(guī)定的事件,就會停止目標(biāo)進(jìn)程的執(zhí)行,此時對于目標(biāo)進(jìn)程來說,根本不知道操作系統(tǒng)為什么將自己停了,因為目標(biāo)進(jìn)程此時正在處于系統(tǒng)調(diào)用中,如線程創(chuàng)建、退出、發(fā)生異常等,而這時的內(nèi)核代碼就會檢查當(dāng)前進(jìn)程是否處于調(diào)試狀態(tài),如果是,那么就啟動調(diào)試器進(jìn)程,這里所說的事件的發(fā)生是由操作系統(tǒng)內(nèi)核實現(xiàn)的,基本是與內(nèi)核的進(jìn)程(線程)操作有關(guān)。

當(dāng)目標(biāo)進(jìn)程中斷后,調(diào)試器也必須通過內(nèi)核才能實現(xiàn)讀取目標(biāo)進(jìn)程的內(nèi)存,如果不是這樣,進(jìn)程間可以互相讀寫進(jìn)程就會讓操作系統(tǒng)的內(nèi)存保護(hù)功能失效了。

內(nèi)核對于目標(biāo)進(jìn)程的內(nèi)存讀寫是相當(dāng)簡單的,就是找到目標(biāo)進(jìn)程的頁表,遍歷頁表找到虛擬內(nèi)存對應(yīng)的實際內(nèi)存地址(物理地址),然后讀寫這個內(nèi)存,對目標(biāo)進(jìn)程的寄存器也是一樣,內(nèi)核很容易獲得目標(biāo)進(jìn)程的進(jìn)程塊,其中保存了目標(biāo)進(jìn)程的所有寄存器值。

如果沒有對目標(biāo)進(jìn)程啟動的事件中斷,那么目標(biāo)進(jìn)程就會一直執(zhí)行,不會受調(diào)試器的控制。正因為內(nèi)核實現(xiàn)了目標(biāo)進(jìn)程一旦創(chuàng)建后就會讓其中斷,然后等待調(diào)試器進(jìn)程的指令。

調(diào)試器在目標(biāo)進(jìn)程剛準(zhǔn)備好就獲得了目標(biāo)進(jìn)程的控制權(quán),然后如果調(diào)試器直接run目標(biāo)進(jìn)程,那么目標(biāo)進(jìn)程就會處于失控狀態(tài),只有當(dāng)系統(tǒng)事件(如線程創(chuàng)建、退出、DLL裝卸載等)發(fā)生時內(nèi)核才會中斷目標(biāo)進(jìn)程,啟動調(diào)試器。

因此調(diào)試時,通常在一開始就要實現(xiàn)對目標(biāo)進(jìn)程的單步調(diào)試或在目標(biāo)進(jìn)程中實現(xiàn)斷點,這樣目標(biāo)進(jìn)程才會頻繁中斷或在目標(biāo)位置中斷,然后交給調(diào)試器處理。

設(shè)置單步調(diào)試比較簡單,設(shè)置CPU的狀態(tài)寄存器的TF位,那么CPU每執(zhí)行一個指令就會產(chǎn)生中斷,當(dāng)然如果目標(biāo)程序是執(zhí)行系統(tǒng)調(diào)用,那么內(nèi)核會清除這個狀態(tài)位,因此一旦進(jìn)入內(nèi)核,CPU就不是單步執(zhí)行了,而是只有等系統(tǒng)調(diào)用返回才能執(zhí)行下一步調(diào)試。

?

單步調(diào)試可以讓我們實現(xiàn)很多功能,如果有源代碼,那么我們執(zhí)行源代碼調(diào)試時,程序會編譯帶上調(diào)試信息,而在其中會構(gòu)建符號表,行號等信息,每條指令會對應(yīng)源程序行號,目標(biāo)程序每執(zhí)行一個指令,就產(chǎn)生中斷,而內(nèi)核中斷服務(wù)程序就調(diào)用調(diào)試器來處理,調(diào)試器啟動后,當(dāng)然還是通過系統(tǒng)調(diào)用來讀寫目標(biāo)程序的內(nèi)存,當(dāng)然首先還是判斷當(dāng)前執(zhí)行的指令是屬于源程序的哪行產(chǎn)生的代碼,因此調(diào)試器會維護(hù)很多關(guān)于源程序與目標(biāo)代碼之間的關(guān)聯(lián)信息。這需要編譯器的支持,經(jīng)過優(yōu)化后,源代碼與目標(biāo)代碼之間的對應(yīng)關(guān)系有時也許并不那么明顯。

?

當(dāng)然要設(shè)置斷點調(diào)試,就不是這么單步執(zhí)行,其實在源代碼調(diào)試時,也可以通過設(shè)置斷點來實現(xiàn)調(diào)試,調(diào)試器也通常這樣做。 調(diào)試器通過修改斷點地址的指令代碼來實現(xiàn)中斷,修改為中斷指令后,目標(biāo)進(jìn)程一執(zhí)行到這里就會產(chǎn)生中斷(異常),然后操作系統(tǒng)就會啟動調(diào)試器來接管。因此必須在以上提到的系統(tǒng)事件發(fā)生時,調(diào)試器才能設(shè)置斷點,例如在進(jìn)程啟動時刻,調(diào)試器會獲得控制權(quán),此時就可以通過修改目標(biāo)進(jìn)程的內(nèi)存來實現(xiàn)斷點。而設(shè)置斷點時,當(dāng)然調(diào)試器會將斷點處的內(nèi)存保存下來,一次可以設(shè)置多個斷點,那么調(diào)試器必然會保存地址與內(nèi)存值對應(yīng)的表。當(dāng)斷點中斷發(fā)生時,調(diào)試器等待用戶指令操作完,進(jìn)行run操作時,調(diào)試器就通過重新寫回目標(biāo)斷點處的指令,目標(biāo)程序接著執(zhí)行。

當(dāng)然要設(shè)置斷點,就必須對目標(biāo)程序非常了解,否則,你設(shè)置斷點時,修改的是一個指令的部分(產(chǎn)生非法指令異常),或者是一個數(shù)據(jù)(沒有反應(yīng))。設(shè)置斷點是有目標(biāo)的,首先我們要遍歷目標(biāo)程序的內(nèi)存,或者通過反編譯找到要調(diào)試的目標(biāo)代碼,然后通過對可執(zhí)行文件的分析,找到代碼加載到內(nèi)存的位置,這樣我們才可以設(shè)置斷點。

當(dāng)然對于源代碼級別的斷點設(shè)置,因為有源代碼和調(diào)試信息的支持,比較容易找到源代碼與加載到內(nèi)存的指令地址的關(guān)系,設(shè)置斷點就比較容易。因此有源代碼的調(diào)試就比直接的內(nèi)存進(jìn)程調(diào)試相對簡單。

調(diào)試器中,經(jīng)常要用到符號表,符號表是一個調(diào)試器用來解釋目標(biāo)內(nèi)存數(shù)據(jù)意義的數(shù)據(jù)表,如獲得一個目標(biāo)進(jìn)程內(nèi)存地址的數(shù)據(jù)后,如果沒有符號表,那么我們只能看到一堆二進(jìn)制數(shù)據(jù),如果用符號表解釋出來,那么就會知道內(nèi)存中保存了什么。

調(diào)試器可以將內(nèi)存轉(zhuǎn)換成某個對象,按照某個結(jié)構(gòu)體解析數(shù)據(jù),然后我們就可以發(fā)現(xiàn)每個對象成員的值。前提是我們知道這個位置保存了某個類型的對象,如果實際上不是,那么解析結(jié)果就是無意義的。

還有一種符號表,假定對象在內(nèi)存中的位置是不變的,那么我們可以通過地址中的數(shù)據(jù)就可以構(gòu)建起對象。

對于內(nèi)核調(diào)試中,我們通常通過結(jié)構(gòu)體(符號表)解析某個地址內(nèi)容的意義。當(dāng)然調(diào)試器通常沒有對象的識別能力,但是完全可以設(shè)計一個調(diào)試器,可以根據(jù)預(yù)先定義的對象網(wǎng)絡(luò)(或樹)來構(gòu)建起對目標(biāo)對象內(nèi)存的解釋。通常在內(nèi)核調(diào)試時,我們也是通過獲得一個核心對象后,然后根據(jù)其中的地址來獲得其引用的其他對象,當(dāng)然前提是調(diào)試者要非常清楚內(nèi)存中對象的實際關(guān)系。

總結(jié)

以上是生活随笔為你收集整理的程序调试器原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。