1. 程式人生 > >PC平臺逆向破解實驗報告(待補充)

PC平臺逆向破解實驗報告(待補充)

http ble 小端序 逆向 info 返回值 模式 cal 一個

PC平臺逆向破解實驗報告(待補充)

實踐目標

本次實踐的對象是一個名為pwn1的linux可執行文件。

該程序正常執行流程是:main調用foo函數,foo函數會簡單回顯任何用戶輸入的字符串。

該程序同時包含另一個代碼片段,getShell,會返回一個可用Shell。正常情況下這個代碼是不會被運行的。我們實踐的目標就是想辦法運行這個代碼片段。我們將學習兩種方法運行這個代碼片段,然後學習如何註入運行任何Shellcode。

實踐內容

  • 手工修改可執行文件,改變程序執行流程,直接跳轉到getShell函數。
  • 利用foo函數的Bof漏洞,構造一個攻擊輸入字符串,覆蓋返回地址,觸發getShell函數。
  • 註入一個自己制作的shellcode並運行這段shellcode。

1 預備知識

linux下的破解自然需要熟悉linux的命令與操作,這裏給出一些linux下有關二進制的一些知識。

1.1 匯編語言基礎

一些同學已經匯編語言程序設計課程學習過匯編語言了,我們使用的語法是Intel的。而在linux下用gdb和objdump得到的匯編代碼的語法是AT&T的(嚴格來說有些區別),這兩者之間存在一些區別。

我們學的還是8086的實模式下的16位匯編語言,還需要了解32位寄存器。這個轉換還是比較容易的。

1.1.1 Intel語法與AT&T語法的區別

區別 Intel語法 AT&T語法 備註
源操作數和目的操作數的位置 MOV EAC, ECX movl %ecx, %eax 將ECX的值存入EAX
常量和寄存器的表示 MOV EAX, 12h movl $0x12, %eax 將0x12存入EAX
尋址方式的表示 MOV EAX, [EBX+20h] movl 0x20(%ebx), %eax Intel語法中[base+index * scale+disp]相當於AT&T中的disp(base, index, scale)
MOV EAX, [EBX+ECX * 4h -20h] movl -0x20h(%ebx, %ecx, 0x04),%eax
指令後綴 MOV EAX, dword ptr [EBX] movl (%ebx), %eax

AT&T語法中,指令後綴l, w, b分別對應long, word, byte, 在Intel語法裏就是dword ptr, word ptr, byte ptr。

1.1.2 x86寄存器與常用指令

x86處理器有一下的一些通用寄存器:

  • %eax (許多函數的返回值默認存在這個寄存器裏)
  • %ebx
  • %ecx
  • %edx
  • %esi
  • %edi

還有幾個專用寄存器:

  • %ebp
  • %esp (指向棧頂)
  • %eip (保存下一條指令的地址)
  • %eflag (各種標誌位都在這裏)

常用匯編指令

指令 機器碼 說明 示例
NOP 0x90 空指令,啥也不做
JNE 0x75 條件轉移指令,標誌位ZF==0 則跳轉
JE 0x74 條件轉移指令,標誌位ZF==1 則跳轉
JMP 0xe9 後面是四個字節的偏移量 無條件轉移指令
0xeb 後面是兩個字節的偏移量
0xff25 後面是四個字節的地址
CMP 比較指令,相當於減法,可以改變標誌位
call 0xe8 後面是四個字節的偏移量 函數調用,可以認為是先push下一條指令的地址,再jmp到對應的地址

1.1.3 其他內容

一般來說,我們看到的匯編語言函數的結構是這個樣子的:


<function_name>:
    pushl %ebp
    movl  %esp, %ebp
    
    subl  4*N, %esp   # N為局部變量個數

    # 函數主體

    leave
    ret

leave指令相當於

    movl %ebp, %esp
    popl %ebp

於是調用函數的時候,看到的匯編語言指令可能是這個樣子的


# 函數: <function_name>
# 函數參數: <p1> <p2> <p3>...<pn>

    pushl <pn>
    ...
    ...
    pushl <p2>
    pushl <p1>
    call  <function_name>  # 執行call命令時,會將<next_code>的地址壓棧

<next_code>:

此時,棧看起來就是這個樣子的:


低地址
+--------------+
| 局部變量2   <---  -8(%ebp)
+--------------+  
| 局部變量1   <--- -4(%ebp)
+--------------+
| 舊%ebp        <--- (%ebp)
+--------------+
| RET地址      <--- 4(%ebp)
+--------------+
| 參數1           <--- 8(%ebp)
+--------------+
| 參數2           <--- 12(%ebp)
+--------------+
| ... ...      
+--------------+
| 參數N          <--- N*4+4(%ebp)
+--------------+
高地址      

1.2 gdb的常用命令

下面給出常用的幾個gdb命令,並不全面。在以後的實驗中慢慢學習gdb就可以了,不用強求。

gdb命令 參數 含義 示例
break 地址 設置斷點,簡寫為b,地址類型包括:函數名,行號,*內存地址 break main; break 12; break *0x08048373
run 命令行參數 運行程序,可簡寫為r
clear 地址 清除斷點,與break相反
info break 顯示斷點信息
continue 繼續執行程序
attach 進程號 調試已經運行的程序
quit 退出

1.3 objdump的常見用法

objdump可以快速方便地反編譯簡單的、未被篡改的二進制文件,它可以讀取所有常用的ELF類型的文件。下面給出objdump常見的用法。

  • 查看ELF文件中所有節的數據或代碼

objdump -D <elf_object>

  • 只查看ELF文件中的程序代碼

objdump -d <elf_object>

  • 查看所有符號

objdump -tT <elf_object>

2 實驗過程

2.1 直接修改程序機器指令,改變程序執行流程

2.1.1 思路

第一個實驗的思路非常簡單,原本的getShell函數是程序中的“死代碼”,正常情況下永遠也不會執行。
我們通過修改main函數中的call指令,使得原本執行foo函數的程序轉而去執行getShell函數。總結下來就是下面兩步:

  • 找到getShell函數的位置
  • 修改main函數中,call指令的參數,使得程序調用getShell函數

2.1.2 過程

先用objdump反匯編目標程序,輸入指令objdump -d 20155110pwn1 | less

技術分享圖片

用less分頁顯示比more更方便,可以像使用vim那樣用“/”查找字符串。

然後再看看main函數

技術分享圖片

我們發現,在main函數中,按照正常流程,call指令會調用foo函數,e8是call指令的機器碼,後面跟著四字節的偏移量ff ff ff d7(小端序,補碼)。這裏的偏移量是怎麽求的呢?

call指令在執行時,會EIP當前的值,也就是下一條指令的地址——0x080484ba壓棧,然後修改寄存器EIP,EIP+偏移量= 0x080484b + 0xffffffd7 = 0x08048491(32位的有符號數運算),將EIP指向foo函數的起始地址。
我們需要修改call指令的偏移量,根據“目的地址=EIP(call的下一條指令的地址)+偏移量”,新的偏移量 = 0x0804847d(getShell函數的起始地址) - 0x080484ba = 0xffffffc3(補碼運算)

接著用十六進制編輯器,或者vim來修改目標程序即可。這裏使用vim,輸入:%!xxd進入十六進制模式,修改偏移量。

技術分享圖片

修改後,得到
技術分享圖片

運行修改後的程序,得到shell
技術分享圖片

未完待續

PC平臺逆向破解實驗報告(待補充)