windows:shellcode生成框架和加載

  http://www.javashuo.com/article/p-kmjmdwcq-ne.html  分享了shellcode 的基本原理,核心思路是動態獲取GetProcAddress和LoadLibrary函數地址,再經過這兩個函數獲取其餘windows dll提供的函數地址;須要注意的是shellcode不能有全局變量和字符串,只能用局部變量和字符數組替代,這樣才能在函數執行時在棧動態分配存儲空間,不會和目標進程現有的空間衝突。html

       實戰中,經過這種方式生成shellcode技術上可行,但比較麻煩,須要改動的地方太多,下面介紹一種shellcode現成的框架,開發人員可直接在特定的地方添加特定的代碼便可,簡單、靈活、易用;shell

  一、先介紹一下vs的編譯順序windows

        一個大型的工程,每每有多個cpp文件組成,最後都被編譯成一個exe文件,那麼exe中那麼多函數,這些函數都是怎麼排序的了?數組

      (1)文件之間:好比有多個文件,默認是按照文件名排序,好比先按照0-9排序,再按照a-z排序;下面默認先編譯A.cpp,再編譯B.cpp,最後編譯main.c;可是也能夠認爲調整配置文件中的順序,讓B最早編譯,A稍後編譯,這樣一來生成exe中函數順序就按照這個來排序;框架

         

   (2)文件內部:按照定義/實現的順序;好比下面的例子:函數實現的順序分別是main、FuncB、FuncA,那麼編譯器在生成exe時編譯的順序也是這樣的。ide

        

   上面說了這麼多編譯順序,能用來幹啥了?------>   確認shellcode 的範圍和提取代碼,好比上面用戶把shellcode寫進A和B函數,那麼FuncA-FuncB者之間的代碼都是shellcode,再調用CreateFile函數就能很容易保存到磁盤了;函數

  二、shellcode框架介紹spa

  (1)先來看看shellcode生成框架:四個文件起名頗有講究,這樣一來編譯器就會按照這個順序來編譯源文件了,生成的exe中也會按照這個順序排列函數;設計

    0.entry文件中,並不執行shellcode,只是經過shellcodeEnd-shellcodeStart 確認shellcode 的範圍和起始地址,而後寫入磁盤的bin文件;3d

       

   (2)a.start: shellcodeStart函數直接jmp到shellcodeEntry;InitFuntions初始化須要用到的幾個關鍵函數;最關鍵的shellcodeEntry函數:用戶能夠在這裏添加shellcode的自定義邏輯

       

      這裏的shellcodeStart函數,是個裸函數,僅有一行代碼,就是jmp到shellcodeEntry,這麼簡單的功能,爲啥不直接把shellcodeEntry的代碼複製到這裏來,從這裏跳轉的意義是啥?

  •  shellcodeStart在這裏,生成shellcode的時候才能把最核心的getKernel32和getProcAddress函數包進來;
  •    後續加載shellcode的時候會用call,那麼須要從shellcode返回,這裏是裸函數,沒有ret,等後續shellcode全部的函數執行完後,最後一個ret恰好能夠返回shellcode加載器,這裏設計比較巧妙;
__declspec(naked) void ShellcodeStart() 
{
    __asm 
    {
        jmp ShellcodeEntry
    }

}

   (3)b.works:開發人員能夠在這裏自定義shellcode的執行邏輯(包裝在單獨的函數裏),而後在上面的shellcodeEntry中調用該函數便可;

      

   (4)最後一個z.end:收尾工做,目前是空白的,啥也沒作;因爲shellcode的範圍是shellcodeEnd-shellcodeStart,因此shellcode在End這行代碼已經截至,這個函數內部任何代碼都不會被收錄到shellcode,這裏請注意

      

   上面是整個框架的簡介,最核心的地方是a.start的shellcodeEntry和b.works,開發人員直接在這裏添加自定義的處理邏輯就好,其餘地方通常都不用更改了,整個框架的複用性很是好;編譯的時候也能看到各個文件編譯的順序:

  

       用IDA查看以下:函數的編譯順序確實是按照上面講的順序編譯的:

        

   三、shellcode代碼加載

  利用上述框架生成的shellcode,保存在磁盤bin文件,怎麼加載和運行了?這裏先介紹一個簡單的方法:本身編寫一個loader;

  核心原理很簡單:調用VirtualAlloc分配一段內存,而後把shellcode拷貝到這段內存,最後call跳轉到shellcode執行;

#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!%d\n", 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:%d\n", GetLastError());
        CloseHandle(hFile);
        return -1;

    }
    DWORD dwRead;
    ReadFile(hFile, lpAddress, dwSize, &dwRead, 0);
    __asm
    {
        call lpAddress;

    }
    _flushall();
    system("pause");
    return 0;
}

  用該加載器成功加載shellcode並運行:

  

 

   四、這裏的shellcode是本身loader加載的,運行空間也是loader分配的,容易被查出來;後續會介紹一些其餘方法,好比把shellcode注入其餘進程執行,增長被查找到的難度;

最後感謝這兩個連接的做者(特別是視頻介紹,由淺入深,通俗易懂),供初學者參考:

https://www.freebuf.com/articles/system/93983.html (有現成的工程供參考使用)

https://www.bilibili.com/video/BV1y4411k7ch?p=10  (有詳細的視頻過程演示和說明)

相關文章
相關標籤/搜索