1. 程式人生 > >Linux逆向---可執行檔案程式碼靜態注入小實驗

Linux逆向---可執行檔案程式碼靜態注入小實驗

分析完節頭之後,我最大的收穫就是,這麼多的01,並不是所有的都是用來執行我寫的那段輸出helloworld的程式的,而且程式碼段中有很大一段空閒空間,這就給我們一個向可執行檔案中注入自己程式碼,然後通過修改程式邏輯達到讓它去執行我們自己寫的的部分的程式碼的邏輯的機會。

這裡我們的原始碼是這樣的:

  1 #include <stdio.h>
  2 int main()
  3 {
  4     printf("hello world");
  5     return 0;
  6 }

也就是我們一直分析的原始碼,把它編譯生成可執行檔案:

gcc hello.c -o hello.out

然後執行看看:

res1

這裡我想完成兩件事:

  1. 通過對其只讀資料節的修改,讓它輸出加個換行
  2. 向程式碼中靜態注入自己的一個函式,然後讓它輸出兩次hello world

這裡我們用hexedit來協助實驗,它的用法很簡單,0-F直接輸入就可以替換原來的值,Ctrl+x儲存退出,Ctrl+c不儲存退出,知道這些就足夠了,沒有的話可以考慮安裝一個。

sudo apt-get install hexedit

1.修改只讀資料段:

這個相對容易一些,之前通過對節的分析,我們知道程式的只讀資料節位於以下位置:

[16] .rodata PROGBITS 00000000004005c0 000005c0
​ 0000000000000010 0000000000000000 A 0 0 4

即偏移為0x05c0的位置,我們檢視一下這一位置:

res2

可以看到"helloworld"這個字串並沒有頂著0x05c0的頭寫,接下來我們再看一下objdump -d hello.out中main方法的指令:

res3

可以看到,讀取這個資料的時候也考慮了偏移量。

那麼現在,為了達到修改資料段並讓函式正確輸出,我們需要做兩件事情:

  1. 修改rodata節中的內容。
  2. 修改main函式中推入暫存器的地址。

1.修改rodata節的內容

換行符的ASCII碼為10,即0x0A,把rodata之前的所有位元組往前推動一位,然後末位新增0x0A即可:

res4

2.修改main函式中的內容

我們只需要把之前的偏移減一,變成0x05c3即可:
res5
好,一切就緒,Ctrl+x儲存退出。

執行一波看看:

res6

可見我們已經用後期幫粗心的程式設計師加上了換行符。

2. 靜態注入函式

注入這個詞聽起來很高階,不過鑑於這只是一個hello world性質的實驗並且本人目前的水平還很一般,這裡其實實現的方式非常簡單,就是在空白的程式碼段的部分增添上自己的程式碼,然後修改程式邏輯讓源控制流指向自己的函式塊而已。這整個過程也是全手動實現,先不用而且也不會太高深的工具。

我們的目的是讓函式輸出兩次hello world,為了達到這個目的,我們需要做兩件事:

  1. 增加自己的函式塊
  2. 修改main函式部分

1.增加自己的函式塊

我們自己的函式邏輯與當前的main函式基本相同,所以我們可以先參考一下當前的main函式的指令

res7

前後兩行是呼叫函式固定的格式,要做的其實就是將字串移入edi暫存器,清空eax暫存器,呼叫printf函式,再清空eax暫存器即可 (到這裡我還沒有去學太多彙編相關的知識,所以有些步驟不一定是必需的,這裡只是為了保證正確性)

接下來我們只要把這些十六進位制格式的指令copy到自己的函式塊所在的地址處就行了,但其實這裡有一個關鍵的問題,我在做這個實驗的時候也被這個問題纏了一會:

觀察呼叫的函式的地址,可以發現,這個值並不直接是0x400400,而是0xfffffec7,這裡其實是有一個地址轉換過程:

  • P-這個值所在的位置:0x400535

  • F-要呼叫的函式所在的位置 0x400400

那麼呼叫地址A=F-P-4=-0x139=FFFFEFC7,(這個4也是有講究的,等看到那部分而且看懂了之後再解釋)。

所以我們在寫我們自己的函式塊的時候也要這樣計算一下,接下來的計算過程我就省略了。

首先我們找到有效程式碼段之後的一塊空白區域,比如這裡:
在這裡插入圖片描述

然後種上我們自己的程式碼,完成後是這個樣子:

res10

2.修改main函式部分

我們已經知道我們的函式的入口位於0x4006F0,值所在位置為0x400535:

res11

則呼叫地址為:0x4006F0-0x400535-4=0x1B7,所以這樣修改:
res12
修改完成,Ctrl+x儲存退出

執行一波看看:

res13

可見我們自己的函式邏輯得以執行而且輸出了想要的結果。

附加實驗

昨晚前兩個之後我突發奇想,能不能輸出除了hello world之外的內容,也就是如果把非只讀資料段位置的資料送入edi暫存器,它能不能被成功輸出?於是這裡我又加了一個實驗,首先我在偏移0x730處增加了一個字串資料:“emmm”

res14

然後我修改我的函式,增加一個指令: mov $0x400730,%edi,修改後的程式碼如下:

res16

然後執行一波可以看到:

res17

出現了我們想要的結果。