一线红队带你从O到无限进步
本文章所涉及所有内容均是大佬上课说讲的内容,其中做了一下基础的补充,高深的技术后面会慢慢在文章中出现
一线在职红队在线性感分享技术,从c语言开始基础带你慢慢进入终端对抗(免杀但是不止免杀)的大门。
紧接上文
通过PEB隐藏函数(包括loadlibrary和getprocaddress)
我们可以通过动态调用的方式隐藏我们想要的函数,但是我们用来加载dll的函数以及寻找函数地址的函数依旧存在,那我们可以通过什么方法去把这个也隐藏了呢,那么就可通过PEB结构体隐藏。
PEB
PEB(Process Environment Block,进程环境块)是Windows操作系统中一个非常重要的数据结构,它包含了进程的全局状态和环境信息。
定义与作用: PEB是一个数据结构,用于存储有关进程的信息,每个进程都有一个对应的PEB结构体。PEB提供了许多关于进程状态和环境的信息,它是用户模式和内核模式之间的一个关键接口。PEB包含进程的许多重要信息,例如环境变量、映像基地址(Image Base Address)、进程启动参数、加载的模块列表以及堆的管理信息。
结构如下:
typedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; PVOID Reserved3[2]; PPEB_LDR_DATA Ldr; PRTL_USER_PROCESS_PARAMETERS ProcessParameters; PVOID Reserved4[3]; PVOID AtlThunkSListPtr; PVOID Reserved5; ULONG Reserved6; PVOID Reserved7; ULONG Reserved8; ULONG AtlThunkSListPtr32; PVOID Reserved9[45]; BYTE Reserved10[96]; PPS_POST_PROCESS_INIT_ROUTINE PostProcessInitRoutine; BYTE Reserved11[128]; PVOID Reserved12[1]; ULONG SessionId;} PEB, *PPEB;
做了简单介绍我们之间看看如何使用(直接看代码)
下面就是通过直接构造代码自己实现getmoudlehandle和getprocaddress函数。
#include <iostream>#include<Windows.h>#include"peb.h"HMODULE GetPebModule1(wchar_t* p);FARPROCWINAPIMyOrdinalGetProcAddress(_In_ HMODULE hModule,_In_ LPCSTR lpProcName);//(定义自实现GetProcAddress(通过序号查找));typedef FARPROC(WINAPI *pGetProcAddress)(_In_ HMODULE hModule,_In_ LPCSTR lpProcName);//(定义自实现GetProcAddress(通过name查找));typedef HMODULE(WINAPI*pgetmoudlehandleA)(_In_opt_ LPCTSTR lpModuleName);//自实现getmoudlehandletypedef int(WINAPI*pMessageBoxW)(_In_opt_ HWND hWnd,_In_opt_ LPCWSTR lpText,_In_opt_ LPCWSTR lpCaption,_In_ UINT uType);//自实现MessageBox函数typedef int(WINAPI*pMessageBoxW)(_In_opt_ HWND hWnd,_In_opt_ LPCWSTR lpText,_In_opt_ LPCWSTR lpCaption,_In_ UINT uType);extern "C" void fun()//因为C++自带名称粉碎(我们无法将程序入口点设置在fun函数),所以此处将其改为c方式加载;{ wchar_t KERNEL32[] = { 'K','E','R','N','E','L','3','2','.','D','L','L',0 };//通过数组来存储需要查找的dll名; HMODULE Hkernel= GetPebModule1(KERNEL32);//调用自实现GetPebModule1传入我们需要的dll名(此函数有两种运行实现方式)char GetProcAddressstr[] getmoudlehandle= { 'G','e','t','P','r','o','c','A','d','d','r','e','s','s',0 };char getmoudlehandleAstr[] = { 'L','o','a','d','L','i','b','r','a','r','y','A',0 };pGetProcAddress GetProcAddressfun=(pGetProcAddress)MyOrdinalGetProcAddress(Hkernel, GetProcAddressstr);//在KERNEL32.dll中获取GetProcAdderss的函数地址pgetmoudlehandle pgetmoudlehandleAfun =(pgetmoudlehandleA)MyOrdinalGetProcAddress(Hkernel, getmoudlehandleAstr);//在KERNEL32.dll中获取getmoudlehandle的函数地址char user32str[] = { 'u','s','e','r','3','2','.','d','l','l',0 };HMODULE Huser32=pgetmoudlehandleAfun(user32str);//获取user32.dll的地址char Messageboxstr[] = { 'M','e','s','s','a','g','e','B','o','x','W',0 };pMessageBoxW MessageBoxWFUN=(pMessageBoxW)GetProcAddressfun(Huser32, Messageboxstr);//在user32.dll中获取MessageBoxw的函数地址MessageBoxWFUN(0, 0, 0, 0);//getmoudlehandle函数地址//getprocaddress函数地址}int strlen_mine(char* p){ int len = 0; for (size_t i = 0; p[i] != 0; i++) { len++; } return len;}int strlen_mine_w(wchar_t* p){ int len = 0; for (size_t i = 0; p[i] != 0; i++) { len++; } return len;}int strcmp_mine(char* p, char* q){ int len_p = strlen_mine(p); int len_q = strlen_mine(q); for (int i = 0; i < len_p && i < len_q; i++) { if (*(p + i) != *(q + i)) { return -1; } } return 0;}int strcmp_mine_w(wchar_t* p, wchar_t* q){ int len_p = strlen_mine_w(p); int len_q = strlen_mine_w(q); for (int i = 0; i < len_p && i < len_q; i++) { if (*(p + i) != *(q + i)) { return -1; } } return 0;}HMODULE getken32base()//第一次实现GetPedModulel的方法{ HMODULE hk32; __asm { mov eax, fs: [0x30]//PEB mov eax, [eax + 0xc]//peb_ldr_data mov eax, [eax + 0xc]//_List_entry mov eax, [eax]//ntdll mov eax, [eax]//ker32 mov eax, dword ptr[eax + 0x18]//ker32base mov hk32, eax } return hk32;}HMODULE GetPebModule1(wchar_t* p)//第二种实现GetPedModulel的方法{PPEB pPeb;//定义PEDPPEB_LDR_DATA pLdr;//定义LdrPLDR_DATA_TABLE_ENTRY pLdrData;//定义Ldr的获取结果//0048E000//0048E000__asm{MOV EAX, FS: [0x18]MOV EAX, [EAX + 0x30]MOV pPeb, EAX}//将PEB的地址给到pPebpLdr = pPeb->Ldr;//获取Ldr的地址pLdrData = (PLDR_DATA_TABLE_ENTRY)pLdr->InLoadOrderModuleList.Flink;//指向一个模块for (; (pLdr->InLoadOrderModuleList.Flink) != (pLdrData->InLoadOrderLinks.Flink); //通过判断指向下一个的模块的指针是否等于其本身,如果不是就继续循环(InLoadOrderLinks.Flink为结构体中指向下一个模块的指针){if (strcmp_mine_w(pLdrData->BaseDllName.Buffer, p) == 0)//判断当前模块的名字是否我们输入对策{return (HMODULE)pLdrData->DllBase;//如果是就返回当前模块的地址}pLdrData = (PLDR_DATA_TABLE_ENTRY)(pLdrData->InLoadOrderLinks.Flink);//如果不是就把下一个地址的模块地址赋值上来(相当于pLdrData++)}return NULL;}int fun(int i){return i + 1;}DWORD RAVTOVA(DWORD RVA, DWORD hModule){return RVA + hModule;}FARPROCWINAPIMyGetProcAddress(_In_ HMODULE hModule,_In_ LPCSTR lpProcName){PIMAGE_DOS_HEADER pIMAGE_DOS_HEADER = (PIMAGE_DOS_HEADER)hModule;PIMAGE_NT_HEADERS pIMAGE_NT_HEADERS = (PIMAGE_NT_HEADERS)(pIMAGE_DOS_HEADER->e_lfanew +(DWORD)hModule);//导出表PIMAGE_EXPORT_DIRECTORY pIMAGE_EXPORT_DIRECTORY = (PIMAGE_EXPORT_DIRECTORY)RAVTOVA((DWORD)pIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[0].VirtualAddress,(DWORD)hModule);//导出函数名称表DWORD* NameAddress = (DWORD*)RAVTOVA(pIMAGE_EXPORT_DIRECTORY->AddressOfNames, (DWORD)hModule);//导出函数名称序号表WORD* NameOrdinalAddress = (WORD*)RAVTOVA(pIMAGE_EXPORT_DIRECTORY->AddressOfNameOrdinals, (DWORD)hModule);//导出函数地址表DWORD* AddressFun = (DWORD*)RAVTOVA(pIMAGE_EXPORT_DIRECTORY->AddressOfFunctions, (DWORD)hModule);for (size_t i = 0; i < pIMAGE_EXPORT_DIRECTORY->NumberOfNames; i++){char* FunName = (char*)RAVTOVA(NameAddress[i], (DWORD)hModule);if (strcmp_mine(FunName, (char *)lpProcName) == 0){return (FARPROC)RAVTOVA(AddressFun[NameOrdinalAddress[i]], (DWORD)hModule);}//使用我们输入的函数name进行循环匹配//printf("%sn", FunName);}return NULL;}FARPROCWINAPIMyOrdinalGetProcAddress(_In_ HMODULE hModule,_In_ LPCSTR lpProcName){if ((DWORD)lpProcName >= 0xffff)//判断lpProcName是否为名字,如果>=0xffff就是name,反之折是序号{return MyGetProcAddress(hModule, lpProcName);//调用nameGetProcAdderss}else {PIMAGE_DOS_HEADER pIMAGE_DOS_HEADER = (PIMAGE_DOS_HEADER)hModule;PIMAGE_NT_HEADERS pIMAGE_NT_HEADERS = (PIMAGE_NT_HEADERS)(pIMAGE_DOS_HEADER->e_lfanew +(DWORD)hModule);//导出表PIMAGE_EXPORT_DIRECTORY pIMAGE_EXPORT_DIRECTORY = (PIMAGE_EXPORT_DIRECTORY)RAVTOVA((DWORD)pIMAGE_NT_HEADERS->OptionalHeader.DataDirectory[0].VirtualAddress,(DWORD)hModule);//导出地址表DWORD* AddressFun = (DWORD*)RAVTOVA(pIMAGE_EXPORT_DIRECTORY->AddressOfFunctions, (DWORD)hModule);DWORD dwBase = pIMAGE_EXPORT_DIRECTORY->Base;DWORD funAddress = RAVTOVA(AddressFun[(DWORD)lpProcName - dwBase], (DWORD)hModule);//输入的序号减去base获得真实位置return (FARPROC)funAddress;//返回查找的函数地址}return NULL;}
PEB.H#pragma once#include<Windows.h>typedef struct _UNICODE_STRING { USHORT Length; USHORT MaximumLength; PWSTR Buffer;} UNICODE_STRING, * PUNICODE_STRING;typedef struct _LDR_DATA_TABLE_ENTRY{ LIST_ENTRY InLoadOrderLinks; LIST_ENTRY InMemoryOrderLinks; LIST_ENTRY InInitializationOrderLinks; PVOID DllBase; PVOID EntryPoint; ULONG SizeOfImage; UNICODE_STRING FullDllName; UNICODE_STRING BaseDllName; ULONG Flags; USHORT LoadCount; USHORT TlsIndex; union { LIST_ENTRY HashLinks; struct { PVOID SectionPointer; ULONG CheckSum; }; }; ULONG TimeDateStamp;} LDR_DATA_TABLE_ENTRY, * PLDR_DATA_TABLE_ENTRY;typedef struct _PEB_LDR_DATA{ ULONG Length; BOOLEAN Initialized; PVOID SsHandle; LIST_ENTRY InLoadOrderModuleList; LIST_ENTRY InMemoryOrderModuleList; LIST_ENTRY InInitializationOrderModuleList;} PEB_LDR_DATA, * PPEB_LDR_DATA;typedef struct _PEB { BYTE Reserved1[2]; BYTE BeingDebugged; BYTE Reserved2[1]; PVOID Reserved3[2]; PPEB_LDR_DATA Ldr;}PEB, * PPEB;
上面的代码是PEB的定义,需要导入这个头文件。
可以直接使用该代码进行尝试
syscall
简单来说,syscall 是一种绕过 EDR 用户态 hook 的方式,它通过获取系统调用号,并构造 syscall stub 的汇编指令直接进入内核态 API 调用。我们通过动态调试器就会发现某厂商的EDR会hook掉我们常用API函数,这也导致我们在程序直接调用函数的时候会触发告警,他会将执行函数的时候跳转到他构造的函数里去,做一些检查。所以我们使用syscall这个方法直接进行系统调用。直接调用R0层,不再经过被hook了的dll。
这是我们写的asm文件的代码
.codesysntalloc procmov r10,rcxmov eax,18hsyscallretsysntalloc endpend
然后我们构造主要代码
#include <windows.h>#include "abc.asm"EXTERN_C NTSTATUS sysntalloc(HANDLE ProcessHandle,PVOID* BaseAddress,ULONG_PTR ZeroBits,PSIZE_T RegionSize,ULONG AllocationType,ULONG Protect);BOOL ExecShell() {size_t len = 0;DWORD status = 0;PVOID pAddress = NULL;status = sysntalloc((HANDLE)-1, &pAddress, 0, &len, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);if (status != 0) {return FALSE;}return TRUE;}int main() {ExecShell();}
通过这种方式进行调用,但是还有一个问题就是在asm文件中我们的代码里面带有调用号以及syscallret这种危险的指令,我们该怎么进一步处理呢,可以私信询问。
原文始发于微信公众号(泾弦安全):免杀对抗从0开始(六)
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论