手把手教你彙編 Debug

程式設計師cxuan發表於2021-11-22

關於彙編的第一篇文章:
愛了愛了,這篇暫存器講的有點意思

Hello大家好,我是程式設計師cxuan!我們上篇文章瞭解了一下基本的暫存器,這篇文章我們來進行實際操作一下。

原文連結:手把手教你彙編 Debug

我們以後將會用到很多 Debug 命令,這裡我們先來熟悉一下它們。

Debug 是什麼

Debug 是 Windows / Dos 作業系統提供的一種功能。使用 Debug 能讓我們方便檢視 CPU 各種暫存器的值、記憶體情況,方便我們除錯指令、跟蹤程式的執行過程。

接下來我們會用到很多 debug 命令,但是使用這些命令的前提是,你需要在電腦上安裝一下 debug,Windows/Mac 都可以安裝,獲取連結我已經給你找出來了。阿,忘記說了,我們這裡使用的是 Dos box來模擬彙編的操作環境。

傳送門(Mac 和 Windows 都是):https://www.dosbox.com/download.php?main=1

image-20211017223618159

下載完成後開啟 DosBox ,開啟之後是這樣的。

image-20211017223818599

此時我們輸入 debug 命令應該提示的是

image-20211017223853097

因為我們還沒有進行連線和掛載,此時我們執行

mount c D:\debug

執行這條命令時,你需要現在 D 盤下建立一個 debug 資料夾,然後我們掛載到 debug 下面。

並且執行 C: 切換到 C 盤路徑下。

image-20211017224242053

此時我們就可以執行 debug 命令了。

image-20211017224458748

這裡需要注意一點,我在 Windows 10 系統下搭建 Debug 環境時,在掛載完成後輸入 debug ,還是提示 Illegal command:debug ,此時你需要再下載一個 debug.exe ,貼心的我也把下載地址給你了。

下載地址:https://pan.baidu.com/s/177arSA34plWqV-iyffWpEw#list/path=%2F 密碼:3akd

需要下載裡面的 debug.exe,然後把它放在你掛載的路徑下,這裡我掛載的路徑時 D 盤下的 debug 資料夾。

放置完成之後,再輸入 debug 就可以了。


因為每次開啟 Dosbox 都會執行上面這些命令,真的好煩,那怎麼辦呢?一個簡單的辦法是在 Dosbox 安裝路徑下找到

image-20211017225250971

開啟之後,在末尾鍵入

image-20211017225326436

就 OK 了,下次直接開啟 Dosbox ,會預設執行這三條命令,至此,就是我搭建 Dosbox 遇到的所有問題了。

Debug 實戰

玩兒彙編得學會用 Debug ,Debug 是一種除錯程式,通過 Debug 能讓我們能夠看到記憶體值,跟蹤堆疊情況,看到暫存器所暫存的內容等,同時也能夠更好地幫助我們理解彙編程式碼,所以學會 Debug ,非常重要,這是一種不可或缺的動手能力。

下面我們會用到幾種 Debug 命令,這裡先簡單介紹下。

image-20211117222634775

Debug 命令有很多,不過常用的一般就上面這幾個。

好了,現在我們直接進入正題,開始在 Dosbox 上正式進行 Debug 操作,首先開啟 Dosbox。

嗯。。。。。。這個介面我們開啟很多次了。

那我寫個命令呢? 好吧,沒演示過,下面就來了!

Debug -r

親,用 Debug -r 就可以檢視和修改 CPU 暫存器內容了呢。

image-20211117224711832

檢視暫存器內容。

image-20211117225112134

這裡需要注意一下 -r 大小寫的問題,Debug -r 是檢視暫存器內容。而 -R 則是無效指令。

上圖列出來了很多暫存器,你可能覺得無從下手,不要亂,我們先從最基本的開始入手,也就是 CS 和 IP,CS(Code Segment)是程式碼段暫存器,一般也被稱為段基址,可以認為是程式訪問的入口,CPU 需要從 CS 中找到從哪個位置開始取指執行,但是我們還不知道要取哪一段,這時候 IP 的作用就體現出來了,IP(Instruction Pointer)就是指令指標暫存器,也叫做偏移地址,它會告訴我們從段基址開始,取哪一段的地址。

可以使用段基址:偏移地址來確定記憶體中的指定地址。

這裡我們只是簡單聊一下這兩個暫存器的概念,要了解這兩個暫存器的具體作用,可以看筆者的上一篇文章

使用 -r 也能夠修改暫存器的內容,如下所示

image-20211119182455417

-r 一般的格式是 -r 暫存器,然後系統會進行冒號提示,後面就是你要修改的內容。

Debug -d

使用 -d 指令可以檢視記憶體中的內容。

image-20211119183448427

輸出的記憶體值預設是按照 CS:IP 的地址開始的,由於 CS 的值預設是 073F,而 IP 預設是 0100,所以 -d 的記憶體值是 073F:0100 。

-d 的格式很多,下面只介紹一下常用的幾種格式。

形似 -d 1000:0 這種 -d 段基址 偏移地址的格式可以產生如下輸出。

image-20211119184935344

如上圖所示,Debug 會列出指定記憶體單元中的的內容。上圖中的每一個 00 都表示 8 位,如果是 4A,那麼這八位展開來說就是 0010 1011 。每一行有 16 個 8 位,所以構成了 128 位記憶體地址。

為什麼都是 00 呢,因為記憶體單元的值沒有被改寫,說白了就是這塊記憶體區域沒有存值,如何改寫我們後面回收。

每一行的中間都有一個 -,這個是為了便於我們閱讀來設定的,- 號前後都有 8 個記憶體單元,這樣便於檢視。

右側幾個 ...... 表示每個記憶體單元可顯示的 ASCII 碼字元,因為記憶體沒有值,所以也沒有對應的 ASCII 碼。我們可以數一下,每行有 16 個 . ,這表示每一個 00 都對應了一個 ASCII 碼。

我們可以使用 -d 1000:9 這種 -d 段基址:起始偏移地址 格式來顯示從 1000 的第幾位開始。

image-20211119194232683

Debug 從 1000:9 開始,一直到 1000:88,一共是 128 個位元組,第一行中的 1000:0 ~ 1000:8 中的內容沒有顯示。

還可以使用 -d 1000:0 9 這種 -d 段基址:起始偏移地址 結尾偏移地址的格式來輸出。

image-20211119194856418

還可以是使用 -d 偏移地址來在不指定段基址的情況下,檢視記憶體值。

image-20211119201832383

Debug -e

上面說的都是檢視記憶體中指定位置或者區域的值,下面我們要來改寫一下記憶體值。

使用 -e 可以改寫記憶體值,比如我們想要改寫 1000:0 ~ 1000:f 中的內容,可以使用 -e 1000:0 0 1 2 3 4 5 6 7 8 9 0 a b c d e f 這種方式,如下圖所示。

image-20211119201236510

這裡需要注意下,在進行 -e 改寫的時候,每個值中間都有一個空格,如果沒有空格的話,會當做一個記憶體值來看待。

然後用 -d 1000:0 看到我們剛改寫的記憶體值。

還可以使用提問的方式來逐個修改從某一地址開始的記憶體單元的內容。

還是用 1000:100 來舉例子,輸出 -e 1000:100 後按下Enter鍵。

image-20211119205201568

如上圖所示,可以看到我們先輸入了一次 -e 1000:100 這個指令,然後按下了Enter鍵。

注意,如果這裡你按下了Enter鍵,就相當於整個 -e 改寫的過程已經完成。

如果你想要繼續改寫後面記憶體中的值,你需要按下空格鍵。

我們改寫了 1000:100 之後的記憶體值,然後使用 -d 1000:100 檢視我們改寫的內容是否生效。

-e 命令還可以支援寫入字元,比如我們可以向 1000:0 這個位置開始寫入數值和字元,-e 1000:0 1 'a' 2 'b' e 'c' 。

image-20211119210708341

如上圖所示,當我們向記憶體寫入字元 'a' 'b' 'c' 的時候,會自動轉換為 ASCII 碼進行儲存,在最右側可以找到剛剛寫入的字元。

Debug -u

如何向記憶體中寫入一段機器碼呢?比如我們想要在記憶體中寫入一段機器碼。

image-20211119220152007

我們可以使用 -e 來進行寫入,向記憶體中寫入 b8 01 00 b9 02 00 01 c8 這個機器碼,如下所示

image-20211119224014093

我們使用 -e 寫入之後,使用 -d 檢視記憶體值,可以發現我們剛剛寫入的值,但是卻看不到機器碼,所以機器碼該如何看呢?

別急,還有個 -u 命令,這個就是看機器碼的,如下圖所示,我們使用 -u 命令顯示我們寫入的機器碼。

image-20211119224851565

可以看到 1000:0000 ~ 1000:0006 這個記憶體地址使我們寫入的機器碼,-u 這個命令就是將記憶體單元的內容翻譯為彙編指令並顯示。

-u 輸出的結果分為三部分顯示:

  • 最左側是每一條機器指令的地址;
  • 中間是機器指令;
  • 最右側是機器指令執行的彙編指令。

1000:0 處存放的是寫入的機器碼 B8 01 00 組成的機器指令,對應的彙編指令是 MOV AX,0001。

1000:0003 處存放的是寫入的機器碼 B9 02 00 組成的機器指令,對應的彙編指令是 MOV CX,0002。

1000:0006 處存放的是寫入的機器碼 C1 C8 所組成的機器指令,對應的彙編指令是 add ax,cx。

Debug -t

上面介紹的一系列指令包括我們上面提到的 Debug -e 機器碼都是向記憶體中進行寫入,那麼如何執行這些指令呢?

我們可以使用 Debug -t 來執行寫入的指令。使用 Debug -t 可以執行由 CS:IP 指向的指令。

既然是 -t 能夠執行從 CS:IP 指向的命令,所以我們有必要將 CS:IP 指向 1000:0(因為我們前面將指令寫在了 1000:0 處)。

首先我們需要執行 -r cs 1000 ,-r ip 0 把 CS:IP 賦值為 1000:0。

然後執行 -t 指令,下圖是已經執行過的指令截圖。

image-20211120060826534

可以看到,執行完 -t 指令之後,MOV AX,0001 這條指令被執行,當前 AX 暫存器的內容變為了 0001,這條彙編指令的意思就是把 0001 移動到 AX 暫存器中。

繼續執行 -t 之後,我們可以看到暫存器的變化。

image-20211120061105506

Debug -a

畢竟機器指令不是那麼好懂,寫入很不方便,所以有沒有辦法能夠支援我們直接寫入彙編指令呢?還真有,Debug 提供了 -a 這種方式來實現彙編指令的寫入。如下圖所示

image-20211120230320647

可以看到,我們使用了 -a 命令來對 1000:0 進行寫入,分別輸入 mov ax,1 mov bx,2 mov cx,3 add ax,bx add ax,cx add ax,ax 指令,然後按回車進行確定執行。

我們使用 -d 1000:0 f 可以看到從偏移地址 0 處開始的第 f 個記憶體指令(因為最大寫入的地址只是 f)。

image-20211120231016386

上圖中的 1000:000F 為什麼有值呢,因為我們上面已經執行過這個寫入了。

另外,使用 -a 可以從一個預設的地址處開始輸入指令。

總結

今天和大家聊了一下 Debug 的基本用法,主要包括

  • -r 檢視、修改暫存器中的內容
  • -d 檢視記憶體中的指令
  • -e 修改記憶體中的內容
  • -u 可以將記憶體中的內容解釋為機器指令和對應的彙編指令
  • -t 執行 CS:IP 處的指令
  • -a 以彙編得形式向記憶體寫入內容

彙編指令的選項有很多,上面介紹的這些屬於經常用到的指令,這些指令要能夠熟練使用。

最後給大家推薦一下我自己的Github ,裡面有非常多的硬核文章,絕對會對你有幫助。

相關文章