Add SSDT dump using Exception directory

This commit is contained in:
Ben Mz
2023-05-25 17:27:36 +02:00
parent 078ec14528
commit c0957ca027
2 changed files with 183 additions and 20 deletions
+24 -20
View File
@@ -9,26 +9,29 @@ My experiments in weaponizing [Nim](https://nim-lang.org/) for implant developme
## Table of Contents
- [OffensiveNim](#offensivenim)
* [Why Nim?](#why-nim)
* [Examples in this repo](#examples-in-this-repo-that-work)
* [Compiling the examples](#compiling-the-examples-in-this-repo)
+ [Easy Way (Recommended)](#easy-way-recommended)
+ [Hard Way (For the Bold)](#hard-way-for-the-bold)
* [Cross Compiling](#cross-compiling)
* [Interfacing with C/C++](#interfacing-with-cc)
* [Creating Windows DLLs with an exported DllMain](#creating-windows-dlls-with-an-exported-dllmain)
* [Optimizing executables for size](#optimizing-executables-for-size)
* [Reflectively Loading Nim Executables](#reflectively-loading-nim-executables)
* [Executable size difference with the Winim Library](#executable-size-difference-when-using-the-winim-library-vs-without)
* [Opsec Considirations](#opsec-considerations)
* [Converting C Code to Nim](#converting-c-code-to-nim)
* [Language Bridges](#language-bridges)
* [Debugging](#debugging)
* [Setting up a dev environment](#setting-up-a-dev-environment)
* [Pitfalls I found myself falling into](#pitfalls-i-found-myself-falling-into)
* [Interesting Nim Libraries](#interesting-nim-libraries)
* [Nim for Implant Dev Links](#nim-for-implant-dev-links)
* [Contributors](#contributors)
- [Table of Contents](#table-of-contents)
- [Why Nim?](#why-nim)
- [Examples in this repo that work](#examples-in-this-repo-that-work)
- [Examples that are a WIP](#examples-that-are-a-wip)
- [Compiling the examples in this repo](#compiling-the-examples-in-this-repo)
- [Easy Way (Recommended)](#easy-way-recommended)
- [Hard way (For the bold)](#hard-way-for-the-bold)
- [Cross Compiling](#cross-compiling)
- [Interfacing with C/C++](#interfacing-with-cc)
- [Creating Windows DLLs with an exported `DllMain`](#creating-windows-dlls-with-an-exported-dllmain)
- [Creating XLLs](#creating-xlls)
- [Optimizing executables for size](#optimizing-executables-for-size)
- [Reflectively Loading Nim Executables](#reflectively-loading-nim-executables)
- [Executable size difference when using the Winim library vs without](#executable-size-difference-when-using-the-winim-library-vs-without)
- [Opsec Considerations](#opsec-considerations)
- [Converting C code to Nim](#converting-c-code-to-nim)
- [Language Bridges](#language-bridges)
- [Debugging](#debugging)
- [Setting up a dev environment](#setting-up-a-dev-environment)
- [Pitfalls I found myself falling into](#pitfalls-i-found-myself-falling-into)
- [Interesting Nim libraries](#interesting-nim-libraries)
- [Nim for implant dev links](#nim-for-implant-dev-links)
- [Contributors](#contributors)
## Why Nim?
@@ -68,6 +71,7 @@ My experiments in weaponizing [Nim](https://nim-lang.org/) for implant developme
| [shellcode_bin.nim](../master/src/shellcode_bin.nim) | Creates a suspended process and injects shellcode with `VirtualAllocEx`/`CreateRemoteThread`. Also demonstrates the usage of compile time definitions to detect arch, os etc..|
| [shellcode_fiber.nim](../master/src/shellcode_fiber.nim) | Shellcode execution via fibers |
| [shellcode_inline_asm_bin.nim](../master/src/shellcode_inline_asm_bin.nim) | Executes shellcode using inline assembly |
| [ssdt_dump.nim](../master/src/ssdt_dump.nim) | Simple SSDT retrieval using runtime function table from exception directory. Technique inspired from [MDSEC](https://www.mdsec.co.uk/2022/04/resolving-system-service-numbers-using-the-exception-directory/) article |
| [syscalls_bin.nim](../master/src/syscalls_bin.nim) | Shows how to make direct system calls |
| [execute_powershell_bin.nim](../master/src/execute_powershell_bin.nim) | Hosts the CLR & executes PowerShell through an un-managed runspace |
| [passfilter_lib.nim](../master/src/passfilter_lib.nim) | Log password changes to a file by (ab)using a password complexity filter |
+159
View File
@@ -0,0 +1,159 @@
when not defined(windows):
{.error: "This module is only supported on Windows".}
# Import required libs
import json
import tables
import strutils
import strformat
# Import external libs
import ptr_math
import winim
type
Syscall = object
ssn: int
name: string
address: int64
IMAGE_RUNTIME_FUNCTION_ENTRY_UNION {.pure, union.} = object
UnwindInfoAddress: DWORD
UnwindData: DWORD
IMAGE_RUNTIME_FUNCTION_ENTRY {.pure.} = object
BeginAddress: DWORD
EndAddress: DWORD
u1: IMAGE_RUNTIME_FUNCTION_ENTRY_UNION
PIMAGE_RUNTIME_FUNCTION_ENTRY = ptr IMAGE_RUNTIME_FUNCTION_ENTRY
## utils from https://github.com/khchen/memlib/blob/master/memlib.nim
template `++`[T](p: var ptr T) =
## syntax sugar for pointer increment
p = cast[ptr T](p[int] +% sizeof(T))
proc `[]`[T](x: T, U: typedesc): U {.inline.} =
## syntax sugar for cast
when sizeof(U) > sizeof(x):
when sizeof(x) == 1: cast[U](cast[uint8](x).uint64)
elif sizeof(x) == 2: cast[U](cast[uint16](x).uint64)
elif sizeof(x) == 4: cast[U](cast[uint32](x).uint64)
else: cast[U](cast[uint64](x))
else:
cast[U](x)
proc `{}`[T](x: T, U: typedesc): U {.inline.} =
## syntax sugar for zero extends cast
when sizeof(x) == 1: x[uint8][U]
elif sizeof(x) == 2: x[uint16][U]
elif sizeof(x) == 4: x[uint32][U]
elif sizeof(x) == 8: x[uint64][U]
else: {.fatal.}
proc `{}`[T](p: T, x: SomeInteger): T {.inline.} =
## syntax sugar for pointer (or any other type) arithmetics
(p[int] +% x{int})[T]
##
# Use RunTime Function table from exception directory to gather SSN: https://www.mdsec.co.uk/2022/04/resolving-system-service-numbers-using-the-exception-directory/
iterator syscalls(codeBase: pointer, exports: PIMAGE_EXPORT_DIRECTORY, rtf: PIMAGE_RUNTIME_FUNCTION_ENTRY): (string, int, DWORD) =
var
i: int = 0
ssn: int = 0
# Loop runtime function table
while rtf[i].BeginAddress:
let current = rtf[i].BeginAddress
# Reset pointers
var
nameRef = codeBase{exports.AddressOfNames}[PDWORD]
funcRef = codeBase{exports.AddressOfFunctions}[PDWORD]
ordinal = codeBase{exports.AddressOfNameOrdinals}[PWORD]
# Search Begin Address in Export Table
for j in 0 ..< exports.NumberOfFunctions:
let
syscall = $(codeBase{nameRef[]}[LPCSTR])
offset = funcRef[ordinal[j][int]]
# Check offset with current function, ensure this is a syscall
if (offset == current) and syscall.startsWith("Zw"):
yield (syscall, ssn, offset)
# Increase syscall number
ssn += 1
break
++nameRef
# Go next address
i += 1
proc lpwstrc(bytes: array[MAX_PATH, WCHAR]): string =
result = newString(bytes.len)
for i in bytes:
result &= cast[char](i)
result = strip(result, chars = {cast[char](0)})
proc listSyscalls(codeBase: pointer): seq[Syscall] =
# Extract headers
let dosHeader = cast[PIMAGE_DOS_HEADER](codeBase)
let ntHeader = cast[PIMAGE_NT_HEADERS](cast[DWORD_PTR](codeBase) + dosHeader.e_lfanew)
# Get export table
let directory = ntHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]
let exports = codeBase{directory.VirtualAddress}[PIMAGE_EXPORT_DIRECTORY]
# Get runtime functions table
let dirExcept = ntHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION]
let rtf = codeBase{dirExcept.VirtualAddress}[PIMAGE_RUNTIME_FUNCTION_ENTRY]
# Resolve ssn & offset
for name, ssn, offset in codeBase.syscalls(exports, rtf):
var entry = Syscall(name: name, ssn: ssn)
# Calculate syscall address
entry.address = cast[int64](codeBase + offset)
result.add(entry)
return result
proc getInfos(me32: MODULEENTRY32): (string, string) =
let
modPath = lpwstrc(me32.szExePath)
infos = modPath.split("\\")
modName = infos[^1].toLower()
modAddr = toHex(cast[int64](me32.modBaseAddr))
return (modName, modAddr)
proc dumpSSDT(pid: DWORD): seq[Syscall] =
# Create module snapshot
let hModule = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid)
defer: CloseHandle(hModule)
# Store process handle
let handle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid)
var
me32: MODULEENTRY32
mi: MODULEINFO
me32.dwSize = cast[DWORD](sizeof(MODULEENTRY32))
# SKip to ntdll.dll
hModule.Module32First(addr me32)
hModule.Module32Next(addr me32)
# Infos about dll
let (modName, modAddr) = me32.getInfos()
echo &"[+] {modName} -> loaded @ 0x{modAddr}"
handle.GetModuleInformation(me32.hModule, addr mi, cast[DWORD](sizeof(mi)))
return listSyscalls(mi.lpBaseOfDll)
when isMainModule:
# Could set arg parse to check different process
let pid = GetCurrentProcessId()
for entry in dumpSSDT(pid):
# You have now everything to rebuild syscall stub...
echo &"\t. 0x{entry.address.toHex()}\t{entry.ssn}\t{entry.name}"