栈溢出笔记1.5 换一个汇编工具
前面的內容中我們使用VC++內聯匯編,雖然很方便,但是無法定義字符串常量,導致我們需要把字符串的ASCII碼找到,再一個一個壓入棧中,比較繁瑣,本節,我們換一個匯編工具——nasm,選擇它是因為它可以跨平臺使用。
有了這個匯編工具,我們就可以定義字符串常量了,例如:
/*****************************************************************************/ DB “example_1”, 0x00 DB “HelloWorld”, 0x00 /*****************************************************************************/- 1
- 2
- 3
- 4
這樣方便是方便了,但是出現了新的問題,我們不可能寫成這個樣子:
/*****************************************************************************/ push ebp mov ebp, esp db “example_1”, 0x00 db “HelloWorld”, 0x00 db “user32.dll”, 0x00 lea ebx, [ebp-24h] push ebx mov ebx, 0x7c801d7b call ebx // LoadLibraryA /*****************************************************************************/- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
因為三個字符串為數據,而不是指令,不可能夾在指令中間,這樣會讓CPU將數據當做指令運行,程序跑飛。因此,只能放在指令區域之外。但是,又必須放在代碼段中,因為Shellcode只有指令,沒有數據段。因此,有了下面這種經典的做法:將字符串定義放置到一條CALL指令之后,由于CALL指令會將返回地址壓棧,此時壓入棧中的地址就為第一個字符串的地址,從而,我們可以在此后的代碼中在棧上獲取該字符串的地址。如下結構:
/*****************************************************************************/ JMP short GetString RunMsgBox: ... GetString: CALL RunMsgBox DB “example_1” DB “HelloWorld” /*****************************************************************************/- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
這種結構就充分說明了指令和數據并不區分,本來CALL指令用來在棧上保存指令,結果現在被用來保存數據了。
下面就是利用上述結構編寫的匯編程序:
/*****************************************************************************/ // example_8 nasm下的匯編Shellcode SECTION .textBITS 32global _main_main: jmp short GetStringRunMsgBox: pop ebx push ebx ; "user32.dll" mov eax, 0x7c801d7b ; LoadLibraryA call eax xor eax,eax push eax lea ebx, [ebx+11] ; "example_1" push ebx lea ebx, [ebx+10] ; "HelloWorld" push ebx push eax mov ebx, 0x77d507ea ; MessageBoxA call ebx push eax mov ebx, 0x7c81cafa ; ExitProcess call ebx GetString: call RunMsgBox db "user32.dll", 0x00 db "example_1", 0x00 db "HelloWorld", 0x00 /*****************************************************************************/- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
RunMsgBox中第一句pop ebx就將“user32.dll”的首地址保存在了ebx,這里注意與棧不同,后面的ebx地址是加而不是減。使用命令:
nasm -f win32 example_8.asm
編譯為obj文件。然后使用VS的cl.exe,鏈接為exe:
cl example_8.obj libcmt.lib
運行exe,成功:
?
圖34
下面我們在Immunity Debugger來看看:?
?
圖35
這一段就是完整的代碼,可見,字符串數據全部被反匯編為指令了,上圖中標出了三個字符串的結尾符。
標出了結尾符之后,問題也就來了,是的,空字節,又出現了空字節,而且這次更不好處理,因為我們只用了字符串的首地址。為了不出現空字節,我們定義字符串的時候不能加0x00結束符,但是使用字符串的時候又需要該結束符,而且,這樣定義的字符串位于代碼段,代碼段是不可寫的,也就是一旦定義,不可修改。所以,如果空字節會引起問題,就不要這樣定義。使用直接壓棧的方法反而容易處理。
使用nasm有個好處,就是可以直接編譯為bin格式,即操作碼,例如:
nasm -f bin -o example_8.bin example_8.asm
用HexEdit打開example_8.bin,如下:
?
圖36
這樣,可以簡單的寫個程序將bin文件寫出為Shellcode,就不用再去Immunity Debugger一個字節一個字節的摳出來了。
總結
以上是生活随笔為你收集整理的栈溢出笔记1.5 换一个汇编工具的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: (计算机组成原理)第三章存储系统-第四节
- 下一篇: 从0到100——知乎架构变迁史