<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    shellcode編寫探究

    VSole2022-06-09 15:34:57

    前言

    shellcode是不依賴環境,放到任何地方都可以執行的機器碼。shellcode的應用場景很多,本文不研究shellcode的具體應用,而只是研究編寫一個shellcode需要掌握哪些知識。

    ShellCode編寫原則

    1、不能有全局變量

    因為我們編寫shellcode時,使用的全局變量是自己的進程里面的全局變量,注入到別的進程里,這個地址就沒用了。

    2、不能使用常量字符串

    和第一點原因一樣,字符串常量值也是全局變量,注入到別的進程里,根本沒有這個字符串。

    要使用字符串,需要使用字符數組。

    char s[] = {'1','2',0};
    

    3、不能直接調用系統函數

    調用系統函數的方式是間接調用(FF15),需要從IAT表里獲取API地址,每個進程的IAT表位置不同,且對方的進程可能沒有導入你需要調用的函數的DLL,那么你是不能調用這個系統函數的。

    所以我們需要用到 LoadLibrary 和 GetProcAddress 這兩個函數,來動態獲取系統API的函數指針。

    但是 LoadLibrary,GetProcAddress 本身就是系統函數,它們本身就依賴IAT表,咋辦呢?

    解決方案是這樣的:通過FS:[0x30] 找到PEB,然后通過PEB里的LDR鏈表 [PEB+0x0C]找到 kernel32.dll 的地址,然后我們遍歷它的 IAT表,找到 LoadLibrary 和 GetProcAddress 函數。

    4、不能嵌套調用其他函數

    和前兩點道理是一樣的,本進程里的函數地址,拿到別的進程的虛擬地址空間是無效的。

    TEB/PEB

    每個線程都有一個TEB結構來存儲線程的一些屬性結構,TEB的地址用fs:[0]來獲取

    在0x30這個地址有一個指針指向PEB結構,PEB就是進程用來記錄自己信息的一個結構

    完整結構如下

    在PEB的0x00c偏移有一個 Ldr _PEB_LDR_DATA結構跟進去

    可以得到3個結構如下所示

    InLoadOrderModuleList:模塊加載的順序
    InMemoryOrderModuleList:模塊在內存的順序
    InInitializationOrderModuleList:模塊初始化的順序

    思路

    我們一般使用api會直接使用LoadLibraryGetProcessAddress,但是這里肯定會依賴IAT表,所以這里我們就需要自己實現api所完成的功能

    TEB -> PEB -> PEB + 0x0C -> Ldr _PEB_LDR_DATA -> InLoadOrderModuleList -> kernel32.dll -> 導出表定位GetProcessAddress -> 通過找到的GetProcessAddress實現LoadLibrary

    實現過程

    首先我們自己定義幾個結構體,因為我們不依賴系統自己實現

    typedef struct _UNICODE_STRING {
     USHORT Length;
     USHORT MaximumLength;
     PWSTR  Buffer;
    } UNICODE_STRING, *PUNICODE_STRING;
    typedef struct _PEB_LDR_DATA
    {
     DWORD Length;
     bool Initialized;
     PVOID SsHandle; 
     LIST_ENTRY InLoadOrderModuleList;
     LIST_ENTRY InMemoryOrderModuleList;
     LIST_ENTRY InInitializationOrderModuleList;
    } PEB_LDR_DATA,*PPEB_LDR_DATA;
    typedef struct _LDR_DATA_TABLE_ENTRY
    {
     LIST_ENTRY InLoadOrderLinks;
     LIST_ENTRY InMemoryOrderLinks;
     LIST_ENTRY InInitializationOrderLinks;
     PVOID DllBase;
     PVOID EntryPoint;
     UINT32 SizeOfImage;
     UNICODE_STRING FullDllName;
     UNICODE_STRING BaseDllName;
     UINT32 Flags;
     USHORT LoadCount;
     USHORT TlsIndex;
     LIST_ENTRY HashLinks;
     PVOID SectionPointer;
     UINT32 CheckSum;
     UINT32 TimeDateStamp;
     PVOID LoadedImports;
     PVOID EntryPointActivationContext;
     PVOID PatchInformation;
    } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
    typedef HMODULE (WINAPI * PLOADLIBRARY)(LPCSTR);
    typedef DWORD (WINAPI * PGETPROCADDRESS)(HMODULE, LPCSTR);
    typedef DWORD (WINAPI * PMESSAGEBOX)(HWND, LPCSTR,LPCSTR,UINT);
    

    然后定義shellcode,這里因為kernel32.dll是unicode字符串所以用兩字節存儲

     char szKernel32[] = {'k',0,'e',0,'r',0,'n',0,'e',0,'l',0,'3',0,'2',0,'.',0,'d',0,'l',0,'l',0,0,0}; // Unicode
     char szUser32[] = {'u','s','e','r','3','2','.','d','l','l',0};
     char szGetProcAddress[] = {'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0};
     char szLoadLibrary[] = {'L','o','a','d','L','i','b','r','a','r','y','A',0};
     char szMessageBox[] = {'M','e','s','s','a','g','e','B','o','x','A',0};
     char szHelloShellCode[] = {'H','e','l','l','o','S','h','e','l','l','C','o','d','e',0};
    

    找到InLoadOrderModuleList存入寄存器

     __asm
     {
      mov eax,fs:[0x30] // PEB
      mov eax,[eax+0x0C] // PEB->LDR
      add eax,0x0C // LDR->InLoadOrderModuleList
      mov pBeg,eax
      mov eax,[eax]
      mov pPLD,eax
     }
    

    找到kernel32.dll,通過遍歷的方式來尋找,通過LDR指向DllBase獲取基址

     // Find Kerner32.dll
     while (pPLD != pBeg)
     {
      pLast = (WORD*)pPLD->BaseDllName.Buffer;
      pFirst = (WORD*)szKernel32;
      while (*pFirst && *pLast == *pFirst)
       pFirst++,pLast++;
      if (*pFirst == *pLast)
      {
       dwKernelBase = (DWORD)pPLD->DllBase;
       break;
      }
      pPLD = (LDR_DATA_TABLE_ENTRY*)pPLD->InLoadOrderLinks.Flink;
     }
    

    然后通過指針定位到導出表

      // 通過指針定位到導出表
      PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dwKernelBase;
      PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
      PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
      PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(/images/shellcode/image_FILE_HEADER));
      PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
      PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)dwKernelBase + pOptionHeader->DataDirectory[0].VirtualAddress);
      // 導出函數地址表RVA
      DWORD *pAddOfFun_Raw = (DWORD*)((DWORD)dwKernelBase + pExportDirectory->AddressOfFunctions);
      // 導出函數名稱表RVA
      WORD *pAddOfOrd_Raw = (WORD*)((DWORD)dwKernelBase + pExportDirectory->AddressOfNameOrdinals);
      // 導出函數序號表RVA
      DWORD *pAddOfNames_Raw = (DWORD*)((DWORD)dwKernelBase + pExportDirectory->AddressOfNames);
    

    還是通過遍歷找到GetProcessAddress,用指針指向這個地址

      DWORD dwCnt = 0;
      char* pFinded = NULL, *pSrc = szGetProcAddress;
      for (; dwCnt < pExportDirectory->NumberOfNames;dwCnt++)
      {
       pFinded = (char*)((DWORD)dwKernelBase + pAddOfNames_Raw[dwCnt]);
       while (*pFinded && *pFinded == *pSrc)
        pFinded++, pSrc++;
       if (*pFinded == *pSrc)
       {
        pGetProcAddress = (PGETPROCADDRESS)(pAddOfFun_Raw[pAddOfOrd_Raw[dwCnt]] + (DWORD)dwKernelBase);
        break;
       }
       pSrc = szGetProcAddress;
      }
    

    然后就可以使用pGetProcessAddress實現LoadLibraryMessageBox

     // 通過pGetProcAddress進行調用
     pLoadLibrary = (PLOADLIBRARY)pGetProcAddress((HMODULE)dwKernelBase, szLoadLibrary);
     pMessageBox = (PMESSAGEBOX)pGetProcAddress(pLoadLibrary(szUser32),szMessageBox);
     pMessageBox(NULL,szHelloShellCode,0,MB_OK);
    

    完整代碼如下

    // shellcode.cpp : Defines the entry point for the console application.
    //
    #include "stdafx.h"
    #include <windows.h>
    #include <stdio.h>
    #include <string.h>
    typedef struct _UNICODE_STRING {
     USHORT Length;
     USHORT MaximumLength;
     PWSTR  Buffer;
    } UNICODE_STRING, *PUNICODE_STRING;
    typedef struct _PEB_LDR_DATA
    {
     DWORD Length;
     bool Initialized;
     PVOID SsHandle; 
     LIST_ENTRY InLoadOrderModuleList;
     LIST_ENTRY InMemoryOrderModuleList;
     LIST_ENTRY InInitializationOrderModuleList;
    } PEB_LDR_DATA,*PPEB_LDR_DATA;
    typedef struct _LDR_DATA_TABLE_ENTRY
    {
     LIST_ENTRY InLoadOrderLinks;
     LIST_ENTRY InMemoryOrderLinks;
     LIST_ENTRY InInitializationOrderLinks;
     PVOID DllBase;
     PVOID EntryPoint;
     UINT32 SizeOfImage;
     UNICODE_STRING FullDllName;
     UNICODE_STRING BaseDllName;
     UINT32 Flags;
     USHORT LoadCount;
     USHORT TlsIndex;
     LIST_ENTRY HashLinks;
     PVOID SectionPointer;
     UINT32 CheckSum;
     UINT32 TimeDateStamp;
     PVOID LoadedImports;
     PVOID EntryPointActivationContext;
     PVOID PatchInformation;
    } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
    typedef HMODULE (WINAPI * PLOADLIBRARY)(LPCSTR);
    typedef DWORD (WINAPI * PGETPROCADDRESS)(HMODULE, LPCSTR);
    typedef DWORD (WINAPI * PMESSAGEBOX)(HWND, LPCSTR,LPCSTR,UINT);
    DWORD WINAPI ShellCode();
    int main(int argc, char* argv[])
    {
     ShellCode();
     getchar();
     return 0;
    }
    DWORD WINAPI ShellCode()
    {
     PGETPROCADDRESS pGetProcAddress = NULL;
     PLOADLIBRARY pLoadLibrary = NULL;
     PMESSAGEBOX  pMessageBox = NULL;
     PLDR_DATA_TABLE_ENTRY pPLD;
     PLDR_DATA_TABLE_ENTRY pBeg;
     WORD *pFirst = NULL;
     WORD *pLast = NULL;
     DWORD ret = 0, i = 0;
     DWORD dwKernelBase = 0;
     char szKernel32[] = {'k',0,'e',0,'r',0,'n',0,'e',0,'l',0,'3',0,'2',0,'.',0,'d',0,'l',0,'l',0,0,0}; // Unicode
     char szUser32[] = {'u','s','e','r','3','2','.','d','l','l',0};
     char szGetProcAddress[] = {'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0};
     char szLoadLibrary[] = {'L','o','a','d','L','i','b','r','a','r','y','A',0};
     char szMessageBox[] = {'M','e','s','s','a','g','e','B','o','x','A',0};
     char szHelloShellCode[] = {'H','e','l','l','o','S','h','e','l','l','C','o','d','e',0};
     __asm
     {
      mov eax,fs:[0x30] // PEB
      mov eax,[eax+0x0C] // PEB->LDR
      add eax,0x0C // LDR->InLoadOrderModuleList
      mov pBeg,eax
      mov eax,[eax]
      mov pPLD,eax
     }
     // Find Kerner32.dll
     while (pPLD != pBeg)
     {
      pLast = (WORD*)pPLD->BaseDllName.Buffer;
      pFirst = (WORD*)szKernel32;
      while (*pFirst && *pLast == *pFirst)
       pFirst++,pLast++;
      if (*pFirst == *pLast)
      {
       dwKernelBase = (DWORD)pPLD->DllBase;
       break;
      }
      pPLD = (LDR_DATA_TABLE_ENTRY*)pPLD->InLoadOrderLinks.Flink;
     }
     // Kernel32.dll -> GetProcAddress
     if (dwKernelBase != 0)
     {
      // 通過指針定位到導出表
      PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)dwKernelBase;
      PIMAGE_NT_HEADERS pNTHeader = (PIMAGE_NT_HEADERS)((DWORD)pDosHeader + pDosHeader->e_lfanew);
      PIMAGE_FILE_HEADER pPEHeader = (PIMAGE_FILE_HEADER)((DWORD)pDosHeader + pDosHeader->e_lfanew + 4);
      PIMAGE_OPTIONAL_HEADER32 pOptionHeader = (PIMAGE_OPTIONAL_HEADER32)((DWORD)pPEHeader + sizeof(/images/shellcode/image_FILE_HEADER));
      PIMAGE_SECTION_HEADER pSectionHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOptionHeader + pPEHeader->SizeOfOptionalHeader);
      PIMAGE_EXPORT_DIRECTORY pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((DWORD)dwKernelBase + pOptionHeader->DataDirectory[0].VirtualAddress);
      // 導出函數地址表RVA
      DWORD *pAddOfFun_Raw = (DWORD*)((DWORD)dwKernelBase + pExportDirectory->AddressOfFunctions);
      // 導出函數名稱表RVA
      WORD *pAddOfOrd_Raw = (WORD*)((DWORD)dwKernelBase + pExportDirectory->AddressOfNameOrdinals);
      // 導出函數序號表RVA
      DWORD *pAddOfNames_Raw = (DWORD*)((DWORD)dwKernelBase + pExportDirectory->AddressOfNames);
      DWORD dwCnt = 0;
      char* pFinded = NULL, *pSrc = szGetProcAddress;
      for (; dwCnt < pExportDirectory->NumberOfNames;dwCnt++)
      {
       pFinded = (char*)((DWORD)dwKernelBase + pAddOfNames_Raw[dwCnt]);
       while (*pFinded && *pFinded == *pSrc)
        pFinded++, pSrc++;
       if (*pFinded == *pSrc)
       {
        pGetProcAddress = (PGETPROCADDRESS)(pAddOfFun_Raw[pAddOfOrd_Raw[dwCnt]] + (DWORD)dwKernelBase);
        break;
       }
       pSrc = szGetProcAddress;
      }
     }
     // 通過pGetProcAddress進行調用
     pLoadLibrary = (PLOADLIBRARY)pGetProcAddress((HMODULE)dwKernelBase, szLoadLibrary);
     pMessageBox = (PMESSAGEBOX)pGetProcAddress(pLoadLibrary(szUser32),szMessageBox);
     pMessageBox(NULL,szHelloShellCode,0,MB_OK);
     
     return 0;
    }
    

    成功彈窗

    這里我們進反匯編看一下,是有檢測堆棧平衡的代碼的

    在物理機里面查看也是有的

    這里關閉一下堆棧平衡的檢測,默認情況如下

    修改為禁用安全檢查

    即可生成沒有檢查堆棧平衡的代碼

    typedefchar函數
    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    本文示例是來自corCTF 2021中 的兩個內核題,由 BitsByWill 和 D3v17 所出。針對UAF漏洞,漏洞對象從kmalloc-64到kmalloc-4096,都能利用 msg_msg 結構實現任意寫。
    看雪論壇作者ID:NYSECbao
    進而如何配合userfaultfd實現對于當前進程的task_struct,以及cred的進攻利用,實現權限提升> 的這樣一種技術。"鉤子",這些鉤子是數據包穿越該協議棧過程中的明確定義的hook point。
    如何調包Win32API函數?其實就是HookPE文件自己的IAT表。
    lib文件在windows下有兩種形式出現,第一種就是普通的靜態庫,第二種是作為dll的導入庫。接下來我來分享一下如何在這兩種lib文件中注入后門代碼,使程序編譯后生成的exe在運行時候自動。執行我們后門,并且不影響正常lib的功能。lib實際上就是一堆Obj文件打包在了一起,當然還有一些額外的信息,這個之后再說。
    MRCTF2022 stuuuuub 題解
    2023-02-07 10:15:04
    Overview學了這么一段時間的Android,難得見到的一道比較對口的逆向題。e.c()通過執行which su命令后讀取輸出來檢查是否有su文件。讀取res.dat文件后調用了decodeSo函數進行解密存放在應用的數據目錄下的libnative.so,而decodeSo是libstub.so里的native函數。但是在libstub.so里卻沒有直接找到decodeSo函數,因此應該是JNI_OnLoad里動態注冊的。decode String另外libstub.so使用了Ollvm的字符串加密和控制流平坦化。這里參考官方給的WP中使用了AndroidNativeEmu框架。
    巧解一道CTF Android題
    2022-08-10 16:15:40
    無須還原代碼,窮舉爆破。我們打開jeb工具,定位到當前activity。我們看一下saveSN方法,可以看到這是一個native方法。我們解包一下apk,獲取到so文件。下面進入ida分析。導出函數并沒有相關java的native方法,說明是動態注冊。我們看下JNI_ONLOAD函數:jint JNI_OnLoad{ if ( !
    SAMPLE服務器的行為及其漏洞取決于一段時間內交換的一系列消息,這些消息決定了服務器的狀態。客戶端發送的消息序列以紅色突出顯示。AFLNET讀取響應報文并提取協議指定的狀態碼,確定當前的執行狀態。所出現了新的狀態序列哈希值則認為當前的測試用例是Interesting的。
    依賴于特定硬件環境的固件無法完整模擬,需要hook掉其中依賴于硬件的函數。LD_PRELOAD的劫持對于特定函數的劫持技術分為動態注入劫持和靜態注入劫持兩種。網上針對LD_PRELOAD的劫持也有大量的描述
    結果分析Hook前Hook后,我們的彈窗本該是hello的但是hook后,程序流程被我們修改了。760D34B2 55 push ebp760D34B3 8BEC mov ebp,esp通過這兩條指令,函數就可以在堆棧中為局部變量分配存儲空間,并在函數執行過程中保存和恢復現場。這樣做的好處是可以避免局部變量和其他函數之間的沖突,同時也可以提高函數的可讀性和可維護性。
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类