IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    如何编写高质量的Windows Shellcode

    针发表于 2017-05-12 02:29:39
    love 0

    概述

    当我们在编写一个Shellcode Payload时,我们总是拥有无限的可能性,尤其是在Windows平台上。但我们要知道的是,想要编写高质量的Shellcode其实并非易事,因此我才决定要通过这篇文章跟大家聊一聊我对此的看法。就我个人而言,我比较喜欢用C语言(用Visual Studio编译源码)来完成我的工作。因为C语言的源代码非常优美,而且编译器可以最大程度地优化源码,如果你需要的话,也可以通过LLVM实现你自己的代码混淆器。

    为了方便演示,我将以x86 Shellcode作为样例进行讲解。当然了,你想将其用于x64平台也是可以的。

    查找基本的DLL

    介绍

    当一个Shellcode Payload在Windows平台中加载之后,第一步就是要定位我们所需要使用到的功能函数。首先,我们要搜索保存了系统功能函数的动态链接库(DLL)。为了实现这个目标,我们将会使用到多种不同的结构体,请大家往下看。

    线程环境块(TEB)

    TEB是Windows系统用于描述一个线程所使用到的一种结构。每一个线程都可以通过FS寄存器(x86平台)以及GS寄存器(x86_64平台)来访问自己的TEB。TEB的结构如下:

    0:000> dt ntdll!_TEB
        +0x000 NtTib            : _NT_TIB
        +0x01c EnvironmentPointer: Ptr32 Void
        +0x020ClientId         : _CLIENT_ID
        +0x028ActiveRpcHandle  : Ptr32 Void
        +0x02cThreadLocalStoragePointer : Ptr32 Void
        +0x030ProcessEnvironmentBlock : Ptr32 _PEB
        ...
    +0xff0 EffectiveContainerId :_GUID

    因此,如果你想要访问进程环境块(PEB)的话,你只需要使用下面这段代码即可:

    PEB* getPeb() {
        __asm {
            mov eax,fs:[0x30];
        }
    }

    进程环境块(PEB)

    如果TEB提供的是有关一个线程的信息,那么PEB提供的就是进程本身的信息了。而我们所需要的信息是那些基本的DLL所处的位置。实际上,当Windows在内存中加载一个进程时,至少会映射出两个DLL:

    1. ntdll.dll:它包含有可以进行系统调用(syscall)的功能函数。所有以Nt*为前缀的dll文件都可以调用相应的内核函数(以Zw*开头)。

    2. kernel32.dll:它是一个内核级文件,它可以调用高层的NTDLL函数。比如说,kernel32!CreateFileA可以调用ntdll!NtCreateFileW,而ntdll!NtCreateFileW又可以调用ntoskrnl!ZwCreateFileW。

    在Windows平台下,其他的DLL可能已经在内存中加载了,但是为了方便讲解,我们可以假设目前内存中只加载了上述两个DLL。

    接下来,让我们一起看一看下面这个TEB结构:

    0:000> dt nt!_PEB
        +0x000InheritedAddressSpace : UChar
        +0x001ReadImageFileExecOptions : UChar
        +0x002BeingDebugged    : UChar
        +0x003BitField         : UChar
        +0x003ImageUsesLargePages : Pos 0, 1 Bit
        +0x003IsProtectedProcess : Pos 1, 1 Bit
        +0x003IsImageDynamicallyRelocated : Pos 2, 1 Bit
        +0x003SkipPatchingUser32Forwarders : Pos 3, 1 Bit
        +0x003IsPackagedProcess : Pos 4, 1 Bit
        +0x003IsAppContainer   : Pos 5, 1 Bit
        +0x003IsProtectedProcessLight : Pos 6, 1 Bit
        +0x003IsLongPathAwareProcess : Pos 7, 1 Bit
        +0x004 Mutant           : Ptr32 Void
        +0x008ImageBaseAddress : Ptr32 Void
        +0x00c Ldr              : Ptr32 _PEB_LDR_DATA
        ...
    +0x25c WaitOnAddressHashTable :[128] Ptr32 Void

    你可以看到其中有一个PEB.BeingDebugged(第4行),这部分信息是IsDebuggerPresent()所要使用到的信息。但有趣的地方就在于上面的PEB.Ldr(第16行),它所对应的结构如下:

    0:000> dt nt!_PEB_LDR_DATA
        +0x000 Length           : Uint4B
        +0x004Initialized      : UChar
        +0x008SsHandle         : Ptr32 Void
        +0x00cInLoadOrderModuleList : _LIST_ENTRY
        +0x014InMemoryOrderModuleList : _LIST_ENTRY
        +0x01cInInitializationOrderModuleList : _LIST_ENTRY
        +0x024EntryInProgress  : Ptr32 Void
        +0x028ShutdownInProgress : UChar
    +0x02c ShutdownThreadId : Ptr32Void

    我们可以看到,PEB.Ldr->In*OrderModuleList(第5、6、7行)都是链表结构(LIST_ENTRY),其中包含内存中所有已经加载的DLL。这三个链表指向的是相同的对象,但顺序不同。我更愿意使用InLoadOrderModuleList,因为我可以直接把它当作指针(指向_LDR_DATA_TABLE_ENTRY)来使用。比如说,如果你想使用InMemoryOrderModuleList,那么_LDR_DATA_TABLE_ENTRY就直接位于_InMemoryOrderModuleList.Flink-0×10,因为InMemoryOrderModuleList.Flink指向的是下一个InMemoryOrderModuleList。其中,每一个链表元素的结构如下:

    0:000> dt nt!_LDR_DATA_TABLE_ENTRY
        +0x000InLoadOrderLinks : _LIST_ENTRY
        +0x008InMemoryOrderLinks : _LIST_ENTRY
        +0x010InInitializationOrderLinks : _LIST_ENTRY
        +0x018DllBase          : Ptr32 Void
        +0x01cEntryPoint       : Ptr32 Void
        +0x020SizeOfImage      : Uint4B
        +0x024FullDllName      : _UNICODE_STRING
        +0x02cBaseDllName      : _UNICODE_STRING
        ...
    +0x0a0 DependentLoadFlags :Uint4B

    上面的BaseDllName包含有DLL的名称(例如ntdll.dll),而DLLBase包含有DLL在内存中的地址。一般来说,InLoadOrderModuleList中的第一个元素就是可执行文件本身,随后便是NTDLL和KERNEL32。但我们无法保证它们在所有的Windows版本中都是以这样的顺序存在的,所以我们还是要以DLL文件的名称来确定位置。

    DJB哈希

    正如我之前所说,DLL的顺序并非固定的,因此我们要根据DLL名称来进行分析。但是在编写Shellcode时,我们一般不会使用ASCII字符串或UNICODE字符串,因为这样会使我们的Shellcode变得臃肿。所以我建议你通过哈希来匹配DLL名称,我这里使用的是DJB哈希,因为它是一种简单且高效的哈希:

    DWORD djbHashW(wchar_t* str) {
        unsigned int hash= 5381;
        unsigned int i =0;
        for (i = 0; str[i]!= 0; i++) {
            hash = ((hash<< 5) + hash) + str[i];
        }
        return hash;
    }

    由于DLL名称可能是大写字母或小写字母,所以我们的哈希算法最好也要支持这样的匹配:

      djbHashW(L"ntdll.dll") == djbHashW(L"NTDLL.DLL")

    代码

    基本内容已经介绍完了,那么接下来就是用代码来实现我们的目标了:

    typedef struct _LSA_UNICODE_STRING {
        USHORT Length;
        USHORTMaximumLength;
        PWSTR  Buffer;
    } LSA_UNICODE_STRING, *PLSA_UNICODE_STRING, UNICODE_STRING,*PUNICODE_STRING;
     
    typedef struct _LDR_DATA_TABLE_ENTRY {
        LIST_ENTRY              InLoadOrderModuleList;
        LIST_ENTRY              InMemoryOrderModuleList;
        LIST_ENTRY              InInitializationOrderModuleList;
        PVOID                   BaseAddress;
        PVOID                   EntryPoint;
        ULONG                   SizeOfImage;
       UNICODE_STRING         FullDllName;
       UNICODE_STRING         BaseDllName;
        ULONG                   Flags;
        SHORT                   LoadCount;
        SHORT                   TlsIndex;
        LIST_ENTRY              HashTableEntry;
        ULONG                   TimeDateStamp;
    } LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;
     
    typedef struct _PEB_LDR_DATA {
        ULONG      Length;
        ULONG      Initialized;
        ULONG      SsHandle;
        LIST_ENTRYInLoadOrderModuleList;
        LIST_ENTRYInMemoryOrderModuleList;
        LIST_ENTRYInInitializationOrderModuleList;
    } PEB_LDR_DATA, *PPEB_LDR_DATA;
     
    typedef struct _RTL_USER_PROCESS_PARAMETERS {
        BYTE           Reserved1[16];
        PVOID          Reserved2[10];
        UNICODE_STRINGImagePathName;
        UNICODE_STRINGCommandLine;
    } RTL_USER_PROCESS_PARAMETERS,*PRTL_USER_PROCESS_PARAMETERS;
     
    typedef struct _PEB {
        BYTE                          Reserved1[2];
        BYTE                          BeingDebugged;
        BYTE                          Reserved2[1];
        PVOID                         Reserved3[2];
        PPEB_LDR_DATA                 Ldr;
       PRTL_USER_PROCESS_PARAMETERS ProcessParameters;
        BYTE                          Reserved4[104];
        PVOID                         Reserved5[52];
        PVOID                        PostProcessInitRoutine;
        BYTE                          Reserved6[128];
        PVOID                         Reserved7[1];
        ULONG                         SessionId;
    } PEB, *PPEB;
     
    DWORD getDllByName(DWORD dllHash) {
      PEB* peb = getPeb();
      PPEB_LDR_DATA Ldr =peb->Ldr;
     PLDR_DATA_TABLE_ENTRY moduleList =(PLDR_DATA_TABLE_ENTRY)Ldr->InLoadOrderModuleList.Flink;
     
      wchar_t*pBaseDllName = moduleList->BaseDllName.Buffer;
      wchar_t*pFirstDllName = moduleList->BaseDllName.Buffer;
     
      do {
         if (pBaseDllName!= NULL) {
            if(djbHashW(pBaseDllName) == dllHash) {
               return(DWORD)moduleList->BaseAddress;
            }
         }
     
         moduleList =(PLDR_DATA_TABLE_ENTRY)moduleList->InLoadOrderModuleList.Flink;
         pBaseDllName =moduleList->BaseDllName.Buffer;
      } while(pBaseDllName != pFirstDllName);
     
      return 0;
    }

    如果你想要使用其他的DLL,那么你需要使用LoadLibrary()函数来加载它们。

    函数地址

    介绍

    DLL的问题已经解决了,那么接下来我们就要搜索函数在内存中的地址了。幸运的是,由于PE头非常好理解,所以这并不会难倒我们。而且需要注意的是,当我们讨论PE头时,绝大多数的地址都是相对地址。

    PE头

    在可执行文件的头部,我们可以找到DOS头:

    0:000> dt nt!_IMAGE_DOS_HEADER
        +0x000 e_magic          : Uint2B
        +0x002 e_cblp           : Uint2B
        +0x004 e_cp             : Uint2B
        +0x006 e_crlc           : Uint2B
        +0x008e_cparhdr        : Uint2B
        +0x00ae_minalloc       : Uint2B
        +0x00ce_maxalloc       : Uint2B
        +0x00e e_ss             : Uint2B
        +0x010 e_sp             : Uint2B
        +0x012 e_csum           : Uint2B
        +0x014 e_ip             : Uint2B
        +0x016 e_cs             : Uint2B
        +0x018e_lfarlc         : Uint2B
        +0x01a e_ovno           : Uint2B
        +0x01c e_res            : [4] Uint2B
        +0x024e_oemid          : Uint2B
        +0x026e_oeminfo        : Uint2B
        +0x028 e_res2           : [10] Uint2B
    +0x03c e_lfanew         : Int4B

    其中的e_lfanew元素代表的是NT头的位置,你需要计算pFile+ e_lfanew,因为这是一个相对地址。

    0:000> dt -r1 nt!_IMAGE_NT_HEADERS
        +0x000 Signature        : Uint4B
        +0x004FileHeader       : _IMAGE_FILE_HEADER
            +0x000Machine          : Uint2B
            +0x002NumberOfSections : Uint2B
            +0x004TimeDateStamp    : Uint4B
            +0x008PointerToSymbolTable : Uint4B
            +0x00c NumberOfSymbols  : Uint4B
            +0x010SizeOfOptionalHeader : Uint2B
            +0x012Characteristics  : Uint2B
        +0x018OptionalHeader   : _IMAGE_OPTIONAL_HEADER
            +0x000Magic            : Uint2B
            +0x002MajorLinkerVersion : UChar
            +0x003MinorLinkerVersion : UChar
            +0x004SizeOfCode       : Uint4B
            +0x008SizeOfInitializedData : Uint4B
            +0x00cSizeOfUninitializedData : Uint4B
            +0x010AddressOfEntryPoint : Uint4B
            +0x014BaseOfCode       : Uint4B
            +0x018 BaseOfData       : Uint4B
            +0x01cImageBase        : Uint4B
            +0x020SectionAlignment : Uint4B
            +0x024FileAlignment    : Uint4B
            +0x028MajorOperatingSystemVersion : Uint2B
            +0x02aMinorOperatingSystemVersion : Uint2B
            +0x02cMajorImageVersion : Uint2B
            +0x02eMinorImageVersion : Uint2B
            +0x030MajorSubsystemVersion : Uint2B
            +0x032MinorSubsystemVersion : Uint2B
            +0x034Win32VersionValue : Uint4B
            +0x038SizeOfImage      : Uint4B
            +0x03cSizeOfHeaders    : Uint4B
            +0x040CheckSum         : Uint4B
            +0x044Subsystem        : Uint2B
            +0x046DllCharacteristics : Uint2B
            +0x048SizeOfStackReserve : Uint4B
            +0x04cSizeOfStackCommit : Uint4B
            +0x050SizeOfHeapReserve : Uint4B
            +0x054SizeOfHeapCommit : Uint4B
            +0x058LoaderFlags      : Uint4B
            +0x05cNumberOfRvaAndSizes : Uint4B
            +0x060DataDirectory    : [16]_IMAGE_DATA_DIRECTORY

    数据目录中包含有一些比较有意思的成员地址,包括上面所提到的所有输出函数的地址。

    0:000> dt nt!_IMAGE_DATA_DIRECTORY
        +0x000VirtualAddress   : Uint4B
    +0x004 Size             : Uint4B

    所以,我们可以使用DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress直接拿到输出目录的地址:

    typedef struct _IMAGE_EXPORT_DIRECTORY {
        DWORD   Characteristics;
        DWORD   TimeDateStamp;
        WORD    MajorVersion;
        WORD    MinorVersion;
        DWORD   Name;
        DWORD   Base;
        DWORD   NumberOfFunctions;
        DWORD   NumberOfNames;
        DWORD   AddressOfFunctions;     
        DWORD   AddressOfNames;         
        DWORD   AddressOfNameOrdinals;  
    } IMAGE_EXPORT_DIRECTORY, *PIMAGE_EXPORT_DIRECTORY;

    我们可以通过函数名或按顺序输出一个函数。因此,下面这三个数组必须保持更新:

    1.  AddressOfFunctions:负责维持函数地址的顺序;

    2.  AddressOfNames:负责维持函数名称;

    3.  AddressOfnameOrdinals:负责维持顺序,该数组中的顺序与数组AddressOfNames相同;

    如果我们想要根据名称来获取到函数地址的话,我们只需要浏览AddressOfFunctions中的函数名,然后通过索引(AddressOfNameOrdinals[index])来搜索即可。伪代码如下:

    int i = 0;
    while (AddressOfNames[i] != searchedName) {
        i++;
    }
     
    return AddressOfFunctions[ AddressOfNamesOrdinals[i] ];

    代码

    与之前搜索DLL一样,我们还是使用DJB哈希(但这一次使用了ASCII字符串):

    PVOID getFunctionAddr(DWORD dwModule, DWORD functionHash) {
        PIMAGE_DOS_HEADERdosHeader = (PIMAGE_DOS_HEADER)dwModule;
        PIMAGE_NT_HEADERSntHeaders = (PIMAGE_NT_HEADERS)((DWORD)dosHeader + dosHeader->e_lfanew);
       PIMAGE_DATA_DIRECTORY dataDirectory = &ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
        if(dataDirectory->VirtualAddress == 0) {
            return NULL;
        }
     
       PIMAGE_EXPORT_DIRECTORY exportDirectory =(PIMAGE_EXPORT_DIRECTORY)(dwModule + dataDirectory->VirtualAddress);
        PDWORD ardwNames =(PDWORD)(dwModule + exportDirectory->AddressOfNames);
        PWORDarwNameOrdinals = (PWORD)(dwModule +exportDirectory->AddressOfNameOrdinals);
        PDWORDardwAddressFunctions = (PDWORD)(dwModule +exportDirectory->AddressOfFunctions);
        char* szName = 0;
        WORD wOrdinal = 0;
     
        for (unsigned inti = 0; i < exportDirectory->NumberOfNames; i++) {
            szName =(char*)(dwModule + ardwNames[i]);
     
            if(djbHash(szName) == functionHash) {
                wOrdinal =arwNameOrdinals[i];
                return(PVOID)(dwModule + ardwAddressFunctions[wOrdinal]);
            }
        }
     
        return NULL;
    }

    代码编译

    最终的代码

    我们已经介绍了重要的数据结构以及所要使用到的算法。接下来,我们要生成最终的shellcode。

    #pragma comment(linker, "/ENTRY:main")
     
    #include "makestr.h"
    #include "peb.h"
     
    typedef HMODULE (WINAPI* _LoadLibraryA)(LPCSTR lpFileName);
    typedef int (WINAPI* _MessageBoxA)(HWND hWnd, LPCSTR lpText,LPCSTR lpCaption, UINT uType);
     
    int main();
    DWORD getDllByName(DWORD dllHash);
    PVOID getFunctionAddr(DWORD dwModule, DWORD functionHash);
    DWORD djbHash(char* str);
    DWORD djbHashW(wchar_t* str);
     
    int main() {
        DWORD hashKernel32= 0x6DDB9555; // djbHashW(L"KERNEL32.DLL");
        DWORD hKernel32 =getDllByName(hashKernel32);
        if (hKernel32 ==0) {
            return 1;
        }
     
        DWORDhashLoadLibraryA = 0x5FBFF0FB; // djbHash("LoadLibraryA");
        _LoadLibraryAxLoadLibraryA = getFunctionAddr(hKernel32, hashLoadLibraryA);
        if (xLoadLibraryA== NULL) {
            return 1;
        }
     
        char szUser32[] =MAKESTR("user32.dll", 10);
        DWORD hUser32 =xLoadLibraryA(szUser32);
        if (hUser32 == 0){
            return 1;
        }
     
        DWORDhashMessageBoxA = 0x384F14B4; // djbHash("MessageBoxA");
        _MessageBoxAxMessageBoxA = getFunctionAddr(hUser32, hashMessageBoxA);
        if (xMessageBoxA== NULL) {
            return 1;
        }
     
        char szMessage[] =MAKESTR("Hello World", 11);
        char szTitle[] =MAKESTR(":)", 2);
        xMessageBoxA(0,szMessage, szTitle, MB_OK|MB_ICONINFORMATION);
     
        return 0;
    }
     
    inline PEB* getPeb() {
        __asm {
            mov eax,fs:[0x30];
        }
    }
     
    DWORD djbHash(char* str) {
        unsigned int hash= 5381;
        unsigned int i =0;
     
        for (i = 0; str[i]!= 0; i++) {
            hash = ((hash<< 5) + hash) + str[i];
        }
     
        return hash;
    }
    DWORD djbHashW(wchar_t* str) {
        unsigned int hash= 5381;
        unsigned int i =0;
     
        for (i = 0; str[i]!= 0; i++) {
            hash = ((hash<< 5) + hash) + str[i];
        }
     
        return hash;
    }
     
    DWORD getDllByName(DWORD dllHash) {
        PEB* peb =getPeb();
        PPEB_LDR_DATA Ldr= peb->Ldr;
       PLDR_DATA_TABLE_ENTRY moduleList = (PLDR_DATA_TABLE_ENTRY)Ldr->InLoadOrderModuleList.Flink;
     
        wchar_t*pBaseDllName = moduleList->BaseDllName.Buffer;
        wchar_t*pFirstDllName = moduleList->BaseDllName.Buffer;
     
        do {
            if(pBaseDllName != NULL) {
                if(djbHashW(pBaseDllName) == dllHash) {
                    return(DWORD)moduleList->BaseAddress;
                }
            }
     
            moduleList =(PLDR_DATA_TABLE_ENTRY)moduleList->InLoadOrderModuleList.Flink;
            pBaseDllName =moduleList->BaseDllName.Buffer;
        } while(pBaseDllName != pFirstDllName);
     
        return 0;
    }
     
    PVOID getFunctionAddr(DWORD dwModule, DWORD functionHash) {
        PIMAGE_DOS_HEADERdosHeader = (PIMAGE_DOS_HEADER)dwModule;
        PIMAGE_NT_HEADERSntHeaders = (PIMAGE_NT_HEADERS)((DWORD)dosHeader + dosHeader->e_lfanew);
       PIMAGE_DATA_DIRECTORY dataDirectory =&ntHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
        if(dataDirectory->VirtualAddress == 0) {
            return NULL;
        }
     
     
       PIMAGE_EXPORT_DIRECTORY exportDirectory =(PIMAGE_EXPORT_DIRECTORY)(dwModule + dataDirectory->VirtualAddress);
        PDWORD ardwNames =(PDWORD)(dwModule + exportDirectory->AddressOfNames);
        PWORDarwNameOrdinals = (PWORD)(dwModule +exportDirectory->AddressOfNameOrdinals);
        PDWORD ardwAddressFunctions =(PDWORD)(dwModule + exportDirectory->AddressOfFunctions);
        char* szName = 0;
        WORD wOrdinal = 0;
     
        for (unsigned inti = 0; i < exportDirectory->NumberOfNames; i++) {
            szName =(char*)(dwModule + ardwNames[i]);
     
            if(djbHash(szName) == functionHash) {
                wOrdinal =arwNameOrdinals[i];
                return(PVOID)(dwModule + ardwAddressFunctions[wOrdinal]);
            }
        }
     
        return NULL;
    }

    函数声明的顺序是非常重要的,因为它规定了代码编译的顺序。因此,如果我们想要直接调用shellcode,那么我们最好在一开始就定义Main函数,这一点想必大家都知道。

    在代码中,我们使用了“MAKESTR”来定义ASCII字符串,Python脚本所生成的代码如下:

    #pragma once
     
    #define MAKESTR(s, length) MAKESTR_##length(s)
     
    /*
    for i in range(1,51):
        s = "#defineMAKESTR_%d(s) {" % i
        for j in range(i):
            s +="s[%d]," % j
        s +="0}"
     
        print(s)
    */
     
    #define MAKESTR_1(s) {s[0],0}
    #define MAKESTR_2(s) {s[0],s[1],0}
    #define MAKESTR_3(s) {s[0],s[1],s[2],0}
    #define MAKESTR_4(s) {s[0],s[1],s[2],s[3],0}
    #define MAKESTR_5(s) {s[0],s[1],s[2],s[3],s[4],0}
    #define MAKESTR_6(s) {s[0],s[1],s[2],s[3],s[4],s[5],0}
    #define MAKESTR_7(s) {s[0],s[1],s[2],s[3],s[4],s[5],s[6],0}
    #define MAKESTR_8(s){s[0],s[1],s[2],s[3],s[4],s[5],s[6],s[7],0}
    #define MAKESTR_9(s){s[0],s[1],s[2],s[3],s[4],s[5],s[6],s[7],s[8],0}
    #define MAKESTR_10(s){s[0],s[1],s[2],s[3],s[4],s[5],s[6],s[7],s[8],s[9],0}
    #define MAKESTR_11(s){s[0],s[1],s[2],s[3],s[4],s[5],s[6],s[7],s[8],s[9],s[10],0}

    编译配置

    我使用的是VisualStudio 2017,但我认为其他编译器的配置也相差不大:

    C / C++
        Optimisation
            Reduce thesize /O1
            Smaller code/Os
        Code generation
            Disablesecurity verifications /GS-
    Linker
        Entries
            Ignore all thedefaults libraries /NODEFAULTLIB

    编译完成之后,我得到了一个约3KB的文件,然后随便使用一款反汇编程序就可以从文件中提取出shellcode了。

    Shellcode

    Shellcode的大小为339字节,其中主要一部分代码负责的是函数加载和DLL搜索。我们的Shellcode非常简单,但你可以根据你的需要来进行自定义修改。代码如下:

    #include <Windows.h>
     
    typedef void(__stdcall* _function)();
     
    char shellcode[] = 
       "\x55\x8B\xEC\x83\xEC\x1C\x53\x56\x57\x64\xA1\x30\x00\x00\x00\x8B"
       "\x40\x0C\x8B\x50\x0C\x8B\x4A\x30\x8B\xD9\x85\xC9\x74\x29\x0F\xB7"
       "\x01\x33\xFF\xBE\x05\x15\x00\x00\x66\x85\xC0\x74\x1A\x6B\xF6\x21"
       "\x0F\xB7\xC0\x03\xF0\x47\x0F\xB7\x04\x79\x66\x85\xC0\x75\xEE\x81"
       "\xFE\x55\x95\xDB\x6D\x74\x17\x8B\x12\x8B\x4A\x30\x3B\xCB\x75\xCA"
       "\x33\xC9\x5F\x5E\x5B\x85\xC9\x75\x0A\x33\xC0\x40\xEB\x74\x8B\x4A"
       "\x18\xEB\xEF\xBA\xFB\xF0\xBF\x5F\xE8\x69\x00\x00\x00\x85\xC0\x74"
       "\xE8\x8D\x4D\xF0\xC7\x45\xF0\x75\x73\x65\x72\x51\xC7\x45\xF4\x33"
       "\x32\x2E\x64\x66\xC7\x45\xF8\x6C\x6C\xC6\x45\xFA\x00\xFF\xD0\x85"
       "\xC0\x74\xC6\xBA\xB4\x14\x4F\x38\x8B\xC8\xE8\x37\x00\x00\x00\x85"
       "\xC0\x74\xB6\x6A\x40\x8D\x4D\xFC\xC7\x45\xE4\x48\x65\x6C\x6C\x51"
       "\x8D\x4D\xE4\xC7\x45\xE8\x6F\x20\x57\x6F\x51\x6A\x00\xC7\x45\xEC"
       "\x72\x6C\x64\x00\x66\xC7\x45\xFC\x3A\x29\xC6\x45\xFE\x00\xFF\xD0"
       "\x33\xC0\x8B\xE5\x5D\xC3\x55\x8B\xEC\x83\xEC\x10\x8B\x41\x3C\x89"
       "\x55\xFC\x8B\x44\x08\x78\x85\xC0\x74\x56\x8B\x54\x08\x1C\x53\x8B"
        "\x5C\x08\x24\x03\xD1\x56\x8B\x74\x08\x20\x03\xD9\x8B\x44\x08\x18"
       "\x03\xF1\x89\x55\xF0\x33\xD2\x89\x75\xF4\x89\x45\xF8\x57\x85\xC0"
       "\x74\x29\x8B\x34\x96\xBF\x05\x15\x00\x00\x03\xF1\xEB\x09\x6B\xFF"
       "\x21\x0F\xBE\xC0\x03\xF8\x46\x8A\x06\x84\xC0\x75\xF1\x3B\x7D\xFC"
       "\x74\x12\x8B\x75\xF4\x42\x3B\x55\xF8\x72\xD7\x33\xC0\x5F\x5E\x5B"
       "\x8B\xE5\x5D\xC3\x0F\xB7\x04\x53\x8B\x55\xF0\x8B\x04\x82\x03\xC1"
       "\xEB\xEB";
     
    int main() {
        char* payload =(char*) VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT,PAGE_EXECUTE_READWRITE);
        memcpy(payload,shellcode, sizeof(shellcode));
     
        _function function= (_function)payload;
        function();
     
        return 0;
    }

    * 参考来源:dimitrifourny, FB小编Alpha_h4ck编译,转载请注明来自FreeBuf.COM



沪ICP备19023445号-2号
友情链接