主頁 > 企業開發 > 1.5 撰寫自定位ShellCode彈窗

1.5 撰寫自定位ShellCode彈窗

2023-07-04 08:30:25 企業開發

在筆者上一篇文章中簡單的介紹了如何運用匯編語言撰寫一段彈窗代碼,雖然簡易ShellCode可以被正常執行,但卻存在很多問題,由于采用了硬編址的方式來呼叫相應API函式的,那么就會存在一個很大的缺陷,如果作業系統的版本不統或系統重啟過,那么基址將會發生變化,此時如果再次呼叫基址引數則會呼叫失敗,本章將解決這個棘手的問題,通過ShellCode動態定位的方式解決這個缺陷,并以此設計出真正符合規范的ShellCode代碼片段,

自定位代碼是一種常見的Shellcode技術,它使Shellcode能夠在任何系統上運行,而無需考慮系統記憶體布局和代碼地址等問題,以下是Shellcode自定位代碼的流程:

  • 1.查找Kernel32.dll基址并在其中尋找LoadLibraryA
  • 2.計算函式名hash摘要并通過hash摘要判斷函式
  • 3.決議Kernel32.dll匯出表
  • 4.最終動態呼叫系列函式

1.5.1 動態查找Kernel32基址

首先我們需要通過匯編的方式來實作動態定位Kernel32.dll中的基址,你或許會有個疑問? 為什么要查找Kernel32.dll的地址而不是User32.dll,這是因為我們最終的目的是呼叫MessageBoxA這個函式,而該函式位于 User32.dll這個元件里,在某些程式中User32模塊并不一定會被加載,而Kernel32則必然會被加載,為了能夠呼叫MessageBoxA函式,我們就需要呼叫LoadLibraryA函式來加載User32.dll這個模塊,而LoadLibraryA恰巧又位于kernel32.dll中,因此我們只需要找到LoadLibraryA函式,即可實作加載任意的元件,并呼叫任意的函式的目的,

由于我們需要動態獲取LoadLibraryA()以及ExitProcess()這兩個函式的地址,而這兩個函式又是存在于kernel32.dll中的,因此這里需要先找到kernel32.dll的基址,然后通過對其進行決議,從而查找兩個函式的動態地址,動態的查找Kernel32.dll的地址可總結為如下:

  • 1.首先通過段選擇子FS在記憶體中找到當前行程內的執行緒環境塊結構體指標TEB,
  • 2.執行緒環境塊偏移位置為fs:[0x30]的位置處存放著指向行程環境塊PEB結構的指標,
  • 3.行程環境塊PEB偏移為0x0c的地址處存放著指向PEB_LDR_DATA的結構體指標,
  • 4.而在PEB_LDR_DATA偏移0x1c的地址處存放著指向Ldr.InMemoryOrderModuleList模塊初始化鏈表的頭指標,
  • 5.在初始化鏈表中存放的就是所有行程的模塊資訊,通過將偏移值加0x08讀者即可獲取到kernel32.dll的基地址,

既然有了固定的查詢定位公式,接下我們就使用WinDBG除錯器來手工完成對Kernel32.dll地址的定位:

小提示:Windbg是Windows Debugger的縮寫,是一種微軟提供的免費除錯器工具,用于分析和除錯Windows作業系統和應用程式,Windbg可以在不重啟系統的情況下,通過連接到正在運行的行程或者作業系統內核,獲取并分析程式的運行資訊、記憶體狀態、暫存器狀態、執行緒狀態、呼叫堆疊等資料,并可以使用符號檔案來決議程式中的符號名,從而幫助開發者定位問題和進行深入除錯,

讀者可通過附件獲取到WinDBG程式,當用戶打開WinDBG時讀者可通過Ctrl+E快捷鍵任意打開一個可執行程式,接著我們開始尋找吧;

1.通過段選擇子FS在記憶體中找到當前的執行緒環境塊TEB,這里可以利用本地除錯,并輸入!teb指令,讀者可看到如下輸出:

小提示:TEB(Thread Environment Block)是Windows作業系統中的一個重要資料結構,每個行程都有一個對應的TEB,它主要用于存盤執行緒的環境資訊和狀態,包括執行緒區域存盤(TLS)指標、例外處理鏈、堆疊資訊、Fiber資訊等,TEB由Windows內核自動創建和管理,可以通過系統呼叫和除錯器工具來訪問和修改其內容,

如上執行緒環境塊偏移位置為0x30的地方存放著指向行程環境塊PEB的指標,結合上圖可見,當前PEB的地址為002bb000

小提示:PEB是Windows作業系統的行程環境塊(Process Environment Block)的縮寫,PEB是一個資料結構,其中包含了關于行程的許多資訊,例如行程的模塊、堆、執行緒等等,PEB由作業系統內核在創建行程時分配和初始化,并且只有在行程運行期間才可用,

2.在行程環境塊中偏移位置為0x0c的地方存放著指向PEB_LDR_DATA結構體的指標,其中存放著已經被行程裝載的元件的資訊,如下圖所示;

3.接著PEB_LDR_DATA結構體偏移位置為0x1c的地方存放著指向模塊初始化鏈表的頭指標InInitializationOrderModuleList,如下圖所示;

4.模塊初始化鏈表InInitializationOrderModuleList中按順序存放著PE裝入運行時初始化模塊的資訊,第一個鏈表節點是ntdll.dll,第二個鏈表結點就是kernel32.dll,我們可以先看看
InInitializationOrderModuleList中的內容:

上圖中的0x005a3ad8保存的是第一個鏈節點的指標,決議一下這個結點,可發現如下地址:

上圖中的0x77200000ntdll.dll的模塊基地址,而0x005a4390則是指向下一個模塊的指標,我們繼續跟隨0x005a4390地址,則此處看到的標黃處是下一個模塊kernel32.dll的基地址,

最后我們通過輸入!peb命令,輸出當前所有載入模塊并驗證一下:

既然有了如上所述的方法,那么讀者可以很容易的實作這段功能,為了便于讀者理解,筆者先提供一段使用C語言書寫的實作方式,如下代碼所示;

#include <windows.h>
#include <stdio.h>

int main(int argc, char * argv[])
{
    DWORD *PEB = NULL;
    DWORD *Ldr = NULL;
    DWORD *Init = NULL;
    DWORD *Kernel32 = NULL;

    __asm
    {
        mov eax, fs:[0x30]
        mov PEB,eax
    }
    printf("得到PEB指標 = %x \n", PEB);

    Ldr = *(DWORD **)((unsigned char *)PEB + 0x0c);
    printf("得到LDR結構指標 = %x \n", Ldr);

    Init = *(DWORD **)((unsigned char *)Ldr + 0x1c);
    printf("得到InInitializationOrderModuleList結構指標 = %x \n", Init);

    Kernel32 = *(DWORD **)((unsigned char *)Init + 0x08);
    printf("得到Kernel32的基地址 = %x \n", Kernel32);

    system("pause");
    return 0;
}

運行輸出效果如下圖所示,讀者可自行檢查讀取結果的準確性;

將此段代碼翻譯為匯編模式也很容易,如下是通過匯編實作的流程;

    .386p
    .model flat,stdcall
    option casemap:none

include windows.inc
include kernel32.inc
includelib kerbcli.lib
assume fs:nothing

.code
    main PROC
        xor eax,eax
        xor edx,edx
        mov eax,fs:[30h]           ; 得到PEB結構地址
        mov eax,[eax + 0ch]        ; 得到PEB_LDR_DATA結構地址
        mov esi,[eax + 1ch]        ; 得到 InInitializationOrderModuleList
        lodsd                      ; 得到KERNEL32.DLL所在LDR_MODULE結構的
        mov eax,[eax]              ; Windows 7 以上要將這里打開
        mov edx,[eax + 8h]         ; 得到BaseAddress,既Kernel32.dll基址
        ret
    main ENDP
END main

1.5.2 動態查找并列舉行程模塊

在讀者閱讀過第一節中的內容時,相信您已經可以熟練的掌握WinDBG除錯器的基本使用了,本節我們將擴展一個知識點,以讓讀者能更好的理解WinDBG除錯命令,本次我們實作列舉行程模塊的功能,本案例將不在解釋基本功能,

通過PEB/TEB找到自身行程的所有載入模塊資料,首先獲取TEB執行緒環境塊,在編程的時候,TEB始終保存在暫存器FS中,

得到LDR結構:Ldr = *( ( DWORD ** )( ( unsigned char * )PEB + 0x0c ) );

然后再找到PEB結構偏移為0x30從該命令的輸出可以看出,PEB結構體的地址位于TEB結構體偏移0x30的位置處,

找到了PEB也就可以找到_PEB_LDR_DATA結構 其位于PEB偏移0c的位置上,

Ldr = *( ( DWORD ** )( ( unsigned char * )PEB + 0x0c ) );

從輸出結果可以看出,LDRPEB結構體偏移的0x0C處,該地址保存的地址是0x77325d80通過該地址來決議LDR結構體,

Flink = *( ( DWORD ** )( ( unsigned char * )Ldr + 0x14 ) );

位于LDR偏移14的位置就是InLoadOrderModuleList其所指向的就是模塊名稱表,

現在來手動遍歷[ 0x5a3bd0 - 0x5aa5b8 ]第一條鏈表,輸入命令dd 0x5a3bd0

鏈表偏移0x18的位置是模塊的映射地址 ImageBase,鏈表偏移 0x28 的位置是模塊的路徑及名稱的地址,鏈表偏移 0x30 的位置是模塊名稱的地址,

如上圖中的輸出結果,地址005a2480保存有當前模塊詳細路徑資訊,而005a24ae則保存有當前模塊名,我們可以通過du命令來進行驗證;

當讀者需要讀入下一個模塊鏈表時,則需要訪問0x005a3ac8這個記憶體地址,其中保存著下一個鏈表結構,依次遍歷,

當然這個鏈表結構其實訪問InMemoryOrderModuleList同樣可以得到,這兩個都指向同一片區域,

上方介紹的結構,是微軟保留結構,只能從網上找到一個結構定義,根據該結構的定義做進一步決議即可,

typedef struct _LDR_DATA_TABLE_ENTRY {
    PVOID Reserved1[2];
    LIST_ENTRY InMemoryOrderLinks;
    PVOID Reserved2[2];
    PVOID DllBase;
    PVOID EntryPoint;
    PVOID Reserved3;
    UNICODE_STRING FullDllName;
    BYTE Reserved4[8];
    PVOID Reserved5[3];
    union {
    ULONG CheckSum;
    PVOID Reserved6;
    };
    ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

根據如上分析細節,那么描述列舉模塊串列的核心代碼就可以寫成如下案例;

#include <Windows.h>
#include <stdio.h>

int main(int argc, char* argv[])
{
    DWORD *PEB = NULL, *Ldr = NULL, *Flink = NULL, *p = NULL;
    DWORD *BaseAddress = NULL, *FullDllName = NULL,*Ba = NULL;

    __asm
    {
        mov eax, fs:[0x30]
        mov PEB, eax
    }

    Ldr = *((DWORD **)((unsigned char *)PEB + 0x0c));
    Flink = *((DWORD **)((unsigned char *)Ldr + 0x14));
    p = Flink;

    p = *((DWORD **)p);
    while (Flink != p)
    {
        BaseAddress = *((DWORD **)((unsigned char *)p + 0x10));
        FullDllName = *((DWORD **)((unsigned char *)p + 0x20));

    if (BaseAddress == 0)
        break;

    printf("鏡像基址 = %08x \n --> 模塊路徑 = %S \n", BaseAddress, (unsigned char *)FullDllName);

        p = *((DWORD **)p);
    }
    system("pause");
    return 0;
}

讀者編譯并運行該程式,則默認會列舉出當前模塊所匯入的所有模塊資訊,其輸出效果如下圖所示;

1.5.3 計算函式Hash摘要值

案例介紹了如何使用Win32匯編語言和C語言計算字串的hash摘要值,字串的hash摘要值是通過一定的演算法將字串壓縮為一個固定長度的十六進制數,用于在程式中進行快速的字串比較,具體而言,該案例使用了回圈移位hash計演算法,并最終得到了字串的 hash 值,并以十六進制數的形式輸出,

讀者一定有疑問為啥需要HASH壓縮處理? 原因是,如果直接將函式名壓堆疊的話,我們就需要提供更多的空間來存盤ShellCode代碼,為了能夠讓我們撰寫的ShellCode代碼更加的短小精悍,所以我們將要對字串進行hash處理,將字串壓縮為一個十六進制數,這樣只需要比較二者hash值就能夠判斷目標函式,盡管這樣會引入額外的hash演算法,但是卻可以節省出存盤函式名字的空間,

為了能讓讀者理解計算原理,此處我們先使用C語言做摘要計算描述,如下代碼中的GetHash函式,該函式接受一個指向字符陣列的指標,即一個字串,然后對字串進行哈希計算,并回傳計算結果,

哈希計算的程序是通過回圈遍歷字串中的每個字符,對其進行位運算和加法運算,最終得到一個32位的哈希值,對于字串中的每個字符,程式首先將哈希值左移25位,然后將結果右移7位,相當于是對哈希值進行了回圈右移25位,然后程式將該字符的ASCII值加到哈希值上,回圈遍歷完字串中的所有字符后,哈希值即為最終的計算結果,

#include <stdio.h>
#include <windows.h>

DWORD GetHash(char *fun_name)
{
    DWORD digest = 0;
    while (*fun_name)
    {
        digest = ((digest << 25) | (digest >> 7));
        digest += *fun_name;
        fun_name++;
    }
    return digest;
}

int main(int argc, char *argv[])
{
    DWORD MessageBoxHash;
    DWORD ExitProcessHash;
    DWORD LoadLibraryAHash;

    MessageBoxHash = GetHash("MessageBoxA");
    printf("MessageBoxHash = 0x%.8x\n", MessageBoxHash);

    ExitProcessHash = GetHash("ExitProcess");
    printf("ExitProcessHash = 0x%.8x\n", ExitProcessHash);

    LoadLibraryAHash = GetHash("LoadLibraryA");
    printf("LoadLibraryAHash = 0x%.8x\n", LoadLibraryAHash);

    system("pause");
    return 0;
}

運行上方C語言實作代碼,則讀者可以此獲取到三個核心函式的Hash值,其輸出效果如下圖所示;

在理解了C語言版本的計算流程后,那么匯編語言版本的也應該很容易理解,如下是使用Win32匯編語言的實作程序,并在MASM上正常編譯,匯編版字串轉換Hash值,

    .386p
    .model flat,stdcall
    option casemap:none
    
include windows.inc
include kernel32.inc
include msvcrt.inc
includelib kernel32.lib
includelib msvcrt.lib

.data
    data db "MessageBoxA",0h
    Fomat db "0x%x",0
.code
    main PROC
        xor eax,eax               ; 清空eax暫存器
        xor edx,edx               ; 清空edx暫存器
        lea esi,data              ; 取出字串地址
    loops:
        movsx eax,byte ptr[esi]   ; 每次取出一個字符放入eax中
        cmp al,ah                 ; 驗證eax是否為0x0即結束符
        jz nops                   ; 為0則說明計算完畢跳轉到nops
        ror edx,7                 ; 不為零,則進行回圈右移7位
        add edx,eax               ; 將回圈右移的值不斷累加
        inc esi                   ; esi自增,用于讀取下一個字符
        jmp loops                 ; 回圈執行
    nops:
        mov eax,edx               ; 結果存在eax里面
        invoke crt_printf,addr Fomat,eax
        ret
    main ENDP
END main

1.5.4 列舉Kernel32匯出表

在文章開頭部分我們通過WinDBG除錯器已經找到了Kernel32.dll這個元件的基地址,而Dll檔案本質上也是PE檔案,在Dll檔案中同樣存在匯出表,其內部記錄著該Dll的匯出函式,接著我們需要對Dll檔案的匯出表進行遍歷,不斷地搜索,從而找到我們所需要的API函式,同樣的可以通過如下定式獲取到指定的匯出表,

  • 1.從kernel32.dll加載基址算起,偏移0x3c的地方就是其PE檔案頭,
  • 2.PE檔案頭偏移0x78的地方存放著指向函式匯出表的指標,
  • 3.匯出表偏移0x1c處的指標指向存盤匯出函式偏移地址(RVA)的串列,
  • 4.匯出表偏移0x20處的指標指向存盤匯出函式函式名的串列,

首先我們通過WinDBG來實作讀取匯入表及匯出表試試,我們以讀取ole32.dll為例,首先讀者需要通過lmvm ole32.dll查詢到該模塊的入口地址,如圖所示該模塊的入口地址為0x75830000

決議DOS頭,DOS頭通過_IMAGE_DOS_HEADER結構被定義,在決議時讀者應傳入模塊入口0x75830000地址,其次DOS頭中e_lfanew欄位指向了PE頭,該欄位需要注意;

  • 執行讀入DOS頭:dt ole32!_IMAGE_DOS_HEADER 75830000

決議PE頭,PE頭通過DOS頭部的e_lfanew中存盤的之加上模塊基地址獲取到,在本例中則是通過75830000+0n264獲取到;

  • 讀入PE頭:dt ole32!_IMAGE_NT_HEADERS 75830000+0n264

接著需要在_IMAGE_OPTIONAL_HEADER可選頭中找到EXPORT匯出表基地址,通過PE頭基址75830108加上0x018也就是OptionalHeader的偏移,即可定位到DataDirectory[0]也就是匯出表基地址,其地址為75830180

根據上述定義,繼續尋找EXPORT匯出表的實際地址,需要注意的是Evaluate expression中的結果是根據ole32模塊的基地址與VirtualAddress當前地址相加后得到的,如下圖所示

當讀者需要列舉特定模塊時,則可通過模塊基地址加上例如Name欄位偏移值,來讀入模塊名稱;

如果讀者需要列舉所有匯出函式,則讀者可通過模塊基地址加上AddressOfNames欄位,并通過如下命令實作完整輸出;

  • .foreach(place {dd 758e4088}){r @$t0=${place}+75830000; .if(@$t0<778e4088){da @$t0}}

匯入表的列舉與匯出表類似,為了節約篇幅此處只給出除錯資料,讀者可根據自己的掌握情況自行分析學習;

# 根據模塊基地址獲取模塊e_lfanew
0:000> dt ole32!_IMAGE_DOS_HEADER 0x75830000
   +0x000 e_magic          : 0x5a4d
   +0x028 e_res2           : [10] 0
   +0x03c e_lfanew         : 0n264

# 定位到NT頭部
0:000> dt ole32!_IMAGE_NT_HEADERS 0x75830000 + 0n264
   +0x000 Signature        : 0x4550
   +0x004 FileHeader       : _IMAGE_FILE_HEADER
   +0x018 OptionalHeader   : _IMAGE_OPTIONAL_HEADER

# 基地址與e_lfanew相加得到OPTIONAL
0:000> ?0x75830000 + 0n264
Evaluate expression: 1971519752 = 75830108

# 查詢OPTIONAL
0:000> dt ole32!_IMAGE_OPTIONAL_HEADER -v -ny DataDirectory 75830108+0x018
struct _IMAGE_OPTIONAL_HEADER, 31 elements, 0xe0 bytes
   +0x060 DataDirectory : [16] struct _IMAGE_DATA_DIRECTORY, 2 elements, 0x8 bytes

0:000> ? 75830108+0x018+0x60
Evaluate expression: 1971519872 = 75830180

# 得到資料目錄表地址
0:000> dt ole32!_IMAGE_DATA_DIRECTORY 75830180+8
   +0x000 VirtualAddress   : 0xbd9f8
   +0x004 Size             : 0x460

0:000> ? 0x75830000+0xbd9f8
Evaluate expression: 1972296184 = 758ed9f8

# DataDirectory[1]即為匯入表,地址為758ed9f8
0:000> dt ole32!_IMAGE_IMPORT_DESCRIPTOR 758ed9f8
   +0x000 Characteristics  : 0xbe700
   +0x000 OriginalFirstThunk : 0xbe700
   +0x004 TimeDateStamp    : 0
   +0x008 ForwarderChain   : 0
   +0x00c Name             : 0xbe87a
   +0x010 FirstThunk       : 0xbd8a8

0:000> da 0x75830000+0xbe87a
758ee87a  "api-ms-win-crt-string-l1-1-0.dll"

# 每一個_IMAGE_IMPORT_DESCRIPTOR的大小為0x14
0:000> ?? sizeof(_IMAGE_IMPORT_DESCRIPTOR)
unsigned int 0x14

# 也就是說,每次遞增14即可輸出下一個匯入函式名
0:000> dt ole32!_IMAGE_IMPORT_DESCRIPTOR 758ed9f8+14
   +0x000 Characteristics  : 0xbe6f4
   +0x000 OriginalFirstThunk : 0xbe6f4
   +0x004 TimeDateStamp    : 0
   +0x008 ForwarderChain   : 0
   +0x00c Name             : 0xbe89c
   +0x010 FirstThunk       : 0xbd89c

0:000> da 0x75830000+0xbe89c
758ee89c  "api-ms-win-crt-runtime-l1-1-0.dl"

0:000> dt ole32!_IMAGE_IMPORT_DESCRIPTOR 758ed9f8+28
   +0x000 Characteristics  : 0xbe64c
   +0x000 OriginalFirstThunk : 0xbe64c
   +0x004 TimeDateStamp    : 0
   +0x008 ForwarderChain   : 0
   +0x00c Name             : 0xbeb88
   +0x010 FirstThunk       : 0xbd7f4
0:000> da 0x75830000+0xbeb88
758eeb88  "api-ms-win-crt-private-l1-1-0.dl"

# 分析第一個IID的IAT和INT
# 先看INT: IMAGE_THUNK_DATA其實就是一個DWORD,如IID一樣,也是一個接一個,最后一個為NULL

第一個:
0:000> dd 0xbe6f4+0x75830000 L1
758ee6f4  000be86c

# 最高位不為1(為1表示為序號輸入)指向_IMAGE_IMPORT_BY_NAME結構

.foreach(place {dd 758ee6f4}) {r @$t0 = ${place}+75830000+2; .if (@$t0<86d00000){da @$t0;}}
758ee86e  "_initterm_e"
758ee862  "_initterm"
75830002  "."
758eeb80  "memset"
758ee84e  "wcsncmp"
758ee858  "strcspn"

我們將問題回歸到列舉匯出表上,函式的RVA地址和名字按照順序存放在上述兩個串列中,我們可以在串列定位任意函式的RVA地址,通過與元件的基地址相加得到其真實的VA,而計算的地址就是我們最終在ShellCode中呼叫時需要的地址,其匯編核心列舉代碼如下所示;

#include <stdio.h>
#include <Windows.h>

int main(int argc, char * argv[])
{
    int a;
    __asm
    {
        mov ebx, dword ptr fs : [0x30]         ; 獲取當前執行緒資訊的地址
        mov ecx, dword ptr[ebx + 0xc]          ; 獲取PEB結構體的地址
        mov ecx, dword ptr[ecx + 0x1c]         ; 獲取PEB結構體中的LDR結構體的地址
        mov ecx, [ecx]                         ; 獲取LDR結構體中的InMemoryOrderModuleList的頭節點地址
        mov edx, [ecx + 0x8]                   ; 獲取第一個模塊的基址,即ntdll.dll的基址

        mov eax, [edx+0x3c]                    ; 獲取PE頭偏移地址
        mov ecx, [edx + eax + 0x78]            ; 獲取匯出表VA地址偏移
        add ecx,edx                            ; 將匯出表的VA地址轉換成絕對地址
        mov ebx, [ecx+0x20]                    ; 獲取匯出表中的匯出函式名偏移陣列的地址
        add ebx,edx                            ; 將函式名偏移陣列的VA地址轉換成絕對地址
        xor edi,edi                            ; 將edi清零,用于回圈計數

    s1:
        inc edi                                ; 計數器自增1
        mov esi, [ebx+edi*4]                   ; 通過偏移獲取匯出函式名的地址
        add esi,edx                            ; 將匯出函式名的VA地址轉換成絕對地址

        cmp esi,edx                            ; 檢查匯出函式名的地址是否合法,如果等于基址則跳過
        je no
        loop s1                                ; 繼續查找匯出函式名

    no:
        xor eax,eax                            ; 清零eax暫存器,用于回傳值
    }
    system("pause");
    return 0;
}

1.5.5 整合自定位ShellCode

完整的匯編代碼如下,下方代碼是一個定式,這里就只做了翻譯,使用編譯器編譯如下代碼,

#include <stdio.h>
#include <windows.h>

int main(int argc, char *argv)
{
    __asm
    {
        // 將索要呼叫的函式hash值入堆疊保存
            CLD                      // 清空標志位DF
            push 0x1E380A6A          // 壓入MessageBoxA-->user32.dll
            push 0x4FD18963          // 壓入ExitProcess-->kernel32.dll
            push 0x0C917432          // 壓入LoadLibraryA-->kernel32.dll
            mov esi, esp             // 指向堆疊中存放LoadLibraryA的地址
            lea edi, [esi - 0xc]     // 后面會利用edi的值來呼叫不同的函式

            // 開辟記憶體空間,這里是堆疊空間
            xor ebx, ebx
            mov bh, 0x04       // ebx為0x400
            sub esp, ebx       // 開辟0x400大小的空間

            // 將user32.dll入堆疊
            mov bx, 0x3233
            push ebx           // 壓入字符'32'
            push 0x72657375    // 壓入字符 'user'
            push esp
            xor edx, edx        // edx=0

            // 查找kernel32.dll的基地址
            mov ebx, fs:[edx + 0x30]     // [TEB+0x30] -> PEB
            mov ecx, [ebx + 0xC]         // [PEB+0xC] -> PEB_LDR_DATA
            mov ecx, [ecx + 0x1C]        // [PEB_LDR_DATA+0x1C] -> InInitializationOrderModuleList
            mov ecx, [ecx]               // 進入鏈表第一個就是ntdll.dll
            mov ebp, [ecx + 0x8]         //ebp = kernel32.dll 的基地址

        // hash 的查找相關
        find_lib_functions :
                           lodsd                     // eax=[ds*10H+esi],讀出來是LoadLibraryA的Hash
                           cmp eax, 0x1E380A6A       // 與MessageBoxA的Hash進行比較
                           jne find_functions        // 如果不相等則繼續查找
                           xchg eax, ebp
                           call[edi - 0x8]
                           xchg eax, ebp

        // 在PE檔案中查找相應的API函式
        find_functions :
        pushad
            mov eax, [ebp + 0x3C]        // 指向PE頭
            mov ecx, [ebp + eax + 0x78]  // 匯出表的指標
            add ecx, ebp                 // ecx=0x78C00000+0x262c
            mov ebx, [ecx + 0x20]        // 匯出函式的名字串列
            add ebx, ebp                 // ebx=0x78C00000+0x353C
            xor edi, edi                 // 清空edi中的內容,用作索引

        // 回圈讀取匯出表函式
        next_function_loop :
        inc edi                            // edi作為索引,自動遞增
            mov esi, [ebx + edi * 4]       // 從串列陣列中讀取
            add esi, ebp                   // esi保存的是函式名稱所在的地址
            cdq

        // hash值的運算程序
        hash_loop :
        movsx eax, byte ptr[esi]         // 每次讀取一個位元組放入eax
            cmp al, ah                   // eax和0做比較,即結束符
            jz compare_hash              // hash計算完畢跳轉
            ror edx, 7
            add edx, eax
            inc esi
            jmp hash_loop
        // hash值的比較函式
        compare_hash :
        cmp edx, [esp + 0x1C]
            jnz next_function_loop         // 比較不成功則查找下一個函式
            mov ebx, [ecx + 0x24]          // ebx=序數表的相對偏移量
            add ebx, ebp                   // ebx=序數表的絕對地址
            mov di, [ebx + 2 * edi]        // di=匹配函式的序數
            mov ebx, [ecx + 0x1C]          // ebx=地址表的相對偏移量
            add ebx, ebp                   // ebx=地址表的絕對地址
            add ebp, [ebx + 4 * edi]       // 添加到EBP(模塊地址庫)
            xchg eax, ebp                  // 將func addr移到eax中    
            pop edi                        // edi是pushad中最后一個堆疊
            stosd
            push edi
            popad

            cmp eax, 0x1e380a6a             // 與MessageBox的hash值比較
            jne find_lib_functions

        // 下方的代碼,就是我們的彈窗
        xor ebx, ebx          // 清空eb暫存器
            push ebx          // 截斷字串0

            push 0x2020206b
            push 0x72616873
            push 0x796c206f
            push 0x6c6c6568
            mov eax, esp

            push ebx          // push 0
            push eax          // push "hello lyshark"
            push eax          // push "hello lyshark"
            push ebx          // push 0
            call[edi - 0x04]  // call MessageBoxA

            push ebx          // push 0
            call[edi - 0x08]  // call ExitProcess
    }
    return 0;
}

運行后會彈出一個提示框hello lyshark說明我們成功了,此列代碼就是所謂的自定位代碼,該代碼可以不依賴于系統環境而獨立運行;

文章作者:lyshark (王瑞)
文章出處:https://www.cnblogs.com/LyShark/p/17521843.html
本博客所有文章除特別宣告外,均采用 BY-NC-SA 許可協議,轉載請注明出處!

轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/556547.html

標籤:訊息安全

上一篇:前端Vue基于騰訊地圖Api實作的選擇位置組件 回傳地址名稱詳細地址經緯度資訊

下一篇:返回列表

標籤雲
其他(162040) Python(38266) JavaScript(25520) Java(18286) C(15238) 區塊鏈(8275) C#(7972) AI(7469) 爪哇(7425) MySQL(7281) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5876) 数组(5741) R(5409) Linux(5347) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4609) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2438) ASP.NET(2404) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) .NET技术(1985) HtmlCss(1983) 功能(1967) Web開發(1951) C++(1942) python-3.x(1918) 弹簧靴(1913) xml(1889) PostgreSQL(1882) .NETCore(1863) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • IEEE1588PTP在數字化變電站時鐘同步方面的應用

    IEEE1588ptp在數字化變電站時鐘同步方面的應用 京準電子科技官微——ahjzsz 一、電力系統時間同步基本概況 隨著對IEC 61850標準研究的不斷深入,國內外學者提出基于IEC61850通信標準體系建設數字化變電站的發展思路。數字化變電站與常規變電站的顯著區別在于程序層傳統的電流/電壓互 ......

    uj5u.com 2020-09-10 03:51:52 more
  • HTTP request smuggling CL.TE

    CL.TE 簡介 前端通過Content-Length處理請求,通過反向代理或者負載均衡將請求轉發到后端,后端Transfer-Encoding優先級較高,以TE處理請求造成安全問題。 檢測 發送如下資料包 POST / HTTP/1.1 Host: ac391f7e1e9af821806e890 ......

    uj5u.com 2020-09-10 03:52:11 more
  • 網路滲透資料大全單——漏洞庫篇

    網路滲透資料大全單——漏洞庫篇漏洞庫 NVD ——美國國家漏洞庫 →http://nvd.nist.gov/。 CERT ——美國國家應急回應中心 →https://www.us-cert.gov/ OSVDB ——開源漏洞庫 →http://osvdb.org Bugtraq ——賽門鐵克 →ht ......

    uj5u.com 2020-09-10 03:52:15 more
  • 京準講述NTP時鐘服務器應用及原理

    京準講述NTP時鐘服務器應用及原理京準講述NTP時鐘服務器應用及原理 安徽京準電子科技官微——ahjzsz 北斗授時原理 授時是指接識訓通過某種方式獲得本地時間與北斗標準時間的鐘差,然后調整本地時鐘使時差控制在一定的精度范圍內。 衛星導航系統通常由三部分組成:導航授時衛星、地面檢測校正維護系統和用戶 ......

    uj5u.com 2020-09-10 03:52:25 more
  • 利用北斗衛星系統設計NTP網路時間服務器

    利用北斗衛星系統設計NTP網路時間服務器 利用北斗衛星系統設計NTP網路時間服務器 安徽京準電子科技官微——ahjzsz 概述 NTP網路時間服務器是一款支持NTP和SNTP網路時間同步協議,高精度、大容量、高品質的高科技時鐘產品。 NTP網路時間服務器設備采用冗余架構設計,高精度時鐘直接來源于北斗 ......

    uj5u.com 2020-09-10 03:52:35 more
  • 詳細解讀電力系統各種對時方式

    詳細解讀電力系統各種對時方式 詳細解讀電力系統各種對時方式 安徽京準電子科技官微——ahjzsz,更多資料請添加VX 衛星同步時鐘是我京準公司開發研制的應用衛星授時時技術的標準時間顯示和發送的裝置,該裝置以M國全球定位系統(GLOBAL POSITIONING SYSTEM,縮寫為GPS)或者我國北 ......

    uj5u.com 2020-09-10 03:52:45 more
  • 如何保證外包團隊接入企業內網安全

    不管企業規模的大小,只要企業想省錢,那么企業的某些服務就一定會采用外包的形式,然而看似美好又經濟的策略,其實也有不好的一面。下面我通過安全的角度來聊聊使用外包團的安全隱患問題。 先看看什么服務會使用外包的,最常見的就是話務/客服這種需要大量重復性、無技術性的服務,或者是一些銷售外包、特殊的職能外包等 ......

    uj5u.com 2020-09-10 03:52:57 more
  • PHP漏洞之【整型數字型SQL注入】

    0x01 什么是SQL注入 SQL是一種注入攻擊,通過前端帶入后端資料庫進行惡意的SQL陳述句查詢。 0x02 SQL整型注入原理 SQL注入一般發生在動態網站URL地址里,當然也會發生在其它地發,如登錄框等等也會存在注入,只要是和資料庫打交道的地方都有可能存在。 如這里http://192.168. ......

    uj5u.com 2020-09-10 03:55:40 more
  • [GXYCTF2019]禁止套娃

    git泄露獲取原始碼 使用GET傳參,引數為exp 經過三層過濾執行 第一層過濾偽協議,第二層過濾帶引數的函式,第三層過濾一些函式 preg_replace('/[a-z,_]+\((?R)?\)/', NULL, $_GET['exp'] (?R)參考當前正則運算式,相當于匹配函式里的引數 因此傳遞 ......

    uj5u.com 2020-09-10 03:56:07 more
  • 等保2.0實施流程

    流程 結論 ......

    uj5u.com 2020-09-10 03:56:16 more
最新发布
  • 1.5 撰寫自定位ShellCode彈窗

    在筆者上一篇文章中簡單的介紹了如何運用匯編語言撰寫一段彈窗代碼,雖然簡易`ShellCode`可以被正常執行,但卻存在很多問題,由于采用了硬編址的方式來呼叫相應API函式的,那么就會存在一個很大的缺陷,如果作業系統的版本不統或系統重啟過,那么基址將會發生變化,此時如果再次呼叫基址引數則會呼叫失敗,本... ......

    uj5u.com 2023-07-04 08:30:25 more
  • 前端Vue基于騰訊地圖Api實作的選擇位置組件 回傳地址名稱詳細地

    #### 前端Vue基于騰訊地圖Api實作的選擇位置組件 回傳地址名稱詳細地址經緯度資訊, 下載完整代碼請訪問uni-app插件市場地址:https://ext.dcloud.net.cn/plugin?id=13310 #### 效果圖如下: ![](https://p3-juejin.bytei ......

    uj5u.com 2023-07-03 07:45:11 more
  • 1.4 撰寫簡易ShellCode彈窗

    在前面的章節中相信讀者已經學會了使用Metasploit工具生成自己的ShellCode代碼片段了,本章將繼續深入探索關于ShellCode的相關知識體系,ShellCode 通常是指一個原始的可執行代碼的有效載荷,攻擊者通常會使用這段代碼來獲得被攻陷系統上的互動Shell的訪問權限,而現在用于描述... ......

    uj5u.com 2023-07-03 07:44:13 more
  • 1.4 撰寫簡易ShellCode彈窗

    在前面的章節中相信讀者已經學會了使用Metasploit工具生成自己的ShellCode代碼片段了,本章將繼續深入探索關于ShellCode的相關知識體系,ShellCode 通常是指一個原始的可執行代碼的有效載荷,攻擊者通常會使用這段代碼來獲得被攻陷系統上的互動Shell的訪問權限,而現在用于描述... ......

    uj5u.com 2023-07-03 07:43:04 more
  • css學習(一)

    ### css引入 1. 行內樣式 ```css 我是div元素 ``` 2.內部樣式 ```css ``` 3. 外部樣式 ```css /* 可以通過@import引入其他的css資源 */ @import url(./style.css); @import url(./test.css); . ......

    uj5u.com 2023-07-02 08:01:55 more
  • 什么是 CSR、SSR、SSG、ISR - 渲染模式詳解

    本文以 `React`、`Vue` 為例,介紹下主流的渲染模式以及在主流框架中如何實作上述的渲染模式。 ## 前置知識介紹 看渲染模式之前我們先看下幾個主流框架所提供的相關能力,了解的可跳到下個章節。 ### 掛載組件到 DOM 節點 這是主流框架最基本的能力,就是將組件渲染到指定的 `DOM` 節 ......

    uj5u.com 2023-07-02 07:56:36 more
  • vue中封裝服務器地址/介面與設定請求頭

    1. 設定請求頭 首先創建一個放置服務器地址的js,如http.js,然后在http.js中引入axios `import axios from "axios";` 如果沒有axios,需要先安裝,npm i axios或者yarn add axois,然后重啟服務器 ...直接上代碼 點擊查看代碼 ......

    uj5u.com 2023-07-02 07:56:27 more
  • vue中封裝服務器地址/介面與設定請求頭

    1. 設定請求頭 首先創建一個放置服務器地址的js,如http.js,然后在http.js中引入axios `import axios from "axios";` 如果沒有axios,需要先安裝,npm i axios或者yarn add axois,然后重啟服務器 ...直接上代碼 點擊查看代碼 ......

    uj5u.com 2023-07-02 07:56:12 more
  • css學習(一)

    ### css引入 1. 行內樣式 ```css 我是div元素 ``` 2.內部樣式 ```css ``` 3. 外部樣式 ```css /* 可以通過@import引入其他的css資源 */ @import url(./style.css); @import url(./test.css); . ......

    uj5u.com 2023-07-02 07:55:38 more
  • 1.3 Metasploit 生成SSL加密載荷

    在本節中,我們將介紹如何通過使用`Metasploit`生成加密載荷,以隱藏網路特征。前一章節我們已經通過`Metasploit`生成了一段明文的ShellCode,但明文的網路傳輸存在安全隱患,因此本節將介紹如何通過生成SSL證書來加密ShellCode,使得網路特征得到隱藏,從而提高后門的生存能... ......

    uj5u.com 2023-07-02 07:55:08 more