[2022]填鸭式shellcode编写教程 (一) huoji shellcode,新手入门 2022-09-07 1300 次浏览 0 次点赞 本系列文章会填鸭式教学编写一个shellcode就像cobal strike、msf那种一样. 本系列分三章: 第一章将介绍实现一个简单的hello world 第二章将实现一个简单的合法的远程协助工具 第三章将分析cobal strike的shellcode并且与第一章第二章对照 不要紧张.本系列很简单的,小白也会.本文不涉及到大量的汇编(本来想手工汇编写的,但是肯定很多人不懂,就整简单的) ### 环境准备 我们要用到如下工具: vs2019(2022也可以,但是bug太多了目前)、ida、x64dbg.这里就不介绍工具的安装了. 打开vs2019,选择控制台应用(记住是c++控制台不是c#),然后一路下一步就到这个页面了: ![](https://key08.com/usr/uploads/2022/09/3522022151.png) 我们只写x32的shellcode,所以环境请保持不要动(debug/x86) ### 简单的注意事项 编写shellcode时候,要注意几点: 1. 由于ALSR的存在,PE文件有重定向表.shellcode是没有的 2. 由于shellcode没有重定向表,所以你在网上搜的那些XP时代的shellcode编写教程最多最多只能跑一次,再次重启就啥也没有了 3. 由于我们是shellcode没有rdata,变量只能在栈上也就是函数内,不能有全局变量 ### 解决重定向第一步: 获取kernel32的地址 为了解决重定向问题不至于shellcode只能启动一次,我们需要人肉定位API函数. 为了实现这个过程,我们需要几步走: 1. 在内存中寻找kernel32.dll、ntdll.dll这些dll 2. 解析这些dll的导出表 3. 通过导出表的名字定位到函数,拿到最关键的两个API,getprocessaddress和loadlibrary这样就不用每次都定位导出函数了 这就相当于你从原始时代一路敲工具进化到工业时代一样. 由于本篇是填鸭式教学,所以就不细说具体原理了 大概原理是32或者64的程序里面有个重要的结构是PEB,PEB是系统的环境变量,你的进程的参数啥的都在里面.PEB里面有LDR,LDR里面塞了kernel32、ntdll这些dll的地址,然后读这些地址的PE头解析拿到导出表,导出表有函数名字跟地址的信息,拿到这些信息就成功了 这是代码,可以直接复制粘贴,复制到main函数上面即可: 请不要对此产生愧疚,**因为成功的学习代码的第一步是让代码跑起来.跑起来了,再学习原理** ```cpp #include #include #define DEREF( name )*(UINT_PTR *)(name) typedef struct _UNICODE_STR { USHORT Length; USHORT MaximumLength; PWSTR pBuffer; } UNICODE_STR, * PUNICODE_STR; // WinDbg> dt -v ntdll!_PEB_LDR_DATA 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; // WinDbg> dt -v ntdll!_PEB_FREE_BLOCK typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes { struct _PEB_FREE_BLOCK* pNext; DWORD dwSize; } PEB_FREE_BLOCK, * PPEB_FREE_BLOCK; // struct _PEB is defined in Winternl.h but it is incomplete // WinDbg> dt -v ntdll!_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; typedef struct _LDR_DATA_TABLE_ENTRY { //LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first 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; void shellcode_start() { //代码不用理解,可以直接复制粘贴. uint64_t base_address = NULL; //0.读取fs,找到peb base_address = __readfsdword(0x30); unsigned short m_counter; uint64_t ldr_table; uint64_t dll_name; uint64_t hash_name; uint64_t kenrle32_base = 0; base_address = (uint64_t)((_PEB*)base_address)->pLdr; ldr_table = (uint64_t)((PPEB_LDR_DATA)base_address)->InMemoryOrderModuleList.Flink; //1. 通过peb里面的LDR找到kernel32的地址 while (ldr_table) { dll_name = (uint64_t)((PLDR_DATA_TABLE_ENTRY)ldr_table)->BaseDllName.pBuffer; m_counter = ((PLDR_DATA_TABLE_ENTRY)ldr_table)->BaseDllName.Length; hash_name = 0; do { hash_name = _rotr((unsigned long)hash_name, 13); if (*((unsigned char*)dll_name) >= 'a') hash_name += *((unsigned char*)dll_name) - 0x20; else hash_name += *((unsigned char*)dll_name); dll_name++; } while (--m_counter); //hash name其实是基于dll_name的因为我们也不想取到其他的莫名其妙的东西,做个简单地hash会准确很多 //如果你不想用hashname,那么你可以printf("%wZ",dll_name);观察一下dll name自己想一下新的思路 if ((unsigned long)hash_name == 0x6A4ABC5B) { //这就是kernel.dll的地址了 kenrle32_base = (uint64_t)((PLDR_DATA_TABLE_ENTRY)ldr_table)->DllBase; break; } ldr_table = DEREF(ldr_table); if (kenrle32_base != 0) { //找到了退出 break; } } if (kenrle32_base == 0) { __debugbreak(); } printf("kernel32: %p \n", kenrle32_base); } ``` 给main调用一下,看看调试输出 ```cpp int main() { shellcode_start(); std::cout << "Hello World!\n"; system("pause"); } ``` 点这个运行程序: ![](https://key08.com/usr/uploads/2022/09/76436711.png) 没有错误的,将会看到这个: ![](https://key08.com/usr/uploads/2022/09/960563760.png) 这个时候,你可以用processhacker看或者用vs自带的工具查看模块列表,确认一下是否有误 ![](https://key08.com/usr/uploads/2022/09/2838027624.png) 我们可以看到,是一模一样的 ![](https://key08.com/usr/uploads/2022/09/2528156804.png) 说明是没有问题的.让我们继续下一步 ### 解决重定向第二步: 解析导出表 有了地址后,我们直接解析导出表 解析导出表具体也分几步: 1. 获取PE头 2. 通过PE头获取导出表 3. 通过导出表获取到函数 这里也只说个大概,具体细节你需要参阅PE结构 以下代码贴在刚刚你的printf后,同时删掉之前的printf ```cpp typedef HMODULE(WINAPI* GetProcAddressT)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName); typedef HMODULE(WINAPI* LoadLibraryAT)(_In_ LPCSTR lpLibFileName); GetProcAddressT fnGetProcAddress = NULL; LoadLibraryAT fnLoadlibrary = NULL; //别在意这边的大小写驼峰混乱,因为是两套代码拼接的,懒得改了.... UINT_PTR uiAddressArray = NULL; UINT_PTR uiNameArray = NULL; UINT_PTR uiNameOrdinals = NULL; PIMAGE_NT_HEADERS32 pNtHeaders32 = NULL; PIMAGE_NT_HEADERS64 pNtHeaders64 = NULL; PIMAGE_DATA_DIRECTORY pDataDirectory = NULL; PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL; // 解析PE头 pNtHeaders32 = (PIMAGE_NT_HEADERS32)(kenrle32_base + ((PIMAGE_DOS_HEADER)kenrle32_base)->e_lfanew); pNtHeaders64 = (PIMAGE_NT_HEADERS64)(kenrle32_base + ((PIMAGE_DOS_HEADER)kenrle32_base)->e_lfanew); // 拿到导出表 pDataDirectory = (PIMAGE_DATA_DIRECTORY)&pNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; // 遍历导出表 pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(kenrle32_base + pDataDirectory->VirtualAddress); uiAddressArray = (kenrle32_base + pExportDirectory->AddressOfFunctions); uiNameArray = (kenrle32_base + pExportDirectory->AddressOfNames); uiNameOrdinals = (kenrle32_base + pExportDirectory->AddressOfNameOrdinals); unsigned long dwCounter = pExportDirectory->NumberOfNames; char str1[] = { 'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', '\0' }; char str2[] = { 'L','o','a','d','L','i','b','r','a','r','y','A','\0' }; while (dwCounter--) { char* cpExportedFunctionName = (char*)(kenrle32_base + DEREF_32(uiNameArray)); char* matchPtr = &str1[0]; int ret = 0; while (!(ret = *cpExportedFunctionName - *matchPtr) && *cpExportedFunctionName) { cpExportedFunctionName++; matchPtr++; } if (ret == 0) { uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(unsigned long)); fnGetProcAddress = (GetProcAddressT)(kenrle32_base + DEREF_32(uiAddressArray)); } else { cpExportedFunctionName = (char*)(kenrle32_base + DEREF_32(uiNameArray)); char* matchPtr = &str2[0]; ret = 0; while (!(ret = *cpExportedFunctionName - *matchPtr) && *cpExportedFunctionName) { cpExportedFunctionName++; matchPtr++; } if (ret == 0) { uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(unsigned long)); fnLoadlibrary = (LoadLibraryAT)(kenrle32_base + DEREF_32(uiAddressArray)); } } if (fnLoadlibrary && fnGetProcAddress) { break; } uiNameArray += sizeof(unsigned long); uiNameOrdinals += sizeof(unsigned short); } if (fnLoadlibrary == NULL || fnGetProcAddress == NULL) { __debugbreak(); } printf("kernel32: %p fnGetProcAddress :%p\n", fnLoadlibrary, fnGetProcAddress); ``` 不出意外的,你应该看得到: ![](https://key08.com/usr/uploads/2022/09/3628323598.png) ### 解决重定向第三步: 工业化! 是时候工业化了,我们现在拥有调用任意API的能力了! 让我们弹出第一个hello world的信息库 以下代码直接复制粘贴即可: ```cpp if (fnLoadlibrary == NULL || fnGetProcAddress == NULL) { __debugbreak(); } char str3[] = { 'U','S','E','R','3','2','.','d','l','l','\0' }; char str4[] = { 'M','e','s','s','a','g','e','B','o','x','A','\0' }; char str5[] = { 'h','e','l','l','o',' ','w','o','r','l','d','\0' }; typedef int (WINAPI* MessageBoxAT)(_In_opt_ HWND hWnd,_In_opt_ LPCSTR lpText,_In_opt_ LPCSTR lpCaption,_In_ UINT uType); MessageBoxAT pMessageBoxA = (MessageBoxAT)fnGetProcAddress(fnLoadlibrary(str3), str4); if (pMessageBoxA == NULL) { __debugbreak(); } pMessageBoxA(NULL, str5, NULL ,NULL); ``` 激动人心的时候到了.再次运行... boom ![](https://key08.com/usr/uploads/2022/09/2087083086.png) 让我们看看为什么 首先我们知道这是访问冲突,一般是访问错地址了,所以有可能是函数地址取错了 所以我们确认一下变量是否正确,点击左下角的局部变量,主要看fnGetProcAddress和fnLoadlibrary ![](https://key08.com/usr/uploads/2022/09/3177123801.png) 然后打开反汇编 ![](https://key08.com/usr/uploads/2022/09/840567697.png) 把这两地址复制到地址栏查看 ![](https://key08.com/usr/uploads/2022/09/1936434672.png) 你会发现fnGetProcAddress是正确function,但是fnLoadlibrary的地址长这样,很明显不是一个function(最简单的标记:function之间应该有int3) ![](https://key08.com/usr/uploads/2022/09/4080455390.png) 所以我们得回过头看去如何定位的.bug很简单,你可以先找一下,这是答案 ![](https://key08.com/usr/uploads/2022/09/1620150502.png) 修复完后,再运行,你应该看得到一个漂亮的hello world了 ![](https://key08.com/usr/uploads/2022/09/1514897830.png) ### 变成shellcode 我们的现在的是function,而不是一段shellcode.我们的目标是编写shellcode 所以我们要把它变成shellcode,代码很简单,请直接复制粘贴,再深究原理: 在shellcode_start的函数下面放一个shellcode_end: ```cpp void shellcode_end() { __debugbreak(); } ``` 然后main里面去掉之前的shellcode_start,生成模式从debug改成release 再然后复制如下代码: ```cpp const auto start_address = (uint32_t)shellcode_start; const auto shellcode_size = (uint32_t)shellcode_end - (uint32_t)start_address; for (size_t i = 0; i < shellcode_size; i++) { auto sig_code = ((unsigned char*)start_address)[i]; printf(",0x%02X", sig_code); } ``` 然后 最重要的来了 我们的VS会优化代码 + 增加security cookie 而前者会完全破坏掉shellcode,后者会让你的shellcode出现空指针异常(因为security cookie是全局变量,但是我们shellcode压根不能用) 所以你必须关掉它: 关闭security cookie: ![](https://key08.com/usr/uploads/2022/09/3839970673.png) 关闭优化 ![](https://key08.com/usr/uploads/2022/09/658076167.png) 请记住,这是配置release的,生成shellcode的时候不能用debug生成,否则你的函数地址会是错的,是debug的gate函数而不是真正的函数!!!!!! 运行后,我们得到了一段美丽的shellcode: ![](https://key08.com/usr/uploads/2022/09/414818771.png) 内存加载它看看: ```cpp char shellcode[] = { .... }; PVOID p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); memcpy(p, shellcode, sizeof(shellcode)); typedef void(__stdcall* door_code) (); door_code run_shellcode = (door_code)p; run_shellcode(); ``` ![](https://key08.com/usr/uploads/2022/09/3274308432.png) 恭喜你,shellcode的hello world就完成了 ### 问题环节 1. 为什么shellcode中不能用全局变量、API函数 已经说过了,因为ALSR加上因为你不是正常的pe,正常的PE这些API函数会通过导出表得到真正的地址,你用API函数的只是你编译那会的API地址,重启程序就没了.也就是shellcode第一次能跑,第二次就炸的问题,所以才要大费周章的重定位 2. 为什么shellcode中的字符串会是 `char str3[] = { 'U','S','E','R','3','2','.','d','l','l','\0' }`; 这种而不是直接的"user32.dll" 因为字符串也会当成全局变量存到rdata段,你是shellcode没办法访问. ### 完整代码 ```cpp // 韭菜之家_day1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。 // #include #include #define DEREF( name )*(UINT_PTR *)(name) #define DEREF_64( name )*(unsigned __int64 *)(name) #define DEREF_32( name )*(unsigned long *)(name) #define DEREF_16( name )*(unsigned short *)(name) #define DEREF_8( name )*(UCHAR *)(name) typedef struct _UNICODE_STR { USHORT Length; USHORT MaximumLength; PWSTR pBuffer; } UNICODE_STR, * PUNICODE_STR; // WinDbg> dt -v ntdll!_PEB_LDR_DATA 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; // WinDbg> dt -v ntdll!_PEB_FREE_BLOCK typedef struct _PEB_FREE_BLOCK // 2 elements, 0x8 bytes { struct _PEB_FREE_BLOCK* pNext; DWORD dwSize; } PEB_FREE_BLOCK, * PPEB_FREE_BLOCK; // struct _PEB is defined in Winternl.h but it is incomplete // WinDbg> dt -v ntdll!_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; typedef struct _LDR_DATA_TABLE_ENTRY { //LIST_ENTRY InLoadOrderLinks; // As we search from PPEB_LDR_DATA->InMemoryOrderModuleList we dont use the first 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; void shellcode_start() { //代码不用理解,可以直接复制粘贴. //0.读取fs,找到peb uint64_t base_address = 0; __asm { mov eax, fs: [30h] mov dword ptr[base_address], eax } unsigned short m_counter; uint64_t ldr_table; uint64_t dll_name; uint64_t hash_name; uint64_t kenrle32_base = 0; base_address = (uint64_t)((_PEB*)base_address)->pLdr; ldr_table = (uint64_t)((PPEB_LDR_DATA)base_address)->InMemoryOrderModuleList.Flink; //1. 通过peb里面的LDR找到kernel32的地址 while (ldr_table) { dll_name = (uint64_t)((PLDR_DATA_TABLE_ENTRY)ldr_table)->BaseDllName.pBuffer; m_counter = ((PLDR_DATA_TABLE_ENTRY)ldr_table)->BaseDllName.Length; hash_name = 0; do { hash_name = _rotr((unsigned long)hash_name, 13); if (*((unsigned char*)dll_name) >= 'a') hash_name += *((unsigned char*)dll_name) - 0x20; else hash_name += *((unsigned char*)dll_name); dll_name++; } while (--m_counter); //hash name其实是基于dll_name的因为我们也不想取到其他的莫名其妙的东西,做个简单地hash会准确很多 //如果你不想用hashname,那么你可以printf("%wZ",dll_name);观察一下dll name自己想一下新的思路 if ((unsigned long)hash_name == 0x6A4ABC5B) { //这就是kernel.dll的地址了 kenrle32_base = (uint64_t)((PLDR_DATA_TABLE_ENTRY)ldr_table)->DllBase; break; } ldr_table = DEREF(ldr_table); if (kenrle32_base != 0) { //找到了退出 break; } } if (kenrle32_base == 0) { __debugbreak(); } typedef HMODULE(WINAPI* GetProcAddressT)(_In_ HMODULE hModule, _In_ LPCSTR lpProcName); typedef HMODULE(WINAPI* LoadLibraryAT)(_In_ LPCSTR lpLibFileName); GetProcAddressT fnGetProcAddress = NULL; LoadLibraryAT fnLoadlibrary = NULL; //别在意这边的大小写驼峰混乱,因为是两套代码拼接的,懒得改了.... UINT_PTR uiAddressArray = NULL; UINT_PTR uiNameArray = NULL; UINT_PTR uiNameOrdinals = NULL; PIMAGE_NT_HEADERS32 pNtHeaders32 = NULL; PIMAGE_NT_HEADERS64 pNtHeaders64 = NULL; PIMAGE_DATA_DIRECTORY pDataDirectory = NULL; PIMAGE_EXPORT_DIRECTORY pExportDirectory = NULL; // 解析PE头 pNtHeaders32 = (PIMAGE_NT_HEADERS32)(kenrle32_base + ((PIMAGE_DOS_HEADER)kenrle32_base)->e_lfanew); pNtHeaders64 = (PIMAGE_NT_HEADERS64)(kenrle32_base + ((PIMAGE_DOS_HEADER)kenrle32_base)->e_lfanew); // 拿到导出表 pDataDirectory = (PIMAGE_DATA_DIRECTORY)&pNtHeaders32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]; // 遍历导出表 pExportDirectory = (PIMAGE_EXPORT_DIRECTORY)(kenrle32_base + pDataDirectory->VirtualAddress); uiAddressArray = (kenrle32_base + pExportDirectory->AddressOfFunctions); uiNameArray = (kenrle32_base + pExportDirectory->AddressOfNames); uiNameOrdinals = (kenrle32_base + pExportDirectory->AddressOfNameOrdinals); unsigned long dwCounter = pExportDirectory->NumberOfNames; char str1[] = { 'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', '\0' }; char str2[] = { 'L','o','a','d','L','i','b','r','a','r','y','A','\0' }; while (dwCounter--) { char* cpExportedFunctionName = (char*)(kenrle32_base + DEREF_32(uiNameArray)); char* matchPtr = &str1[0]; int ret = 0; while (!(ret = *cpExportedFunctionName - *matchPtr) && *cpExportedFunctionName) { cpExportedFunctionName++; matchPtr++; } if (ret == 0) { uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(unsigned long)); fnGetProcAddress = (GetProcAddressT)(kenrle32_base + DEREF_32(uiAddressArray)); } else { cpExportedFunctionName = (char*)(kenrle32_base + DEREF_32(uiNameArray)); char* matchPtr = &str2[0]; ret = 0; while (!(ret = *cpExportedFunctionName - *matchPtr) && *cpExportedFunctionName) { cpExportedFunctionName++; matchPtr++; } if (ret == 0) { uiAddressArray = (kenrle32_base + pExportDirectory->AddressOfFunctions); uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(unsigned long)); fnLoadlibrary = (LoadLibraryAT)(kenrle32_base + DEREF_32(uiAddressArray)); } } if (fnLoadlibrary && fnGetProcAddress) { break; } uiNameArray += sizeof(unsigned long); uiNameOrdinals += sizeof(unsigned short); } if (fnLoadlibrary == NULL || fnGetProcAddress == NULL) { __debugbreak(); } char str3[] = { 'U','S','E','R','3','2','.','d','l','l','\0' }; char str4[] = { 'M','e','s','s','a','g','e','B','o','x','A','\0' }; char str5[] = { 'h','e','l','l','o',' ','w','o','r','l','d','\0' }; typedef int (WINAPI* MessageBoxAT)(_In_opt_ HWND hWnd,_In_opt_ LPCSTR lpText,_In_opt_ LPCSTR lpCaption,_In_ UINT uType); MessageBoxAT pMessageBoxA = (MessageBoxAT)fnGetProcAddress(fnLoadlibrary(str3), str4); if (pMessageBoxA == NULL) { __debugbreak(); } pMessageBoxA(NULL, str5, NULL ,NULL); __debugbreak(); } void shellcode_end() { __debugbreak(); } int main() { /* const auto start_address = (uint32_t)shellcode_start; const auto shellcode_size = (uint32_t)shellcode_end - (uint32_t)start_address; for (size_t i = 0; i < shellcode_size; i++) { auto sig_code = ((unsigned char*)start_address)[i]; printf(",0x%02X", sig_code); } char shellcode[] = { ... }; PVOID p = VirtualAlloc(NULL, sizeof(shellcode), MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); memcpy(p, shellcode, sizeof(shellcode)); typedef void(__stdcall* door_code) (); door_code run_shellcode = (door_code)p; run_shellcode(); */ system("pause"); } ``` 本文由 huoji 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。 点赞 0
还不快抢沙发