直接与间接 Syscall
背景
直接 Syscall
间接 Syscall
Hell's Gate
https://alice.climent-pommeret.red/posts/direct-syscalls-hells-halos-syswhispers2/
if (*((PBYTE)pFunctionAddress + cw) == 0x4c
&& *((PBYTE)pFunctionAddress + 1 + cw) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + cw) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + cw) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + cw) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + cw) == 0x00) {
BYTE high = *((PBYTE)pFunctionAddress + 5 + cw);
BYTE low = *((PBYTE)pFunctionAddress + 4 + cw);
pVxTableEntry->wSystemCall = (high << 8) | low;
break;
}
// Search the pattern mov r10,rcx mov eax
if (*((PBYTE)pFunctionAddress + cw) == 0x4c
&& *((PBYTE)pFunctionAddress + 1 + cw) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + cw) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + cw) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + cw) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + cw) == 0x00)
Halo's Gate
int GoUp -32;
int GoDown 32;
// If the first instruction of the syscall is a an inconditional jump (aka it's hooked)
if (*((PBYTE)pFunctionAddress) == 0xe9) {
// Search beginning pattern of syscall stub through 500 function up and down from our location
for (WORD index = 1; index <= 500; index++) {
// Search the begining of a syscall stub in the next function down
if (*((PBYTE)pFunctionAddress + index * GoDown) == 0x4c
&& *((PBYTE)pFunctionAddress + 1 + index * GoDown) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + index * GoDown) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + index * GoDown) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + index * GoDown) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + index * GoDown) == 0x00) {
BYTE high = *((PBYTE)pFunctionAddress + 5 + index * GoDown);
BYTE low = *((PBYTE)pFunctionAddress + 4 + index * GoDown);
// substract the index from the current syscall identifier to find the one of our target function
pVxTableEntry->wSystemCall = (high << 8) | low - index;
return TRUE;
}
// Search the begining of a syscall stub in the next function down
if (*((PBYTE)pFunctionAddress + index * GoUp) == 0x4c
&& *((PBYTE)pFunctionAddress + 1 + index * GoUp) == 0x8b
&& *((PBYTE)pFunctionAddress + 2 + index * GoUp) == 0xd1
&& *((PBYTE)pFunctionAddress + 3 + index * GoUp) == 0xb8
&& *((PBYTE)pFunctionAddress + 6 + index * GoUp) == 0x00
&& *((PBYTE)pFunctionAddress + 7 + index * GoUp) == 0x00) {
BYTE high = *((PBYTE)pFunctionAddress + 5 + index * GoUp);
BYTE low = *((PBYTE)pFunctionAddress + 4 + index * GoUp);
// substract the index from the current syscall identifier to find the one of our target function
pVxTableEntry->wSystemCall = (high << 8) | low + index;
return TRUE;
}
}
FreshyCalls
//This function retrieve the RVA and name of "Nt" functions and put them in the sorted map
//
//We go trough the functions names in the Export Directory
for (size_t i = 0; i < export_dir->NumberOfNames; i++) {
function_name = reinterpret_cast<const char *>(ntdll_base + names_table[i]);
// If the name of the function start with "Nt" and don't start with "Ntdll"
// we retrieve the info
if (function_name.rfind("Nt", 0) == 0 && function_name.rfind("Ntdll", 0) == std::string::npos) {
stub_ordinal = names_ordinals_table[i];
stub_address = ntdll_base + functions_table[stub_ordinal];
// We put the RVA as a key and the function name as the value.
// This is a sorted map.
// The elements are automatically sorted using the key value.
// This means that when all the Nt function will be loaded,
// the first element will be the Nt function with the lowest address
// and the last the one with the biggest address.
stub_map.insert({stub_address, function_name});
}
}
// `stub_map` is ordered from lowest to highest using the stub address. Syscalls numbers are
// assigned using this ordering too. The lowest stub address will be the stub with the lowest
// syscall number (0 in this case). We just need to iterate `stub_map` and iterate the syscall
// number on every iteration.
static inline void ExtractSyscallsNumbers() noexcept {
uint32_t syscall_no = 0;
// The stub_map filled previously in the code presented above
for (const auto &pair: stub_map) {
//Creation of a map associating function name and syscall identifier.
syscall_map.insert({pair.second, syscall_no});
syscall_no++;
}
};
SysWhisper2
DWORD i = 0;
/*
//For info. It's in the header file.
typedef struct _SW2_SYSCALL_ENTRY
{
DWORD Hash;
DWORD Address;
} SW2_SYSCALL_ENTRY, *PSW2_SYSCALL_ENTRY;
#define SW2_MAX_ENTRIES 500
*/
PSW2_SYSCALL_ENTRY Entries = SW2_SyscallList.Entries;
do
{
// Retrieve function name from the Dll.
PCHAR FunctionName = SW2_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]);
// Check if the function name starts with "Zw"
if (*(USHORT*)FunctionName == 'wZ')
{
// If yes, hash the name (for AV/EDR/Malware Analyst evasion reasons) and put it in an Entries element
Entries[i].Hash = SW2_HashSyscall(FunctionName);
// Put also the address of the function
Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]];
i++;
if (i == SW2_MAX_ENTRIES) break;
}
} while (--NumberOfNames);
// Save total number of system calls found.
SW2_SyscallList.Count = i;
// Sort the list by address in ascending order.
for (i = 0; i < SW2_SyscallList.Count - 1; i++)
{
for (DWORD j = 0; j < SW2_SyscallList.Count - i - 1; j++)
{
if (Entries[j].Address > Entries[j + 1].Address)
{
// Swap entries.
SW2_SYSCALL_ENTRY TempEntry;
TempEntry.Hash = Entries[j].Hash;
TempEntry.Address = Entries[j].Address;
Entries[j].Hash = Entries[j + 1].Hash;
Entries[j].Address = Entries[j + 1].Address;
Entries[j + 1].Hash = TempEntry.Hash;
Entries[j + 1].Address = TempEntry.Address;
}
}
}
return TRUE;
}
TartarusGate
https://trickster0.github.io/posts/Halo's-Gate-Evolves-to-Tartarus-Gate/