Skip to content
4 changes: 4 additions & 0 deletions common/ReflectiveDLLInjection.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
// OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//===============================================================================================//
// #ifdef ARKARI_OBFUSCATOR
// #pragma clang optimize off
// #pragma optimize("", off)
// #endif
#ifndef _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H
#define _REFLECTIVEDLLINJECTION_REFLECTIVEDLLINJECTION_H
//===============================================================================================//
Expand Down
40 changes: 21 additions & 19 deletions dll/src/DirectSyscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,39 +13,41 @@
#pragma GCC push_options
#pragma GCC optimize("O0")
#endif
// #ifdef ARKARI_OBFUSCATOR
// #pragma GCC push_options
// #pragma GCC optimize("O0")
// #pragma clang optimize off
// #endif

#pragma warning(disable : 4100) // Unreferenced parameter 'pSyscall' is intentionally handled by assembly.
NTSTATUS SyscallStub(Syscall *pSyscall, ...)
COMPILER_OPTIONS NTSTATUS SyscallStub(Syscall *pSyscall, DWORD dwNumberOfArgs, ULONG_PTR *lpArgs)
{
// This function acts as a bridge to the assembly trampoline. The first argument,
// pSyscall, is passed in the first argument register (rcx/x0/stack), and all
// subsequent arguments follow the standard C calling convention.
return DoSyscall();
return DoSyscall(pSyscall->pStub, pSyscall->dwSyscallNr, lpArgs, dwNumberOfArgs);
}

#pragma warning(default : 4100)

NTSTATUS rdiNtAllocateVirtualMemory(Syscall *pSyscall, HANDLE hProcess, PVOID *pBaseAddress, ULONG_PTR pZeroBits, PSIZE_T pRegionSize, ULONG ulAllocationType, ULONG ulProtect)
COMPILER_OPTIONS NTSTATUS rdiNtAllocateVirtualMemory(Syscall *pSyscall, HANDLE hProcess, PVOID *pBaseAddress, ULONG_PTR pZeroBits, PSIZE_T pRegionSize, ULONG ulAllocationType, ULONG ulProtect)
{
return SyscallStub(pSyscall, hProcess, pBaseAddress, pZeroBits, pRegionSize, ulAllocationType, ulProtect);
ULONG_PTR lpArgs[] = { (ULONG_PTR)hProcess, (ULONG_PTR)pBaseAddress, (ULONG_PTR)pZeroBits, (ULONG_PTR)pRegionSize, (ULONG_PTR)ulAllocationType, (ULONG_PTR)ulProtect };
return SyscallStub(pSyscall, sizeof(lpArgs) / sizeof(ULONG_PTR), (ULONG_PTR *)&lpArgs);
}
NTSTATUS rdiNtProtectVirtualMemory(Syscall *pSyscall, HANDLE hProcess, PVOID *pBaseAddress, PSIZE_T pNumberOfBytesToProtect, ULONG ulNewAccessProtection, PULONG ulOldAccessProtection)
COMPILER_OPTIONS NTSTATUS rdiNtProtectVirtualMemory(Syscall *pSyscall, HANDLE hProcess, PVOID *pBaseAddress, PSIZE_T pNumberOfBytesToProtect, ULONG ulNewAccessProtection, PULONG ulOldAccessProtection)
{
return SyscallStub(pSyscall, hProcess, pBaseAddress, pNumberOfBytesToProtect, ulNewAccessProtection, ulOldAccessProtection);
ULONG_PTR lpArgs[] = { (ULONG_PTR)hProcess, (ULONG_PTR)pBaseAddress, (ULONG_PTR)pNumberOfBytesToProtect, (ULONG_PTR)ulNewAccessProtection, (ULONG_PTR)ulOldAccessProtection };
return SyscallStub(pSyscall, sizeof(lpArgs) / sizeof(ULONG_PTR), (ULONG_PTR *)&lpArgs);
}
NTSTATUS rdiNtFlushInstructionCache(Syscall *pSyscall, HANDLE hProcess, PVOID *pBaseAddress, SIZE_T FlushSize)
COMPILER_OPTIONS NTSTATUS rdiNtFlushInstructionCache(Syscall *pSyscall, HANDLE hProcess, PVOID *pBaseAddress, SIZE_T FlushSize)
{
return SyscallStub(pSyscall, hProcess, pBaseAddress, FlushSize);
ULONG_PTR lpArgs[] = { (ULONG_PTR)hProcess, (ULONG_PTR)pBaseAddress, (ULONG_PTR)FlushSize };
return SyscallStub(pSyscall, sizeof(lpArgs) / sizeof(ULONG_PTR), (ULONG_PTR *)&lpArgs);
}
NTSTATUS rdiNtLockVirtualMemory(Syscall *pSyscall, HANDLE hProcess, PVOID *pBaseAddress, PSIZE_T NumberOfBytesToLock, ULONG MapType)
COMPILER_OPTIONS NTSTATUS rdiNtLockVirtualMemory(Syscall *pSyscall, HANDLE hProcess, PVOID *pBaseAddress, PSIZE_T NumberOfBytesToLock, ULONG MapType)
{
return SyscallStub(pSyscall, hProcess, pBaseAddress, NumberOfBytesToLock, MapType);
ULONG_PTR lpArgs[] = { (ULONG_PTR)hProcess, (ULONG_PTR)pBaseAddress, (ULONG_PTR)NumberOfBytesToLock, (ULONG_PTR)MapType };
return SyscallStub(pSyscall, sizeof(lpArgs) / sizeof(ULONG_PTR), (ULONG_PTR *)&lpArgs);
}

#ifdef __MINGW32__
#pragma GCC pop_options
#endif
#pragma optimize("g", on)

//===============================================================================================//
// This function resolves the necessary information for direct syscall invocation. It uses
// a hybrid strategy.
Expand All @@ -64,7 +66,7 @@ NTSTATUS rdiNtLockVirtualMemory(Syscall *pSyscall, HANDLE hProcess, PVOID *pBase
// exact opcode of a given function's 'svc' instruction, verifying its integrity.
// The "stub" we execute is the function address itself.
//===============================================================================================//
BOOL getSyscalls(PVOID pNtdllBase, Syscall *Syscalls[], DWORD dwSyscallSize)
COMPILER_OPTIONS BOOL getSyscalls(PVOID pNtdllBase, Syscall *Syscalls[], DWORD dwSyscallSize)
{
PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)pNtdllBase;
PIMAGE_NT_HEADERS pNtHdrs = (PIMAGE_NT_HEADERS)((PBYTE)pNtdllBase + pDosHdr->e_lfanew);
Expand Down
18 changes: 12 additions & 6 deletions dll/src/DirectSyscall.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@
#include <windows.h>
#include <intrin.h>

#ifdef ARKARI_OBFUSCATOR
#define COMPILER_OPTIONS __attribute((__annotate__(("-indbr -icall -indgv -cse -fla"))))
#else
#define COMPILER_OPTIONS
#endif

#ifdef _WIN64
#define SYS_STUB_SIZE 32
#else
Expand Down Expand Up @@ -200,13 +206,13 @@ typedef struct __PEB // 65 elements, 0x210 bytes

//===============================================================================================//

BOOL getSyscalls(PVOID pNtdllBase, Syscall* Syscalls[], DWORD dwNumberOfSyscalls);
extern NTSTATUS DoSyscall(VOID);
COMPILER_OPTIONS BOOL getSyscalls(PVOID pNtdllBase, Syscall* Syscalls[], DWORD dwNumberOfSyscalls);
extern COMPILER_OPTIONS NTSTATUS DoSyscall(VOID *fn, DWORD dwSyscallNr, ULONG_PTR *lpArgs, DWORD dwNumberOfArgs);

//
// Native API functions
//
NTSTATUS rdiNtAllocateVirtualMemory(Syscall* pSyscall, HANDLE hProcess, PVOID* pBaseAddress, ULONG_PTR pZeroBits, PSIZE_T pRegionSize, ULONG ulAllocationType, ULONG ulProtect);
NTSTATUS rdiNtProtectVirtualMemory(Syscall* pSyscall, HANDLE hProcess, PVOID* pBaseAddress, PSIZE_T pNumberOfBytesToProtect, ULONG ulNewAccessProtection, PULONG ulOldAccessProtection);
NTSTATUS rdiNtFlushInstructionCache(Syscall* pSyscall, HANDLE hProcess, PVOID* pBaseAddress, SIZE_T FlushSize);
NTSTATUS rdiNtLockVirtualMemory(Syscall* pSyscall, HANDLE hProcess, PVOID* pBaseAddress, PSIZE_T NumberOfBytesToLock, ULONG MapType);
COMPILER_OPTIONS NTSTATUS rdiNtAllocateVirtualMemory(Syscall* pSyscall, HANDLE hProcess, PVOID* pBaseAddress, ULONG_PTR pZeroBits, PSIZE_T pRegionSize, ULONG ulAllocationType, ULONG ulProtect);
COMPILER_OPTIONS NTSTATUS rdiNtProtectVirtualMemory(Syscall* pSyscall, HANDLE hProcess, PVOID* pBaseAddress, PSIZE_T pNumberOfBytesToProtect, ULONG ulNewAccessProtection, PULONG ulOldAccessProtection);
COMPILER_OPTIONS NTSTATUS rdiNtFlushInstructionCache(Syscall* pSyscall, HANDLE hProcess, PVOID* pBaseAddress, SIZE_T FlushSize);
COMPILER_OPTIONS NTSTATUS rdiNtLockVirtualMemory(Syscall* pSyscall, HANDLE hProcess, PVOID* pBaseAddress, PSIZE_T NumberOfBytesToLock, ULONG MapType);
73 changes: 37 additions & 36 deletions dll/src/GateTrampoline32.asm
Original file line number Diff line number Diff line change
@@ -1,3 +1,15 @@
;
; GateTrampoline32.asm
;
; DoSyscall function implementation for 32-bit Windows to perform system calls with arguments passed as an array.
; NTSTATUS DoSyscall(VOID *fn, DWORD dwSyscallNr, ULONG_PTR *lpArgs, DWORD dwNumberOfArgs);
;
; Authors:
; Christophe De La Fuente <christophe_delafuente[at]rapid7[dot]com> - Original implementation
; Muzaffer Umut ŞAHİN <mailatmayinlutfen[at]gmail[dot]com> - Argument as array modification
; Diego Ledda <diego_ledda[at]rapid7[dot]com> - Argument as array porting and cleanup
;

.686P
.model flat, C

Expand All @@ -6,43 +18,32 @@
OPTION LANGUAGE: C
DoSyscall PROC

mov eax, [esp+0Ch] ; get the pointer to Syscall
mov eax, [eax+4] ; get the number of arguments
lea eax, [4*eax] ; calculate the number of bytes needed to store the arguments
sub esp, eax ; make room on the stack for the arguments

push edi ; store edi on stack to be able to restore it later
push ebx ; store ebx on stack to be able to restore it later
push ecx ; store ecx on stack to be able to restore it later

mov edi, [esp+0Ch+eax] ; save the return address
mov ebx, [esp+18h+eax] ; get the pointer to the Syscall structure
mov ecx, [ebx+4] ; get the number of arguments (.dwNumberOfArgs)

mov [esp+0Ch], edi ; place the return address on the stack

test ecx, ecx ; check if we have arguments
jz _end ; we don't, jump directly to _end
xor eax, eax ; zero out eax, this will be the index
lea edi, [esp+0Ch+4*ecx] ; set the base pointer that will be used in loop

_loop:
mov edx, [edi+10h+4*eax] ; get the argument
mov [esp+10h+4*eax], edx ; store it to the correct location
inc eax ; increment the index
cmp eax, ecx ; check if we have more arguments to process
jl _loop ; loop back to process the next argument

_end:
mov eax, ebx ; save the pointer to the Syscall structure to eax

pop ecx ; restore ecx
pop ebx ; restore ebx
pop edi ; restore edi

push [eax+0Ch] ; push the syscall stub on the stack
mov eax, [eax+8] ; store the syscall number to eax
ret ; return to the stub
push esi ; store esi on stack to be able to restore it later
push edi ; store edi on stack to be able to restore it later
push ebp ; store ebp on stack to be able to restore it later
mov ebp, esp ; save the current stack pointer in ebp
mov edi, [ebp + 14h] ; move the function pointer (first argument) into edi
mov esi, [ebp + 18h] ; move the syscall number (second argument) into esi
mov ebx, [ebp + 1Ch] ; move the pointer to the arguments (third argument) into ebx
mov ecx, [ebp + 20h] ; move the number of arguments (fourth argument) into ecx
test ecx, ecx ; if no arguments, jump to _no_args
je _no_args
lea ebx, [ebx + ecx * 4 - 4] ; point ebx to the last argument
_push_args:
push [ebx] ; push the argument onto the stack
sub ebx, 4
dec ecx
jnz _push_args ; repeat until all arguments are pushed onto the stack
_no_args:
mov eax, esi ; move the syscall number into eax for the syscall
call edi ; call the syscall function pointer in edi
mov esp, ebp ; restore the original stack pointer from ebp
pop ebp ; restore ebp from stack
pop edi ; restore edi from stack
pop esi ; restore esi from stack
pop ebx ; restore ebx from stack
ret

DoSyscall ENDP

Expand Down
75 changes: 37 additions & 38 deletions dll/src/GateTrampoline32.s
Original file line number Diff line number Diff line change
@@ -1,45 +1,44 @@
#
# GateTrampoline32.s
#
# DoSyscall function implementation for 32-bit Windows to perform system calls with arguments passed as an array.
# NTSTATUS DoSyscall(VOID *fn, DWORD dwSyscallNr, ULONG_PTR *lpArgs, DWORD dwNumberOfArgs);
#
# Authors:
# Christophe De La Fuente <christophe_delafuente[at]rapid7[dot]com> - Original implementation
# Muzaffer Umut ŞAHİN <mailatmayinlutfen[at]gmail[dot]com> - Argument as array modification
# Diego Ledda <diego_ledda[at]rapid7[dot]com> - Argument as array porting and cleanup
#
.intel_syntax noprefix

.global _DoSyscall

.text
_DoSyscall:

mov eax, [esp+0x14] # get the pointer to Syscall
mov eax, [eax+4] # get the number of arguments
lea eax, [4*eax] # calculate the number of bytes needed to store the arguments
sub esp, eax # make room on the stack for the arguments

push edi # store edi on stack to be able to restore it later
push ebx # store ebx on stack to be able to restore it later
push ecx # store ecx on stack to be able to restore it later

mov edi, [esp+0x0C+eax] # save the return address
mov ebx, [esp+0x20+eax] # get the pointer to the Syscall structure
mov ecx, [ebx+4] # get the number of arguments (.dwNumberOfArgs)

mov [esp+0x0C], edi # place the return address on the stack

test ecx, ecx # check if we have arguments
jz _end # we don't, jump directly to _end
xor eax, eax # zero out eax, this will be the index
lea edi, [esp+0x0C+4*ecx] # set the base pointer that will be used in loop

_loop:
mov edx, [edi+0x18+4*eax] # get the argument
mov [esp+0x10+4*eax], edx # store it to the correct location
inc eax # increment the index
cmp eax, ecx # check if we have more arguments to process
jl _loop # loop back to process the next argument

_end:
mov eax, ebx # save the pointer to the Syscall structure to eax

pop ecx # restore ecx
pop ebx # restore ebx
pop edi # restore edi

push [eax+0x0C] # push the syscall stub on the stack
mov eax, [eax+8] # store the syscall number to eax
ret # return to the stub
push ebx # store ebx on stack to be able to restore it later
push esi # store esi on stack to be able to restore it later
push edi # store edi on stack to be able to restore it later
push ebp # store ebp on stack to be able to restore it later
mov ebp, esp # save the current stack pointer in ebp
mov edi, [ebp + 0x14] # move the function pointer (first argument) into edi
mov esi, [ebp + 0x18] # move the syscall number (second argument) into esi
mov ebx, [ebp + 0x1C] # move the pointer to the arguments (third argument) into ebx
mov ecx, [ebp + 0x20] # move the number of arguments (fourth argument) into ecx
test ecx, ecx # if no arguments, jump to _no_args
je _no_args
lea ebx, [ebx + ecx * 4 - 4] # point ebx to the last argument
_push_args:
push [ebx] # push the argument onto the stack
sub ebx, 4
dec ecx
jnz _push_args # repeat until all arguments are pushed onto the stack
_no_args:
mov eax, esi # move the syscall number into eax for the syscall
call edi # call the syscall function pointer in edi
mov esp, ebp # restore the original stack pointer from ebp
pop ebp # restore ebp from stack
pop edi # restore edi from stack
pop esi # restore esi from stack
pop ebx # restore ebx from stack
ret

Loading