Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add x86_64 support #97

Open
wants to merge 21 commits into
base: x86_64
Choose a base branch
from
Open

Add x86_64 support #97

wants to merge 21 commits into from

Conversation

cube0x8
Copy link
Collaborator

@cube0x8 cube0x8 commented Mar 3, 2021

The x64 support has started!

  1. it is possible to map and link an x64 executable. Basic tests have been created.
  2. An mpclient_x64 Makefile target has been created. For now, it's only for development purpose
  3. x64 linux and windows calling conventions differ, as volatile and non-volatile registers. Two wrappers for HeapAlloc and HeapFree APIs have been created in order to prove we can effectively call the winapi in x64 mode!

Running "make debug" will build the target with debugging symbols, low level of optimization and without NDEBUG flag.
This target is meant to be used for development purposes.
The "all" target remain the same.
…ch successfully check a PE (x64) header.

The peloader library can be built passing the ARCH flag as argument, which allows the user
to choose between x86 or x64. This flag is available for the peloader library only, but
the idea is to expand it to the whole project.

check_nt_hdr function has been changed in order to recognize x64 NT headers magic values.
For this purpose, a Check unit test has been added.
LDLIBS variable has been modified in order to intiialize crtexports. This makes us able to map and link a .dll in the Check unit tests.
test target do uses O0 and -g compiler flags.
Created the first two x64 APIs (HeapAlloc and HeapFree).
The x64 APIs will have the "_x64" suffix and they will be mostly wrappers around the
true APIs.
An only development-purpose Makefile target (mpclient_x64) has been created.
… replaced by zydis, so now we've got a x86/x86_64 disassembler. hook.c now uses zydis to patch functions (only x86 for now)
@cube0x8
Copy link
Collaborator Author

cube0x8 commented Mar 22, 2021

I have just replaced libdisasm with Zydis. I did basic tests (only x86) for the insert_function_redirect and redirect_call_within_function functions and they passed but please, @taviso, when/if you have time check and let me know if it breaks your stuff!

Next steps:

  1. add x64 support to hook.c
  2. create dispatchers to switch linux <=> windows calling conventions

…direct functions and switch calling convention
@cube0x8
Copy link
Collaborator Author

cube0x8 commented Apr 9, 2021

I added x86_64 support for the intercept set of libraries:

  1. added two assembly (NASM) dispatchers to switch calling convention from x86_64 linux to x86_64 windows and the other way around
  2. It is possible to create a HOOK_FUNCTION_REPLACE redirect. It is not possible (yet) to create a HOOK_DEFAULT redirect
  3. Added a suite test with a test DLL to prove that the redirect and the calling convention switch is working. The code of the DLL has been added too and it has to be built (VS2019) before running the tests)
  4. mpclient_x64 does not work yet (SIGSEGV). I started debugging it to understand what's going on.
  5. intercept became a CMake project

@cube0x8
Copy link
Collaborator Author

cube0x8 commented May 9, 2021

So,

  1. I fixed the x64 assembly dispatchers. It looks like they are working quite well now
  2. All the WINAPIs are correctly linked and executed
  3. I managed to run a full scan of eicar.com using mpclient_x64.c
  4. Tests have been removed

Now, the framework should be able to load, link and execute x86_64 DLLs.

On the other side, I guess I am doing something wrong with one (or more than one) of the mpengine data structures we provide to the __rsignal entry point. It happens that, when EngineScanCallback is called, Scan->Flags randomly changes between executions while it should be always the same as it happens in the x86 version. I probably will investigate this further, even if I reversed multiple time mpengine.dll and all the data structures seem correctly aligned.
Also, I've been able to run a scan ONLY passing eicar.com as input. All the other files/binaries I've tested so far raise an exception and terminate the scan.

Next steps:

  1. Provide a variadic function which, given a DLL exported function and its arguments as input, proceeds to switch the calling convention and call the user provided entry point.
  2. Test more x86_64 DLLs
  3. Provide additional hooking capabilities
  4. Try to understand what's wrong with mpclient

@cube0x8
Copy link
Collaborator Author

cube0x8 commented May 11, 2021

It is possible to call DLL exports using the x86_64_call_exported_function wrapper.

…ing addresses on the stack and overwriting some useful value on the redzone. For now, the redzone has been disabled (-mno-red-zone).
@cube0x8
Copy link
Collaborator Author

cube0x8 commented May 14, 2021

Subhook hooks were pushing values on the redzone, which led to some useful values on the stack to be overwritten. I disabled the redzone for now (-mno-red-zone).

I managed to use loadlibrary for other x86_64 DLL - like the BitDefender engine - and I am able to perform scans on different files and binaries. Everything is working quite good I would say.

Still, mpclient just works (more or less, since Scan->Flags is still unreliable) with eicar.com and it fails (uncaught exception) with all the other files I tried so far.

One thing to keep in mind:

  • the x86_64 mode works only if peloader is compiled without any optimization (-O0)

@cube0x8
Copy link
Collaborator Author

cube0x8 commented May 18, 2021

Update: with the new release of mpengine.dll (Product Version Number: 1.1.18200.2) it does not work also with eicar.com.
It correctly boots up the engine but it raises an uncaught exception during the scan. I will try to sort this out as soon as I get some free time.

@cube0x8 cube0x8 changed the title Introduced x64 support for mapping and link PE executables. Created first x64 APIs stub for mpclient. Add x86_64 support May 18, 2021
@taviso
Copy link
Owner

taviso commented May 19, 2021

This is really impressive progress, I think we can try to merge this into main. Let me test it out today to make sure it's not regressing any x86 stuff, I'm a bit nervous about merging the hook and the x64 stuff in one - but maybe it will be easy...

…ok. We can't rely on a jmp near immediate in this case.
@cube0x8
Copy link
Collaborator Author

cube0x8 commented May 28, 2021

Two notes:

  1. The framework can't work with selinux enforcing, since mprotect fails to change permission on peloader's functions. I have to find a better solution than patching the APIs at runtime.
  2. As I started using it for fuzzing purposes, bugs are mushrooming. For example, on Windows xmm6 and xmm7 are preserved registers and I forgot to store them through the calling convention switch.

I am going to open a couple of issues (assigned to me) to keep track of these bugs. I will provide a patch as soon as I can.

…e dead and make a good coffee (and eventually it changes the calling convention of a function to Windows x86_64 as well...)
@cube0x8
Copy link
Collaborator Author

cube0x8 commented May 29, 2021

So... I have just found out that GCC (and Clang too) implements the function's attribute __attribute__((ms_abi)), which compiles a function with the Windows x86_64 calling convention. This solved more than a half of the workload on the x86_64 support.

Basically, all the heavy lifting with x64 NASM dispatchers and the new hook library is... USELESS.

Next steps:

  1. clean the code
  2. bring back and test the old good hooking library
  3. implement SEH for x86_64 (https://llvm.org/docs/ExceptionHandling.html#wineh)

@cube0x8
Copy link
Collaborator Author

cube0x8 commented May 31, 2021

Updates:

  1. Lately I've been working on the implementation for the x86_64 version of the SEH unwind support.
  2. After 1) is done, I should also fix the regression for the plain x86 version.

@taviso good news: after 1) and 2) are done, I am 99% sure we fully achieved x86_64 native (and reliable) windows execution on Linux, and we would be ready to merge everything into master!

@cube0x8
Copy link
Collaborator Author

cube0x8 commented Jul 16, 2021

Added the support for the x64 Structured Exception Handling.
I tested it with few x64 programs that use SEH and it worked.

This said, I also tested the last version of mpengine with different malicious programs:
the Scan->Flags parameter is sometimes correct and it returns the right detection but - other times - it gets another value and the user does not get any detection. I am still not able to explain this behaviour, but I guess is due to some bad aligned data structure or incorrect peloader API.

Other AVs engines/x64 programs are correctly loaded and work as expected.

@taviso
Copy link
Owner

taviso commented Jul 16, 2021

wow! 🥳

I think I don't really understand the flags even for x86, I just guessed from testing them experimentally. I think it probably just needs some reversing of msmpeng to see what it does with the flags, I'll get to it some time!

Do you think we should start trying to merge it into main?

@cube0x8
Copy link
Collaborator Author

cube0x8 commented Jul 19, 2021

I will start fixing the things for the x86 version and I think we can gradually merge them into main.

@cube0x8
Copy link
Collaborator Author

cube0x8 commented Aug 10, 2021

The x86 version of mpclient works. The x64 version is still unreliable.

I think we can start merging into main.

@redmed666
Copy link

Hi all!
Maybe a stupid question but is "subhook/subhook.h" not missing from your branch @cube0x8 ? Because when trying to compile it, I get this error:

$ make
cc -march=native -ggdb3 -std=gnu99 -fshort-wchar -Wno-multichar -Iinclude -Iintercept/include -Ilog -Ipeloader -mstackrealign -maccumulate-outgoing-args -O3 -m32 -D_GNU_SOURCE -I. -DNDEBUG  -c -o mpclient.o mpclient.c
In file included from mpclient.c:46:
intercept/include/hook.h:4:10: fatal error: ../subhook/subhook.h: No such file or directory
    4 | #include "../subhook/subhook.h"
      |          ^~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
make: *** [<builtin>: mpclient.o] Error 1

@cube0x8
Copy link
Collaborator Author

cube0x8 commented Oct 23, 2021

Hello @redmed666

subhook is configured as a submodule in the x86/64 version of loadlibrary. Maybe did you forget to clone the submodules?

If you've already cloned the repository and you checked out on the x64 branch, then run:
git submodule update --init --recursive

Otherwise, you should clone the repo using the --recurse-submodules switch:
git clone --recurse-submodules --branch x64 https://github.com/cube0x8/loadlibrary.git

@redmed666
Copy link

It was indeed a stupid question. Thank you!

@shuxin
Copy link

shuxin commented Nov 1, 2021

Excellent job.

@shuxin
Copy link

shuxin commented Nov 6, 2021

after a few test, I found many faked api should change from noregmarm to ms_abi. for example crt.c

@cube0x8
Copy link
Collaborator Author

cube0x8 commented Nov 9, 2021

Hello @shuxin,

yeah I definetely forgot about the APIs in crt.c. Anyway, I ran few tests on mpclient_x64.c with different file inputs and never seen them executed. Can you confirm?

I'm planning to get back on this PR as soon as possible to finally make it work reliably for mpclient and merge it.

@shuxin
Copy link

shuxin commented Nov 9, 2021

Hello @shuxin,

yeah I definetely forgot about the APIs in crt.c. Anyway, I ran few tests on mpclient_x64.c with different file inputs and never seen them executed. Can you confirm?

I'm planning to get back on this PR as soon as possible to finally make it work reliably for mpclient and merge it.

mse mpengine does not depends on msvcrt#.dll .
so it should works well. you can merge first.
I am trying to load some other dlls, if I have any stable fix, I will make another pull request.

@kh-abd-kh
Copy link

kh-abd-kh commented Dec 3, 2022

Hi and thank you,

in the last step of make i get this error

cc -march=native -ggdb3 -std=gnu99 -fshort-wchar -Wno-multichar -Iinclude -Iintercept/include -Ilog -Ipeloader -mstackrealign -maccumulate-outgoing-args -O3 -g -O0 -fPIC mpclient_x64.o log/log.o -o mpclient_x64 -Wl,--whole-archive peloader/libpeloader.a -Wl,intercept/libhook.a -Wl,intercept/libZydis.a -Wl,intercept/libsubhook.a -Wl,--no-whole-archive -march=native -ggdb3 -std=gnu99 -fshort-wchar -Wno-multichar -Iinclude -Iintercept/include -Ilog -Ipeloader -mstackrealign -maccumulate-outgoing-args -O3 -g -O0 -fPIC -lm -Wl,--dynamic-list=exports.lst -ldl
/usr/bin/ld: i386 architecture of input file log/log.o' is incompatible with i386:x86-64 output /usr/bin/ld: i386 architecture of input file peloader/libpeloader.a(Heap.o)' is incompatible with i386:x86-64 output
/usr/bin/ld: i386 architecture of input file peloader/libpeloader.a(Files.o)' is incompatible with i386:x86-64 output /usr/bin/ld: i386 architecture of input file peloader/libpeloader.a(IsProcessorFeaturePresent.o)' is incompatible with i386:x86-64 output
/usr/bin/ld: i386 architecture of input file `peloader/libpeloader.a(EncodePointer.o)' is incompatible with i386:x86-64 output

....

/usr/bin/ld: i386 architecture of input file intercept/libsubhook.a(subhook_unix.c.o)' is incompatible with i386:x86-64 output /usr/bin/ld: peloader/libpeloader.a(crt.o): in function _alldiv':
/home/mabd/loadlibrary64/peloader/crt.c:421: undefined reference to __divdi3' /usr/bin/ld: peloader/libpeloader.a(crt.o): in function _aulldiv':
/home/mabd/loadlibrary64/peloader/crt.c:427: undefined reference to __udivdi3' /usr/bin/ld: peloader/libpeloader.a(crt.o): in function _allrem':
/home/mabd/loadlibrary64/peloader/crt.c:445: undefined reference to __moddi3' /usr/bin/ld: peloader/libpeloader.a(crt.o): in function _aullrem':
/home/mabd/loadlibrary64/peloader/crt.c:451: undefined reference to __umoddi3' /usr/bin/ld: intercept/libZydis.a(String.c.o): in function ZydisStringAppendDecU64':
String.c:(.text+0x3bf): undefined reference to `__udivdi3'
collect2: error: ld returned 1 exit status
make: *** [Makefile:47: mpclient_x64] Error 1

thank you

@kh-abd-kh
Copy link

I cloned everything with this, as mentioned above,

git clone --recurse-submodules --branch x64 https://github.com/cube0x8/loadlibrary.git

@cube0x8
Copy link
Collaborator Author

cube0x8 commented Dec 4, 2022

@kh-abd-kh it looks like you have a mix x86 and x86_64 environment. Do you want to execute a 64 or 32 bit DLL?

You might need to make clean first and then try to build your target again.

@ravone
Copy link

ravone commented Dec 14, 2022

I leave this comment as yet another confirmation of the x86-64 implementation operability. Creating a binary file to interact with my dll took me less time than I expected. Wow! Thanks to the authors for a great job!

@cube0x8
Copy link
Collaborator Author

cube0x8 commented Dec 14, 2022

@ravone thank you very much for your feedback. I appreciate it :)

@LordNoteworthy
Copy link

@cube0x8 any idea when this will get merged ?

@cube0x8
Copy link
Collaborator Author

cube0x8 commented Apr 11, 2023 via email

ENGINE_CONFIG EngineConfig;

// Load the mpengine module.
if (pe_load_library(image.name, &image.image, &image.size) == false) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there's a bug: image.size is an int but pe_load_library takes a size_t*.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

8 participants