国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

使用 GDB 查看程序的??臻g

開發 后端
這是一個快速教程,介紹如何使用 GDB 查看 C 程序的??臻g。我認為這對于 Rust 程序來說也是相似的。但我這里仍然使用 C 語言,因為我發現用它更簡單,而且用 C 語言也更容易寫出錯誤的程序。

[[409296]]

昨天我和一些人在閑聊的時候,他們說他們并不真正了解棧是如何工作的,而且也不知道如何去查看??臻g。

這是一個快速教程,介紹如何使用 GDB 查看 C 程序的??臻g。我認為這對于 Rust 程序來說也是相似的。但我這里仍然使用 C 語言,因為我發現用它更簡單,而且用 C 語言也更容易寫出錯誤的程序。

我們的測試程序

這里是一個簡單的 C 程序,聲明了一些變量,從標準輸入讀取兩個字符串。一個字符串在堆上,另一個字符串在棧上。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3.  
  4. int main() {
  5. char stack_string[10] = "stack";
  6. int x = 10;
  7. char *heap_string;
  8.  
  9. heap_string = malloc(50);
  10.  
  11. printf("Enter a string for the stack: ");
  12. gets(stack_string);
  13. printf("Enter a string for the heap: ");
  14. gets(heap_string);
  15. printf("Stack string is: %s\n", stack_string);
  16. printf("Heap string is: %s\n", heap_string);
  17. printf("x is: %d\n", x);
  18. }

這個程序使用了一個你可能從來不會使用的極為不安全的函數 gets 。但我是故意這樣寫的。當出現錯誤的時候,你就知道是為什么了。

第 0 步:編譯這個程序

我們使用 gcc -g -O0 test.c -o test 命令來編譯這個程序。

-g 選項會在編譯程序中將調式信息也編譯進去。這將會使我們查看我們的變量更加容易。

-O0 選項告訴 gcc 不要進行優化,我要確保我們的 x 變量不會被優化掉。

第一步:啟動 GDB

像這樣啟動 GDB:

  1. $ gdb ./test

它打印出一些 GPL 信息,然后給出一個提示符。讓我們在 main 函數這里設置一個斷點:

  1. (gdb) b main

然后我們就可以運行程序:

  1. (gdb) b main
  2. Starting program: /home/bork/work/homepage/test
  3. Breakpoint 1, 0x000055555555516d in main ()
  4.  
  5. (gdb) run
  6. Starting program: /home/bork/work/homepage/test
  7.  
  8. Breakpoint 1, main () at test.c:4
  9. 4 int main() {

好了,現在程序已經運行起來了。我們就可以開始查看棧空間了。

第二步:查看我們變量的地址

讓我們從了解我們的變量開始。它們每個都在內存中有一個地址,我們可以像這樣打印出來:

  1. (gdb) p &x
  2. $3 = (int *) 0x7fffffffe27c
  3. (gdb) p &heap_string
  4. $2 = (char **) 0x7fffffffe280
  5. (gdb) p &stack_string
  6. $4 = (char (*)[10]) 0x7fffffffe28e

因此,如果我們查看那些地址的堆棧,那我們應該能夠看到所有的這些變量!

概念:棧指針

我們將需要使用棧指針,因此我將盡力對其進行快速解釋。

有一個名為 ESP 的 x86 寄存器,稱為“棧指針stack pointer”。 基本上,它是當前函數的棧起始地址。 在 GDB 中,你可以使用 $sp 來訪問它。 當你調用新函數或從函數返回時,棧指針的值會更改。

第三步:在 main 函數開始的時候,我們查看一下在棧上的變量

首先,讓我們看一下 main 函數開始時的棧。 現在是我們的堆棧指針的值:

  1. (gdb) p $sp
  2. $7 = (void *) 0x7fffffffe270

因此,我們當前函數的棧起始地址是 0x7fffffffe270,酷極了。

現在,讓我們使用 GDB 打印出當前函數堆棧開始后的前 40 個字(即 160 個字節)。 某些內存可能不是棧的一部分,因為我不太確定這里的堆棧有多大。 但是至少開始的地方是棧的一部分。

 

我已粗體顯示了 stack_string,heap_string 和 x 變量的位置,并改變了顏色:

  • x 是紅色字體,并且起始地址是 0x7fffffffe27c
  • heap_string 是藍色字體,起始地址是 0x7fffffffe280
  • stack_string 是紫色字體,起始地址是 0x7fffffffe28e

你可能會在這里注意到的一件奇怪的事情是 x 的值是 0x5555,但是我們將 x 設置為 10! 那是因為直到我們的 main 函數運行之后才真正設置 x ,而我們現在才到了 main 最開始的地方。

第三步:運行到第十行代碼后,再次查看一下我們的堆棧

讓我們跳過幾行,等待變量實際設置為其初始化值。 到第 10 行時,x 應該設置為 10

首先我們需要設置另一個斷點:

  1. (gdb) b test.c:10
  2. Breakpoint 2 at 0x5555555551a9: file test.c, line 11.

然后繼續執行程序:

  1. (gdb) continue
  2. Continuing.
  3.  
  4. Breakpoint 2, main () at test.c:11
  5. 11 printf("Enter a string for the stack: ");

好的! 讓我們再來看看堆棧里的內容! gdb 在這里格式化字節的方式略有不同,實際上我也不太關心這些(LCTT 譯注:可以查看 GDB 手冊中 x 命令,可以指定 c 來控制輸出的格式)。 這里提醒一下你,我們的變量在棧上的位置:

  • x 是紅色字體,并且起始地址是 0x7fffffffe27c
  • heap_string 是藍色字體,起始地址是 0x7fffffffe280
  • stack_string 是紫色字體,起始地址是 0x7fffffffe28e

 

在繼續往下看之前,這里有一些有趣的事情要討論。

stack_string 在內存中是如何表示的

現在(第 10 行),stack_string 被設置為字符串stack。 讓我們看看它在內存中的表示方式。

我們可以像這樣打印出字符串中的字節(LCTT 譯注:可以通過 c 選項直接顯示為字符):

  1. (gdb) x/10x stack_string
  2. 0x7fffffffe28e: 0x73 0x74 0x61 0x63 0x6b 0x00 0x00 0x00
  3. 0x7fffffffe296: 0x00 0x00

stack 是一個長度為 5 的字符串,相對應 5 個 ASCII 碼- 0x73、0x74、0x61、0x63 和 0x6b。0x73 是字符 s 的 ASCII 碼。 0x74 是 t 的 ASCII 碼。等等...

同時我們也使用 x/1s 可以讓 GDB 以字符串的方式顯示:

  1. (gdb) x/1s stack_string
  2. 0x7fffffffe28e: "stack"

heap_string 與 stack_string 有何不同

你已經注意到了 stack_string 和 heap_string 在棧上的表示非常不同:

  • stack_string 是一段字符串內容(stack
  • heap_string 是一個指針,指向內存中的某個位置

這里是 heap_string 變量在內存中的內容:

  1. 0xa0 0x92 0x55 0x55 0x55 0x55 0x00 0x00

這些字節實際上應該是從右向左讀:因為 x86 是小端模式,因此,heap_string 中所存放的內存地址 0x5555555592a0

另一種方式查看 heap_string 中存放的內存地址就是使用 p 命令直接打印 :

  1. (gdb) p heap_string
  2. $6 = 0x5555555592a0 ""

整數 x 的字節表示

x 是一個 32 位的整數,可由 0x0a 0x00 0x00 0x00 來表示。

我們還是需要反向來讀取這些字節(和我們讀取 heap_string 需要反過來讀是一樣的),因此這個數表示的是 0x000000000a 或者是 0x0a,它是一個數字 10;

這就讓我把把 x 設置成了 10

第四步:從標準輸入讀取

好了,現在我們已經初始化我們的變量,我們來看一下當這段程序運行的時候,??臻g會如何變化:

  1. printf("Enter a string for the stack: ");
  2. gets(stack_string);
  3. printf("Enter a string for the heap: ");
  4. gets(heap_string);

我們需要設置另外一個斷點:

  1. (gdb) b test.c:16
  2. Breakpoint 3 at 0x555555555205: file test.c, line 16.

然后繼續執行程序:

  1. (gdb) continue
  2. Continuing.

我們輸入兩個字符串,為棧上存儲的變量輸入 123456789012 并且為在堆上存儲的變量輸入 bananas;

讓我們先來看一下 stack_string(這里有一個緩存區溢出)

  1. (gdb) x/1s stack_string
  2. 0x7fffffffe28e: "123456789012"

這看起來相當正常,對嗎?我們輸入了 12345679012,然后現在它也被設置成了 12345679012(LCTT 譯注:實測 gcc 8.3 環境下,會直接段錯誤)。

但是現在有一些很奇怪的事。這是我們程序的??臻g的內容。有一些紫色高亮的內容。

 

令人奇怪的是 stack_string 只支持 10 個字節。但是現在當我們輸入了 13 個字符以后,發生了什么?

這是一個典型的緩沖區溢出,stack_string 將自己的數據寫在了程序中的其他地方。在我們的案例中,這還沒有造成問題,但它會使你的程序崩潰,或者更糟糕的是,使你面臨非常糟糕的安全問題。

例如,假設 stack_string 在內存里的位置剛好在 heap_string 之前。那我們就可能覆蓋 heap_string 所指向的地址。我并不確定 stack_string 之后的內存里有一些什么。但我們也許可以用它來做一些詭異的事情。

確實檢測到了有緩存區溢出

當我故意寫很多字符的時候:

  1. ./test
  2. Enter a string for the stack: 01234567891324143
  3. Enter a string for the heap: adsf
  4. Stack string is: 01234567891324143
  5. Heap string is: adsf
  6. x is: 10
  7. *** stack smashing detected ***: terminated
  8. fish: Job 1, './test' terminated by signal SIGABRT (Abort)

這里我猜是 stack_string 已經到達了這個函數棧的底部,因此額外的字符將會被寫在另一塊內存中。

當你故意去使用這個安全漏洞時,它被稱為“堆棧粉碎”,而且不知何故有東西在檢測這種情況的發生。

我也覺得這很有趣,雖然程序被殺死了,但是當緩沖區溢出發生時它不會立即被殺死——在緩沖區溢出之后再運行幾行代碼,程序才會被殺死。 好奇怪!

這些就是關于緩存區溢出的所有內容。

現在我們來看一下 heap_string

我們仍然將 bananas 輸入到 heap_string 變量中。讓我們來看一下內存中的樣子。

這是在我們讀取了字符串以后,heap_string 在??臻g上的樣子:

 

需要注意的是,這里的值是一個地址。并且這個地址并沒有改變,但是我們來看一下指向的內存上的內容。

  1. (gdb) x/10x 0x5555555592a0
  2. 0x5555555592a0: 0x62 0x61 0x6e 0x61 0x6e 0x61 0x73 0x00
  3. 0x5555555592a8: 0x00 0x00

看到了嗎,這就是字符串 bananas 的字節表示。這些字節并不在??臻g上。他們存在于內存中的堆上。

堆和棧到底在哪里?

我們已經討論過棧和堆是不同的內存區域,但是你怎么知道它們在內存中的位置呢?

每個進程都有一個名為 /proc/$PID/maps 的文件,它顯示了每個進程的內存映射。 在這里你可以看到其中的棧和堆。

  1. $ cat /proc/24963/maps
  2. ... lots of stuff omitted ...
  3. 555555559000-55555557a000 rw-p 00000000 00:00 0 [heap]
  4. ... lots of stuff omitted ...
  5. 7ffffffde000-7ffffffff000 rw-p 00000000 00:00 0 [stack]

需要注意的一件事是,這里堆地址以 0x5555 開頭,棧地址以 0x7fffff 開頭。 所以很容易區分棧上的地址和堆上的地址之間的區別。

像這樣使用 gdb 真的很有幫助

這有點像旋風之旅,雖然我沒有解釋所有內容,但希望看到數據在內存中的實際情況可以使你更清楚地了解堆棧的實際情況。

我真的建議像這樣來把玩一下 gdb —— 即使你不理解你在內存中看到的每一件事,我發現實際上像這樣看到我程序內存中的數據會使抽象的概念,比如“棧”和“堆”和“指針”更容易理解。

更多練習

一些關于思考棧的后續練習的想法(沒有特定的順序):

 

  • 嘗試將另一個函數添加到 test.c 并在該函數的開頭創建一個斷點,看看是否可以從 main 中找到堆棧! 他們說當你調用一個函數時“堆棧會變小”,你能在 gdb 中看到這種情況嗎?
  • 從函數返回一個指向棧上字符串的指針,看看哪里出了問題。 為什么返回指向棧上字符串的指針是不好的?
  • 嘗試在 C 中引起堆棧溢出,并嘗試通過在 gdb 中查看堆棧溢出來準確理解會發生什么!
  • 查看 Rust 程序中的堆棧并嘗試找到變量!
  • 在 噩夢課程 中嘗試一些緩沖區溢出挑戰。每個問題的答案寫在 README 文件中,因此如果你不想被寵壞,請避免先去看答案。 所有這些挑戰的想法是給你一個二進制文件,你需要弄清楚如何導致緩沖區溢出以使其打印出 flag 字符串。

 

責任編輯:龐桂玉 來源: Linux中國
相關推薦

2017-02-06 18:42:37

Linuxgdb程序

2010-04-16 10:00:06

Oracle查看表空間

2021-06-04 05:18:29

ARM程序Gdbserver

2015-10-09 16:42:16

GDB 排查Python程序故障

2021-03-15 06:23:40

GDB調試代碼編程語言

2015-08-14 09:21:09

gdb工具調試 Go

2021-08-04 12:09:04

Linuxdf命令

2010-11-16 11:17:41

Oracle表空間大小

2010-10-27 16:33:39

Oracle查看表空間

2010-06-04 17:48:20

Linux編程工具

2021-07-28 08:53:53

GoGDB調試

2025-08-21 10:01:22

2009-11-24 17:20:48

Oracle查看用戶表

2021-01-20 13:50:45

MySQL數據庫代碼

2024-11-15 16:52:23

C#棧邊界棧基址

2023-05-04 12:39:27

GDB命令程序

2009-06-30 13:54:00

名稱空間Java

2022-09-15 14:56:12

GDB調試鴻蒙

2024-03-22 11:40:40

Node.jsNodeCRUD

2010-05-27 17:51:55

Linux查看磁盤空間
點贊
收藏

51CTO技術棧公眾號

91精品一区二区三区久久久久久| 国内外成人免费激情在线视频| 宅男在线观看免费高清网站| 久久精品国产亚洲精品| 色喇叭免费久久综合| 真实国产乱子伦对白视频| 图片区小说区区亚洲影院| 欧洲成人一区| 国产一区二区高清不卡| 亚洲精品五月天| 99久久久国产| 色一情一乱一伦一区二区三区丨| 亚洲大片一区二区三区| 综合毛片免费视频| 亚洲一区二区久久久久久久| 91丨九色porny丨蝌蚪| 日p在线观看| av二区三区| 超碰在线图片| 久久成人精品一区二区三区| 999久久久久久久久6666| 成人激情春色网| 国产成人综合网| 阿v免费在线观看| 97在线视频精品| 久久不射2019中文字幕| 97视频免费| 性欧美在线看片a免费观看| 国产成人99久久亚洲综合精品| 亚洲最大黄色| 国产精品爽爽爽| 亚洲国产精品欧美一二99| 999久久精品| 亚州av一区| 亚洲黄色成人久久久| 亚洲午夜免费电影| 中文字幕亚洲在线观看| 免费国产成人av| 久久人人爽国产| 亚洲成av人片| 亚洲无毛电影| 北岛玲一区二区三区| 美国av一区二区三区| 欧美日韩视频第一区| 影音国产精品| 日本成人网址| 亚洲bbw性色大片| 91精品欧美福利在线观看| 午夜影院日韩| www成人免费观看| 亚洲国产日韩综合一区| 亚洲欧美第一页| 久久色.com| 日韩精品欧美大片| 午夜国产视频| 奇米影视首页 狠狠色丁香婷婷久久综合| 亚洲美女屁股眼交| 玖玖在线精品| 我要色综合中文字幕| 亚洲第一精品区| 国内精品一区二区三区| ●精品国产综合乱码久久久久| 午夜精品毛片| dy888亚洲精品一区二区三区| 亚洲国产精品一区二区第一页| 欧美精品一区二区免费| 大桥未久av一区二区三区| 老色鬼精品视频在线观看播放| 亚洲电影二区| 日本中文在线| 先锋影音成人资源| 国产精品久久久久久久久久久久冷 | 香港成人在线视频| 国产一区二区三区四| 日韩av密桃| 亚洲成人三级| 在线日韩电影| 亚洲精品一区二区三区在线| 伊人成人开心激情综合网| 欧美三级韩国三级日本一级| 超碰国产精品久久国产精品99| 欧美日韩国产小视频| 亚洲成人久久电影| 性生交免费视频| 欧美日韩亚洲一区二区三区在线观看| 精品国产欧美成人夜夜嗨| 欧美极品xxx| 美女视频黄 久久| 国产高清一区| 日韩一二三区在线观看| 欧美人体一区二区三区| 免费国产视频| 91制片厂毛片| 国产视频九色蝌蚪| 亚洲一区二区三区欧美| 欧美在线播放一区| 99re视频在线| 国产欧美日韩综合一区在线观看| 欧美孕妇孕交黑巨大网站| 97色在线视频观看| 色噜噜狠狠狠综合欧洲色8| 你懂的在线看| 亚洲男人网站| 午夜刺激在线| 肉丝一区二区| 亚洲国产精品成人一区二区在线| av免费观看大全| 亚洲精品一区二| 亚洲一二三区在线| 水蜜桃亚洲精品| 亚洲资源视频| 欧洲精品久久久| 国产精品久久久久久久美男| 日本电影亚洲天堂| 庆余年2免费日韩剧观看大牛| 日本久久精品视频| 91在线观看免费高清完整版在线观看| 国产一区视频在线播放| 亚洲精品欧美日韩专区| 精品一区国产| 丁香婷婷综合激情| 蜜桃视频网站www| 国产黄网站在线观看| 91黄页在线观看| 亚洲天堂手机| 亚洲91在线| 忘忧草在线www成人影院| 蜜桃一区二区| 国内精品美女在线观看| 日本人妖一区二区| 久久久亚洲综合| 精品日本美女福利在线观看| 精品欧美一区二区在线观看| 中文字幕久久精品| 国产一区二区在线免费| 美国av在线播放| 成年人免费视频观看| 国产精品va在线观看视色| 久久精品国产精品亚洲毛片| 97青娱国产盛宴精品视频| 亚洲理伦在线| 国产午夜精品美女毛片视频| 91精品国产欧美日韩| 欧美激情亚洲精品| 亚洲成人午夜在线| 日本一二区视频| 97在线视频免费观看完整版| 久久99国产精品视频| 丰满亚洲少妇av| 日本高清不卡一区| 日韩视频免费在线| 亚洲一区二区三区免费看| 国产99久久精品一区二区永久免费 | 国产劲爆久久| 鲁大师成人一区二区三区| 精品久久久久久中文字幕一区奶水 | 97在线观看免费观看高清| 日本不卡免费高清视频在线| 久久91精品| 国产午夜亚洲精品理论片色戒| 精品欧美一区二区三区精品久久| 91久久精品美女高潮| 缅甸午夜性猛交xxxx| 成人综合影院| 国产一区视频在线观看免费| 天天影视网天天综合色在线播放| 7777精品伊久久久大香线蕉语言 | 亚洲少妇中出一区| 欧美xxxx做受欧美| 日韩中文字幕二区| 免费观看亚洲视频大全| 国产精品久久久一区麻豆最新章节| 久久这里有精品视频| 国产视频在线视频| 麻豆国产欧美一区二区三区r| 欧美日韩亚洲一区二区三区在线| 国产精品一二三区| 国产精品久久久久久久久免费丝袜 | 在线久久视频| 黄色av资源| 黄色小网站在线观看| 久久精品magnetxturnbtih| 天天做天天爱天天高潮| 另类av导航| 日韩黄色免费电影| 国产成人精品影视| 欧美日韩高清一区二区三区| 444亚洲人体| 天天久久夜夜| 欧美日韩国产小视频| 中文字幕久久综合| 久久av日韩| 国产日韩欧美在线一区| 丰满放荡岳乱妇91ww| 2020国产精品久久精品美国| 亚洲男人的天堂网站| 男女av免费观看| 欧美 日韩 国产 一区| 日韩一区二区在线看| 午夜精品一区二区在线观看|