├── .gitignore
├── 2024-10-03_20-49.png
├── Cargo.toml
├── readme.md
├── rust-toolchain.toml
└── src
├── lib.rs
└── main.rs
/.gitignore:
--------------------------------------------------------------------------------
1 | /target
2 | Cargo.lock
3 |
--------------------------------------------------------------------------------
/2024-10-03_20-49.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Teach2Breach/noldr/0536e635ac31d6fc5a6a528382c8545978a342ab/2024-10-03_20-49.png
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "noldr"
3 | version = "0.1.0"
4 | edition = "2021"
5 |
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7 |
8 | [lib]
9 | name = "noldr"
10 | path = "src/lib.rs"
11 |
12 | [profile.release]
13 | opt-level = "z" # Optimize for size.
14 | lto = true # Enable Link Time Optimization
15 | codegen-units = 1 # Reduce number of codegen units to increase optimizations.
16 | panic = "abort" # Abort on panic
17 | strip = true # Automatically strip symbols from the binary.
18 |
19 | [dependencies]
20 | memoffset = "0.9.1"
21 | litcrypt = "0.3.0"
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # noldr Library
2 |
3 | This Rust library provides low-level functionality for working with Windows Portable Executable (PE) files and dynamic-link libraries (DLLs). It offers a set of tools for interacting with the Windows process environment, loading DLLs, and retrieving function addresses.
4 |
5 | It was written to be used in a C2 implant for hiding API calls and limiting the number of dependencies in a DLL. There are no Windows API crates imported, not even for types.
6 |
7 | This library was written for a very specific use case in mind. If you want something more robust, check out Kudaes [DInvoke_rs](https://github.com/Kudaes/DInvoke_rs).
8 |
9 | ## Features
10 |
11 | - Retrieve the Thread Environment Block (TEB) and Process Environment Block (PEB)
12 | - Get the base address of loaded DLLs
13 | - Retrieve function addresses from DLLs
14 | - List all loaded DLLs in the current process
15 | - Load DLLs dynamically
16 | - Various Windows PE-related structures and types
17 |
18 | ## Usage
19 |
20 | Add this to your `Cargo.toml`:
21 |
22 | ```toml
23 | [dependencies]
24 | noldr = { git = "https://github.com/Teach2Breach/noldr.git", branch = "main" }
25 | ```
26 |
27 | There is an example of how to use the library in the `src/main.rs` file.
28 | **note** - You only need to load dlls that are not already loaded.
29 |
30 | ## Example Image
31 |
32 | 
33 |
34 | Please note that litcrypt is used to encrypt specific strings, so you will need to add that to your project as well and set a `LITCRYPT_ENCRYPT_KEY` environment variable. The value is arbitrary, but it must be set. If you encrypt the API names which you want to call, in the same way as shown in main.rs, then those strings will not be visible in the compiled program. It is highly recommended to use litcrypt.
35 |
36 |
--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | channel = "nightly"
--------------------------------------------------------------------------------
/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![allow(unused_assignments)]
2 | #![allow(unused_imports)]
3 | #![allow(non_camel_case_types)]
4 | #![allow(non_snake_case)]
5 |
6 | use std::arch::asm;
7 | use std::ffi::c_void;
8 |
9 | #[macro_use]
10 | extern crate litcrypt;
11 |
12 | use_litcrypt!();
13 |
14 | #[repr(C)]
15 | pub struct IMAGE_DATA_DIRECTORY {
16 | pub VirtualAddress: u32,
17 | pub Size: u32,
18 | }
19 | impl Copy for IMAGE_DATA_DIRECTORY {}
20 | impl Clone for IMAGE_DATA_DIRECTORY {
21 | fn clone(&self) -> Self {
22 | *self
23 | }
24 | }
25 |
26 | #[repr(transparent)]
27 | #[derive(Debug, Clone, Copy, PartialEq, Eq)]
28 | pub struct IMAGE_DLL_CHARACTERISTICS(pub u16);
29 |
30 | #[repr(transparent)]
31 | #[derive(PartialEq, Eq, Copy, Clone, Default)]
32 | pub struct IMAGE_OPTIONAL_HEADER_MAGIC(pub u16);
33 | impl TypeKind for IMAGE_OPTIONAL_HEADER_MAGIC {
34 | type TypeKind = CopyType;
35 | }
36 | impl core::fmt::Debug for IMAGE_OPTIONAL_HEADER_MAGIC {
37 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
38 | f.debug_tuple("IMAGE_OPTIONAL_HEADER_MAGIC")
39 | .field(&self.0)
40 | .finish()
41 | }
42 | }
43 |
44 | #[repr(transparent)]
45 | #[derive(PartialEq, Eq, Copy, Clone, Default)]
46 | pub struct IMAGE_SUBSYSTEM(pub u16);
47 | impl TypeKind for IMAGE_SUBSYSTEM {
48 | type TypeKind = CopyType;
49 | }
50 | impl core::fmt::Debug for IMAGE_SUBSYSTEM {
51 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
52 | f.debug_tuple("IMAGE_SUBSYSTEM").field(&self.0).finish()
53 | }
54 | }
55 |
56 | #[repr(C, packed(4))]
57 | pub struct IMAGE_OPTIONAL_HEADER64 {
58 | pub Magic: IMAGE_OPTIONAL_HEADER_MAGIC,
59 | pub MajorLinkerVersion: u8,
60 | pub MinorLinkerVersion: u8,
61 | pub SizeOfCode: u32,
62 | pub SizeOfInitializedData: u32,
63 | pub SizeOfUninitializedData: u32,
64 | pub AddressOfEntryPoint: u32,
65 | pub BaseOfCode: u32,
66 | pub ImageBase: u64,
67 | pub SectionAlignment: u32,
68 | pub FileAlignment: u32,
69 | pub MajorOperatingSystemVersion: u16,
70 | pub MinorOperatingSystemVersion: u16,
71 | pub MajorImageVersion: u16,
72 | pub MinorImageVersion: u16,
73 | pub MajorSubsystemVersion: u16,
74 | pub MinorSubsystemVersion: u16,
75 | pub Win32VersionValue: u32,
76 | pub SizeOfImage: u32,
77 | pub SizeOfHeaders: u32,
78 | pub CheckSum: u32,
79 | pub Subsystem: IMAGE_SUBSYSTEM,
80 | pub DllCharacteristics: IMAGE_DLL_CHARACTERISTICS,
81 | pub SizeOfStackReserve: u64,
82 | pub SizeOfStackCommit: u64,
83 | pub SizeOfHeapReserve: u64,
84 | pub SizeOfHeapCommit: u64,
85 | pub LoaderFlags: u32,
86 | pub NumberOfRvaAndSizes: u32,
87 | pub DataDirectory: [IMAGE_DATA_DIRECTORY; 16],
88 | }
89 | impl Copy for IMAGE_OPTIONAL_HEADER64 {}
90 | impl Clone for IMAGE_OPTIONAL_HEADER64 {
91 | fn clone(&self) -> Self {
92 | *self
93 | }
94 | }
95 | impl TypeKind for IMAGE_OPTIONAL_HEADER64 {
96 | type TypeKind = CopyType;
97 | }
98 | impl Default for IMAGE_OPTIONAL_HEADER64 {
99 | fn default() -> Self {
100 | unsafe { core::mem::zeroed() }
101 | }
102 | }
103 |
104 | #[repr(C)]
105 | pub struct IMAGE_NT_HEADERS64 {
106 | pub Signature: u32,
107 | pub FileHeader: IMAGE_FILE_HEADER,
108 | pub OptionalHeader: IMAGE_OPTIONAL_HEADER64,
109 | }
110 |
111 | impl Copy for IMAGE_NT_HEADERS64 {}
112 |
113 | impl Clone for IMAGE_NT_HEADERS64 {
114 | fn clone(&self) -> Self {
115 | *self
116 | }
117 | }
118 |
119 | impl TypeKind for IMAGE_NT_HEADERS64 {
120 | type TypeKind = CopyType;
121 | }
122 |
123 | impl Default for IMAGE_NT_HEADERS64 {
124 | fn default() -> Self {
125 | unsafe { core::mem::zeroed() }
126 | }
127 | }
128 |
129 | #[repr(C)]
130 | pub struct IMAGE_FILE_HEADER {
131 | pub Machine: IMAGE_FILE_MACHINE,
132 | pub NumberOfSections: u16,
133 | pub TimeDateStamp: u32,
134 | pub PointerToSymbolTable: u32,
135 | pub NumberOfSymbols: u32,
136 | pub SizeOfOptionalHeader: u16,
137 | pub Characteristics: IMAGE_FILE_CHARACTERISTICS,
138 | }
139 |
140 | #[repr(transparent)]
141 | #[derive(PartialEq, Eq, Copy, Clone, Default)]
142 | pub struct IMAGE_FILE_MACHINE(pub u16);
143 | impl TypeKind for IMAGE_FILE_MACHINE {
144 | type TypeKind = CopyType;
145 | }
146 | impl std::fmt::Debug for IMAGE_FILE_MACHINE {
147 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
148 | write!(f, "IMAGE_FILE_MACHINE({})", self.0)
149 | }
150 | }
151 |
152 | #[repr(transparent)]
153 | #[derive(PartialEq, Eq, Copy, Clone, Default)]
154 | pub struct IMAGE_FILE_CHARACTERISTICS(pub u16);
155 | impl TypeKind for IMAGE_FILE_CHARACTERISTICS {
156 | type TypeKind = CopyType;
157 | }
158 | impl std::fmt::Debug for IMAGE_FILE_CHARACTERISTICS {
159 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
160 | write!(f, "IMAGE_FILE_CHARACTERISTICS({})", self.0)
161 | }
162 | }
163 |
164 | impl Copy for IMAGE_FILE_HEADER {}
165 |
166 | impl Clone for IMAGE_FILE_HEADER {
167 | fn clone(&self) -> Self {
168 | *self
169 | }
170 | }
171 |
172 | impl core::fmt::Debug for IMAGE_FILE_HEADER {
173 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
174 | f.debug_struct("IMAGE_FILE_HEADER")
175 | .field("Machine", &self.Machine)
176 | .field("NumberOfSections", &self.NumberOfSections)
177 | .field("TimeDateStamp", &self.TimeDateStamp)
178 | .field("PointerToSymbolTable", &self.PointerToSymbolTable)
179 | .field("NumberOfSymbols", &self.NumberOfSymbols)
180 | .field("SizeOfOptionalHeader", &self.SizeOfOptionalHeader)
181 | .field("Characteristics", &self.Characteristics)
182 | .finish()
183 | }
184 | }
185 |
186 | impl TypeKind for IMAGE_FILE_HEADER {
187 | type TypeKind = CopyType;
188 | }
189 |
190 | impl PartialEq for IMAGE_FILE_HEADER {
191 | fn eq(&self, other: &Self) -> bool {
192 | self.Machine == other.Machine
193 | && self.NumberOfSections == other.NumberOfSections
194 | && self.TimeDateStamp == other.TimeDateStamp
195 | && self.PointerToSymbolTable == other.PointerToSymbolTable
196 | && self.NumberOfSymbols == other.NumberOfSymbols
197 | && self.SizeOfOptionalHeader == other.SizeOfOptionalHeader
198 | && self.Characteristics == other.Characteristics
199 | }
200 | }
201 |
202 | impl Eq for IMAGE_FILE_HEADER {}
203 |
204 | impl Default for IMAGE_FILE_HEADER {
205 | fn default() -> Self {
206 | unsafe { core::mem::zeroed() }
207 | }
208 | }
209 |
210 | #[repr(transparent)]
211 | #[derive(PartialEq, Eq)]
212 | pub struct HMODULE(pub isize);
213 | impl HMODULE {
214 | pub fn is_invalid(&self) -> bool {
215 | self.0 == 0
216 | }
217 | }
218 | impl Default for HMODULE {
219 | fn default() -> Self {
220 | unsafe { core::mem::zeroed() }
221 | }
222 | }
223 | impl Clone for HMODULE {
224 | fn clone(&self) -> Self {
225 | *self
226 | }
227 | }
228 | impl Copy for HMODULE {}
229 | impl core::fmt::Debug for HMODULE {
230 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
231 | f.debug_tuple("HMODULE").field(&self.0).finish()
232 | }
233 | }
234 | impl TypeKind for HMODULE {
235 | type TypeKind = CopyType;
236 | }
237 | /*
238 | impl windows_core::CanInto for HMODULE {}
239 | impl From for HINSTANCE {
240 | fn from(value: HMODULE) -> Self {
241 | Self(value.0)
242 | }
243 | }
244 | */
245 | pub type TYPEKIND = i32;
246 |
247 | #[doc(hidden)]
248 | pub trait TypeKind {
249 | type TypeKind;
250 | }
251 |
252 | #[doc(hidden)]
253 | pub struct InterfaceType;
254 |
255 | #[doc(hidden)]
256 | pub struct CloneType;
257 |
258 | #[doc(hidden)]
259 | pub struct CopyType;
260 |
261 | #[repr(C, packed(2))]
262 | pub struct IMAGE_DOS_HEADER {
263 | pub e_magic: u16,
264 | pub e_cblp: u16,
265 | pub e_cp: u16,
266 | pub e_crlc: u16,
267 | pub e_cparhdr: u16,
268 | pub e_minalloc: u16,
269 | pub e_maxalloc: u16,
270 | pub e_ss: u16,
271 | pub e_sp: u16,
272 | pub e_csum: u16,
273 | pub e_ip: u16,
274 | pub e_cs: u16,
275 | pub e_lfarlc: u16,
276 | pub e_ovno: u16,
277 | pub e_res: [u16; 4],
278 | pub e_oemid: u16,
279 | pub e_oeminfo: u16,
280 | pub e_res2: [u16; 10],
281 | pub e_lfanew: i32,
282 | }
283 | impl Copy for IMAGE_DOS_HEADER {}
284 | impl Clone for IMAGE_DOS_HEADER {
285 | fn clone(&self) -> Self {
286 | *self
287 | }
288 | }
289 | impl TypeKind for IMAGE_DOS_HEADER {
290 | type TypeKind = CopyType;
291 | }
292 | impl Default for IMAGE_DOS_HEADER {
293 | fn default() -> Self {
294 | unsafe { core::mem::zeroed() }
295 | }
296 | }
297 |
298 | #[repr(C)]
299 | pub struct IMAGE_EXPORT_DIRECTORY {
300 | pub Characteristics: u32,
301 | pub TimeDateStamp: u32,
302 | pub MajorVersion: u16,
303 | pub MinorVersion: u16,
304 | pub Name: u32,
305 | pub Base: u32,
306 | pub NumberOfFunctions: u32,
307 | pub NumberOfNames: u32,
308 | pub AddressOfFunctions: u32,
309 | pub AddressOfNames: u32,
310 | pub AddressOfNameOrdinals: u32,
311 | }
312 | #[repr(C)]
313 | pub struct LDR_DATA_TABLE_ENTRY {
314 | pub Reserved1: [*mut core::ffi::c_void; 2],
315 | pub InMemoryOrderLinks: LIST_ENTRY,
316 | pub Reserved2: [*mut core::ffi::c_void; 2],
317 | pub DllBase: *mut core::ffi::c_void,
318 | pub Reserved3: [*mut core::ffi::c_void; 2],
319 | pub FullDllName: UNICODE_STRING,
320 | pub Reserved4: [u8; 8],
321 | pub Reserved5: [*mut core::ffi::c_void; 3],
322 | pub Anonymous: LDR_DATA_TABLE_ENTRY_0,
323 | pub TimeDateStamp: u32,
324 | }
325 |
326 | #[repr(C)]
327 | pub struct UNICODE_STRING {
328 | pub Length: u16,
329 | pub MaximumLength: u16,
330 | pub Buffer: PWSTR,
331 | }
332 |
333 | pub type PWSTR = *mut u16;
334 |
335 | #[repr(C)]
336 | pub union LDR_DATA_TABLE_ENTRY_0 {
337 | pub CheckSum: u32,
338 | pub Reserved6: *mut c_void,
339 | }
340 |
341 | #[repr(C)]
342 | pub struct TEB {
343 | pub Reserved1: [*mut c_void; 12],
344 | pub ProcessEnvironmentBlock: *mut PEB,
345 | pub Reserved2: [*mut c_void; 399],
346 | pub Reserved3: [u8; 1952],
347 | pub TlsSlots: [*mut c_void; 64],
348 | pub Reserved4: [u8; 8],
349 | pub Reserved5: [*mut c_void; 26],
350 | pub ReservedForOle: *mut c_void,
351 | pub Reserved6: [*mut c_void; 4],
352 | pub TlsExpansionSlots: *mut c_void,
353 | }
354 |
355 | #[repr(C)]
356 | pub struct PEB {
357 | pub Reserved1: [u8; 2],
358 | pub BeingDebugged: u8,
359 | pub Reserved2: [u8; 1],
360 | pub Reserved3: [*mut c_void; 2],
361 | pub Ldr: *mut PEB_LDR_DATA,
362 | pub ProcessParameters: *mut RTL_USER_PROCESS_PARAMETERS,
363 | pub Reserved4: [*mut c_void; 3],
364 | pub AtlThunkSListPtr: *mut c_void,
365 | pub Reserved5: *mut c_void,
366 | pub Reserved6: u32,
367 | pub Reserved7: *mut c_void,
368 | pub Reserved8: u32,
369 | pub AtlThunkSListPtr32: u32,
370 | pub Reserved9: [*mut c_void; 45],
371 | pub Reserved10: [u8; 96],
372 | pub PostProcessInitRoutine: PPS_POST_PROCESS_INIT_ROUTINE,
373 | pub Reserved11: [u8; 128],
374 | pub Reserved12: [*mut c_void; 1],
375 | pub SessionId: u32,
376 | }
377 |
378 | #[repr(C)]
379 | pub struct PEB_LDR_DATA {
380 | pub Reserved1: [u8; 8],
381 | pub Reserved2: [*mut c_void; 3],
382 | pub InMemoryOrderModuleList: LIST_ENTRY,
383 | }
384 |
385 | #[repr(C)]
386 | pub struct RTL_USER_PROCESS_PARAMETERS {
387 | pub Reserved1: [u8; 16],
388 | pub Reserved2: [*mut c_void; 10],
389 | pub ImagePathName: UNICODE_STRING,
390 | pub CommandLine: UNICODE_STRING,
391 | }
392 |
393 | pub type PPS_POST_PROCESS_INIT_ROUTINE = Option;
394 |
395 | #[repr(C)]
396 | pub struct LIST_ENTRY {
397 | pub Flink: *mut LIST_ENTRY,
398 | pub Blink: *mut LIST_ENTRY,
399 | }
400 |
401 |
402 |
403 | //use windows::Win32::System::Diagnostics::Debug;
404 |
405 | #[macro_use]
406 | extern crate memoffset;
407 |
408 | macro_rules! container_of {
409 | ($ptr:expr, $type:ty, $field:ident) => {{
410 | (($ptr as usize) - offset_of!($type, $field)) as *const $type
411 | }};
412 | }
413 |
414 | // Add this type definition
415 | type IMAGE_NT_HEADERS = IMAGE_NT_HEADERS64;
416 |
417 | #[inline]
418 | pub fn get_teb() -> *const TEB {
419 | let teb: *const TEB;
420 | unsafe {
421 | #[cfg(target_arch = "x86_64")]
422 | asm!("mov {}, gs:[0x30]", out(reg) teb);
423 | #[cfg(target_arch = "x86")]
424 | asm!("mov {}, fs:[0x18]", out(reg) teb);
425 | }
426 | teb
427 | }
428 |
429 | type HANDLE = *mut c_void;
430 | //get the base address of an already loaded dll
431 | pub fn get_dll_address(dll_name: String, teb: *const TEB) -> Option<*const c_void> {
432 | let mut peb_address: *const PEB = std::ptr::null();
433 | let dll_name_lower = dll_name.to_lowercase(); // Convert the input dll_name to lowercase
434 | unsafe {
435 | if !teb.is_null() {
436 | peb_address = (*teb).ProcessEnvironmentBlock;
437 | let ldr_data = (*peb_address).Ldr;
438 | if !ldr_data.is_null() {
439 | let list_entry = (*ldr_data).InMemoryOrderModuleList.Flink;
440 | if !list_entry.is_null() {
441 | let mut current_entry =
442 | container_of!(list_entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
443 | loop {
444 | let dll_base = (*current_entry).DllBase;
445 | let dll_name_in_memory = std::slice::from_raw_parts(
446 | (*current_entry).FullDllName.Buffer,
447 | (*current_entry).FullDllName.Length as usize / 2,
448 | )
449 | .as_ptr();
450 | let dll_name_len = (*current_entry).FullDllName.Length as usize / 2;
451 |
452 | // Convert the DLL name to a Rust string and make it lowercase for case-insensitive comparison
453 | let dll_name_in_memory = String::from_utf16_lossy(
454 | std::slice::from_raw_parts(dll_name_in_memory, dll_name_len),
455 | )
456 | .to_lowercase();
457 |
458 | if dll_name_in_memory.ends_with(&dll_name_lower) {
459 | return Some(dll_base);
460 | }
461 |
462 | // Move to the next entry
463 | let next_entry = (*current_entry).InMemoryOrderLinks.Flink;
464 | if next_entry == list_entry {
465 | // We've looped back to the start of the list, so the DLL was not found
466 | break;
467 | }
468 | current_entry =
469 | container_of!(next_entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
470 | }
471 | }
472 | }
473 | }
474 | }
475 | None
476 | }
477 |
478 | //get the address of a function in a dll
479 | pub fn get_function_address(dll_base: *const c_void, function_name: &str) -> Option<*const c_void> {
480 | unsafe {
481 | let dos_header = &*(dll_base as *const IMAGE_DOS_HEADER);
482 | let nt_headers =
483 | &*((dll_base as usize + dos_header.e_lfanew as usize) as *const IMAGE_NT_HEADERS);
484 | let export_directory_rva = nt_headers.OptionalHeader.DataDirectory[0].VirtualAddress;
485 | let export_directory = &*((dll_base as usize + export_directory_rva as usize)
486 | as *const IMAGE_EXPORT_DIRECTORY);
487 |
488 | let names_rva = export_directory.AddressOfNames;
489 | let functions_rva = export_directory.AddressOfFunctions;
490 | let ordinals_rva = export_directory.AddressOfNameOrdinals;
491 |
492 | let names = std::slice::from_raw_parts(
493 | (dll_base as usize + names_rva as usize) as *const u32,
494 | export_directory.NumberOfNames as usize,
495 | );
496 | let ordinals = std::slice::from_raw_parts(
497 | (dll_base as usize + ordinals_rva as usize) as *const u16,
498 | export_directory.NumberOfNames as usize,
499 | );
500 |
501 | for i in 0..export_directory.NumberOfNames as usize {
502 | let name_ptr = (dll_base as usize + names[i] as usize) as *const u8;
503 | let name = std::ffi::CStr::from_ptr(name_ptr as *const i8)
504 | .to_str()
505 | .unwrap_or_default();
506 | if name == function_name {
507 | let ordinal = ordinals[i] as usize;
508 | let function_rva =
509 | *((dll_base as usize + functions_rva as usize) as *const u32).add(ordinal);
510 | return Some((dll_base as usize + function_rva as usize) as *const c_void);
511 | }
512 | }
513 | }
514 | None
515 | }
516 |
517 | // Function to get the current process handle
518 | pub fn get_current_process_handle(_peb: *const PEB) -> HANDLE {
519 | // NtCurrentProcess is a pseudo-handle that always represents the current process.
520 | // It's a special constant that doesn't need to be closed.
521 | const NT_CURRENT_PROCESS: HANDLE = -1isize as HANDLE;
522 |
523 | // Return the pseudo-handle for the current process
524 | NT_CURRENT_PROCESS
525 | }
526 |
527 | //use std::ffi::c_void;
528 |
529 | pub fn list_all_dlls(teb: *const TEB) -> Vec<(String, *mut c_void)> {
530 | let mut dll_list = Vec::new();
531 | let mut peb_address: *const PEB = std::ptr::null();
532 | unsafe {
533 | if !teb.is_null() {
534 | peb_address = (*teb).ProcessEnvironmentBlock;
535 | let ldr_data = (*peb_address).Ldr;
536 | if !ldr_data.is_null() {
537 | let list_entry = (*ldr_data).InMemoryOrderModuleList.Flink;
538 | if !list_entry.is_null() {
539 | let mut current_entry =
540 | container_of!(list_entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
541 | loop {
542 | let dll_base = (*current_entry).DllBase;
543 | let buffer = (*current_entry).FullDllName.Buffer;
544 | let length = (*current_entry).FullDllName.Length as usize / 2;
545 |
546 | // Skip if buffer is null or length is 0
547 | if !buffer.is_null() && length > 0 {
548 | // Ensure the buffer is properly aligned for u16
549 | if (buffer as usize) % std::mem::align_of::() == 0 {
550 | let dll_name_in_memory = std::slice::from_raw_parts(buffer, length);
551 | let dll_name = String::from_utf16_lossy(dll_name_in_memory);
552 | dll_list.push((dll_name, dll_base));
553 | }
554 | }
555 |
556 | // Move to the next entry
557 | let next_entry = (*current_entry).InMemoryOrderLinks.Flink;
558 | if next_entry == list_entry {
559 | // We've looped back to the start of the list
560 | break;
561 | }
562 | current_entry =
563 | container_of!(next_entry, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
564 | }
565 | }
566 | }
567 | }
568 | }
569 | dll_list
570 | }
571 |
572 |
573 | //for loading the dll and getting a handle to it
574 | pub fn load_dll(dll_name: &str, kernel32_base: *const c_void) -> HMODULE {
575 | unsafe {
576 | // Get the base address of kernel32.dll
577 | //let kernel32_base = get_dll_address("kernel32.dll".to_string(), get_teb()).unwrap();
578 |
579 | // Get the address of LoadLibraryA function
580 | let load_library_a = get_function_address(kernel32_base, &lc!("LoadLibraryA")).unwrap();
581 | let load_library_a: extern "system" fn(*const i8) -> HMODULE =
582 | std::mem::transmute(load_library_a);
583 |
584 | // Convert dll_name to a C-style string
585 | let c_dll_name = std::ffi::CString::new(dll_name).unwrap();
586 |
587 | // Call LoadLibraryA to get the handle
588 | load_library_a(c_dll_name.as_ptr())
589 | }
590 | }
591 |
--------------------------------------------------------------------------------
/src/main.rs:
--------------------------------------------------------------------------------
1 | //ignore irrefutable warning
2 | #![allow(irrefutable_let_patterns)]
3 |
4 | use std::ffi::{c_void, CString};
5 | use noldr::{get_dll_address, get_function_address, list_all_dlls, load_dll};
6 | use noldr::get_teb;
7 | use noldr::HMODULE;
8 |
9 | #[macro_use]
10 | extern crate litcrypt;
11 |
12 | use_litcrypt!();
13 |
14 | fn main() {
15 | // Get the Thread Environment Block (TEB)
16 | let teb = get_teb();
17 | println!("teb: {:?}", teb);
18 |
19 | // List all currently loaded DLLs
20 | let dlls = list_all_dlls(teb);
21 | println!("Loaded DLLs:");
22 | for dll in dlls {
23 | if let (name, _) = dll {
24 | // Filter and print only valid DLL names
25 | if !name.is_empty() && name != "\0" && name.to_lowercase().ends_with(".dll") {
26 | println!(" {}", name);
27 | }
28 | }
29 | }
30 |
31 | // Locate kernel32.dll as an example
32 | println!("locating kernel32.dll as an example");
33 | let dll_base_address = get_dll_address("kERnel32.DLL".to_string(), teb).unwrap();
34 | println!("kernel32.dll base address: {:?}", dll_base_address);
35 |
36 | //locate ntdll.dll
37 | println!("locating ntdll.dll as an example");
38 | let ntdll_base_address = get_dll_address("ntdll.dll".to_string(), teb).unwrap();
39 | println!("ntdll.dll base address: {:?}", ntdll_base_address);
40 |
41 | // Load user32.dll manually
42 | println!("loading user32.dll");
43 | let user32_handle = load_dll("user32.dll", dll_base_address);
44 | println!("user32.dll handle: {:?}", user32_handle);
45 |
46 | // Get the base address of user32.dll
47 | let user32_base_address = user32_handle.0 as usize;
48 | println!("user32.dll base address: 0x{:x}", user32_base_address);
49 |
50 | // List all loaded DLLs again to confirm user32.dll is now loaded
51 | let dlls = list_all_dlls(teb);
52 | println!("New loaded DLLs:");
53 | for dll in dlls {
54 | if let (name, _) = dll {
55 | if !name.is_empty() && name != "\0" && name.to_lowercase().ends_with(".dll") {
56 | println!(" {}", name);
57 | }
58 | }
59 | }
60 |
61 | // Check if user32.dll was successfully loaded
62 | if user32_handle != HMODULE::default() {
63 | println!("{}", &lc!("locating MessageBoxA in user32.dll as an example"));
64 |
65 | // Get the address of MessageBoxA function
66 | let message_box_a = unsafe {
67 | let function_address = get_function_address(user32_handle.0 as *mut _, &lc!("MessageBoxA")).unwrap();
68 | // Convert the function address to a callable function pointer
69 | std::mem::transmute::<_, extern "system" fn(*mut c_void, *const i8, *const i8, u32) -> i32>(function_address)
70 | };
71 |
72 | print!("{}", &lc!("MessageBoxA address: "));
73 | println!("{:?}", message_box_a as *const ());
74 |
75 | // Call MessageBoxA to display a message
76 | let title = CString::new("Example").unwrap();
77 | let message = CString::new("Hello from no-LDR technique!").unwrap();
78 | message_box_a(std::ptr::null_mut(), message.as_ptr(), title.as_ptr(), 0);
79 | } else {
80 | println!("Failed to load user32.dll");
81 | }
82 | }
--------------------------------------------------------------------------------