Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
42 changes: 22 additions & 20 deletions dll/src/DirectSyscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,43 +9,45 @@
//===============================================================================================//

#pragma optimize("g", off)
#ifdef __MINGW32__
#ifdef __MINGW32__
#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, ULONG_PTR **lpArgs, DWORD dwNumberOfArgs)
{
// 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, &lpArgs, 6);
}
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, &lpArgs, 5);
}
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, &lpArgs, 3);
}
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, &lpArgs, 4);
}

#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 **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);
61 changes: 25 additions & 36 deletions dll/src/GateTrampoline32.asm
Original file line number Diff line number Diff line change
Expand Up @@ -6,43 +6,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
64 changes: 26 additions & 38 deletions dll/src/GateTrampoline32.s
Original file line number Diff line number Diff line change
Expand Up @@ -4,42 +4,30 @@

.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

88 changes: 52 additions & 36 deletions dll/src/GateTrampoline64.asm
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,58 @@ DoSyscall Proc
push r11 ; store r11 on stack to be able to restore it later
push r12 ; store r12 on stack to be able to restore it later
push r13 ; store r13 on stack to be able to restore it later

add rsp, 40h ; restore the stack pointer to the previous stack frame
mov r11, [rsp+10h] ; get the pointer to the Syscall structure that has been stored in the shadow space

mov r10, [r11+10h] ; store the syscall stub in r10. Note that the `.pStub` field is padded with 4 null bytes on x64.
mov [rsp], r10 ; place the stub address on the stack, which will be used as return address

mov rcx, rdx ; Arg1 is the pointer to the Syscall structure and we don't need it.
mov rdx, r8 ; We need to shift all the arguments to have the correct arguments for the syscall.
mov r8, r9 ; This meens, rdx move to rcx, r8 to rdx, r9 to r8 and first argument on the stack
mov r9, [rsp+30h] ; to r9.

; Now, if the syscall needs more than 4 arguments, we need to deal with arguments stored on the stack
xor r12, r12
mov r12d, dword ptr [r11+4] ; store the number of arguments in r12, which will be our counter
cmp r12, 4 ; we already processed 4 arguments, so, check if we have more
jle _end ; we have less than 4 arguments, jump directly to _end
sub r12, 4 ; adjust the argument counter
xor r13, r13 ; zero out r13, this will be the index

_loop:
mov r10, [rsp+38h+8*r13] ; get the argument
mov [rsp+30h+8*r13], r10 ; store it to the correct location
inc r13 ; increment the index
cmp r13, r12 ; check if we have more arguments to process
jl _loop ; loop back to process the next argument

_end:
mov r10, rcx ; store the first argument to r10, like the original syscall do
xor rax, rax ; zero out rax
mov eax, dword ptr [r11+8] ; store the syscall number to eax

mov r13, [rsp-40h] ; restore r13
mov r12, [rsp-38h] ; restore r12
mov r11, [rsp-30h] ; restore r11
ret ; return to the stub
push r14 ; store r14 on stack to be able to restore it later
push r15 ; store r15 on stack to be able to restore it later
mov r15, rsp ; save the current stack pointer in r15
mov r11, rcx ; move the function pointer (first argument) into r11
mov r14, rdx ; move the syscall number (second argument) into r14
mov r12, r8 ; move the pointer to the arguments (third argument) into r12
mov r13, r9 ; move the number of arguments (fourth argument) into r13
lea r12, [r12 + r13 * 8 - 8] ; point r12 to the last argument
cmp r13, 4
jle _setup_registers ; if there are 4 or fewer arguments, jump to setup_registers
_setup_stack:
push [r12] ; push the last argument onto the stack
dec r13
sub r12, 8
cmp r13, 4
jne _setup_stack ; repeat until all arguments are pushed onto the stack
_setup_registers:
cmp r13, 4
je _setup_4_registers ; if there are exactly 4 arguments, jump to setup_4_registers
cmp r13, 3
je _setup_3_registers ; if there are exactly 3 arguments, jump
cmp r13, 2
je _setup_2_registers ; if there are exactly 2 arguments, jump
cmp r13, 1
je _setup_1_register ; if there is exactly 1 argument, jump
jmp _no_args ; if there are no arguments, jump to no_args
_setup_4_registers:
mov r9, [r12]
sub r12, 8
_setup_3_registers:
mov r8, [r12]
sub r12, 8
_setup_2_registers:
mov rdx, [r12]
sub r12, 8
_setup_1_register:
mov rcx, [r12] ; move the first argument into rcx for the syscall
_no_args:
push r9 ; push r9 shadow stack
push r8 ; push r8 shadow stack
push rdx ; push rdx shadow stack
push rcx ; push rcx shadow stack
mov r10, rcx
mov rax, r14 ; move the syscall number into rax for the syscall
call r11 ; call the syscall function pointer in r11
mov rsp, r15 ; restore the original stack pointer from r15
pop r15 ; restore r15 from stack
pop r14 ; restore r14 from stack
pop r13 ; restore r13 from stack
pop r12 ; restore r12 from stack
pop r11 ; restore r11 from stack
ret

DoSyscall ENDP

Expand Down
Loading