Closed Path-17 closed 12 months ago
To follow up, I found a solution that I think confirms my suspicion.
The default rust toolchain uses Visual Studio build tools (MSVC), switching to the gnu build tools fixes the issues I was coming across.
Just run "rustup toolchain install stable-x86_64-pc-windows-gnu" then set it to your default with "rustup default stable-gnu".
Testing further on the MSVC toolchain, even the simplest println!("Hello world") doesn't work with pe2shc.exe / runshc64.exe.
I will test further with side by side with a debugger injecting into notepad.exe to see what the cause of the crash is!
hi @Path-17 ! Thank you for reporting. It is true that sometimes the compiler choice affects whether or not the executable is convertible without any issues. For example: https://github.com/hasherezade/pe_to_shellcode/issues/29
I will look into your particular case soon, and see what was the immediate cause.
@Path-17 : I did some quick tests today, and it seems to me that the problem lies in the TLS.
First I tried to load the runshc64.exe <shellcode_path>
under the debugger, and run it. There is an access violation:
This is at RVA = 8E80F
in the implant. Seeing it in IDA:
So it seems that the value couldn't be fetched from the TLS.
Another check I did, by tracing the original application vs the shellcodified version vs the original one with TLS removed. Those are the results (fragments of the tracelogs).
Shellcodified:
> 13377339000+d82;kernel32.GetCurrentThread
> 13377339000+d70;kernel32.GetModuleHandleA
> 13377310000+cb7;kernel32.GetProcAddress
GetProcAddress:
Arg[0] = ptr 0x00007ffc88c70000 -> {MZ\x90\x00\x03\x00\x00\x00}
Arg[1] = ptr 0x00000133773951a9 -> "SetThreadDescription"
> 13377310000+cdc;kernelbase.SetThreadDescription
SetThreadDescription:
Arg[0] = 0xfffffffffffffffe = 18446744073709551614
Arg[1] = ptr 0x0000013362bdbaa0 -> L"main"
> 13377288000+c96;kernel32.HeapFree
> 13377288000+c1b;ntdll.RtlAllocateHeap
> 1337733a000+8c5;vcruntime140.memmove
> 13377288000+c1b;ntdll.RtlAllocateHeap
> 13377288000+c96;kernel32.HeapFree
> 13377288000+c96;kernel32.HeapFree
> 1337730f000+d6f;kernel32.GetStdHandle
> 1337730f000+dd1;kernel32.GetConsoleMode
> 13377310000+208;kernel32.MultiByteToWideChar
> 13377310000+245;kernel32.WriteConsoleW
WriteConsoleW:
Arg[0] = 0x0000000000000058 = 88
Arg[1] = ptr 0x0000000fe21fd0e0 -> L"thread panicked while processing panic. aborting.
"
Original:
b9d82;kernel32.GetCurrentThread
b9d70;kernel32.GetModuleHandleA
90cb7;kernel32.GetProcAddress
GetProcAddress:
Arg[0] = ptr 0x00007ffc88c70000 -> {MZ\x90\x00\x03\x00\x00\x00}
Arg[1] = ptr 0x00007ff6a3ef51a9 -> "SetThreadDescription"
90cdc;kernelbase.SetThreadDescription
8c96;kernel32.HeapFree
8c1b;ntdll.RtlAllocateHeap
ba8c5;vcruntime140.memmove
8c1b;ntdll.RtlAllocateHeap
8c1b;ntdll.RtlAllocateHeap
23f4;kernel32.GetComputerNameExW
8c1b;ntdll.RtlAllocateHeap
240b;kernel32.GetComputerNameExW
8c1b;ntdll.RtlAllocateHeap
ba8c5;vcruntime140.memmove
Original with TLS directory removed:
b9d82;kernel32.GetCurrentThread
b9d70;kernel32.GetModuleHandleA
90cb7;kernel32.GetProcAddress
GetProcAddress:
Arg[0] = ptr 0x00007ffc88c70000 -> {MZ\x90\x00\x03\x00\x00\x00}
Arg[1] = ptr 0x00007ff65d2651a9 -> "SetThreadDescription"
90cdc;kernelbase.SetThreadDescription
SetThreadDescription:
Arg[0] = 0xfffffffffffffffe = 18446744073709551614
Arg[1] = ptr 0x000002375de09170 -> L"main"
8c96;kernel32.HeapFree
8c1b;ntdll.RtlAllocateHeap
ba8c5;vcruntime140.memmove
8c1b;ntdll.RtlAllocateHeap
8c96;kernel32.HeapFree
8c96;kernel32.HeapFree
8fd6f;kernel32.GetStdHandle
We can see that the shellcodified version, and the version with TLS directory removed failed at the same point of execution. So there is a strong clue that inability to properly execute TLS caused the issue.
Although pe_to_shellcode supports TLS in a way, but it is a very simplified version. The TLS is called only once, before the Entry Point is executed. This may not be enough in some cases. But implementing a proper TLS support is beyond the scope of this small loader, because it would require hooking of functions.
If it is possible, I would recommend you to try compiling it with TLS disabled. Maybe the GNU compiler already did it, and that helped? Please let me know your thoughts.
As far as I can tell there is no way to fully disable TLS with rust's compiler, I haven't found any flags for it.
I guess it is just a quirk of the implementation between GNU and MSVC that made the difference!
Thank you for your detailed explanation and work on this, I learned a lot from your explanation above :)
Hi, I am running into some issues generating shellcode from my rust binary.
It works when run in a copy of the same process using a built-in shellcode runner command (VirtualAlloc, CreateThread), but when executed in the context of another process, either injected or using runshc64, it does not work.
I am thinking that it could be some kind of pre-main entrypoint that is relying on the current process' environment but I cannot figure out how to skip over it the past few days.
Have you run into this before?
For context the raw code can be found here https://github.com/Path-17/diet-c2/tree/main/implants/implant-v2/src
Thank you! I attached a copy of the executable (not the output of pe2shc.exe) as well
implant-v2.zip