Proof of Concept for implementing PLT/IAT hooking for MetaCall. This will be used in order to allow MetaCall to be loaded in places where a runtime is already started.
The feature once implemented will solve the following isuses:
This PoC is based on a modified version of PLTHook Library from @kubo.
First of all we have the following preconditions:
libmetacall
which loadslibnode_loader
.libnode_loader
is not linked to anything but we are going to weakly link it tolibnode
, this means that in Windows it must be linked with/DELAYLOAD
, in Linux and MacOS it must not be linked.
There are two possible cases, this happens before loading libnode_loader:
-
MetaCall is not being executed by
node.exe
, then:-
Windows:
libmetacall
loads dynamically all the dependencies oflibnode_loader
(akalibnode
).- We list all the symbols of each dependency (aka
libnode
) so we construct a hashmap of symbol string to symbol function pointer. - We list all the unresolved symbols of
libnode_loader
and we link them tolibnode
.
-
MacOS & Linux:
libmetacall
loads dynamically all the dependencies oflibnode_loader
(akalibnode
).- Linking is resolved by the linker automatically.
-
-
MetaCall is being executed by node.exe, then we have two possible cases:
-
node.exe
compiled statically (withoutlibnode
):- We get all the library dependencies from
node.exe
and we do not findlibnode
, so we get the handle of the currrent process. - We list all symbols of
node.exe
and we construct a hash map a hashmap of symbol string to symbol function pointer. - We list all the unresolved symbols of
libnode_loader
and we link them tonode.exe
.
- We get all the library dependencies from
-
node.exe
compiled dynamically (withlibnode
):- We get all the library dependencies from
node.exe
and we findlibnode
so we get the handle from it. - We list all the symbols of each dependency (aka
libnode
) so we construct a hashmap of symbol string to symbol function pointer of those dependencies (libnode
). - We list all the unresolved symbols of
libnode_loader
and we link them tolibnode
ofnode.exe
.
- We get all the library dependencies from
-
With this methodology we prevent loading a library that contains a runtime. This is very dangerous because numerous runtimes rely on constructors (C++ constructors of static class delacarations or C compiler dependant constructor mechanisms like GNU or Clang __attribute__((constructor))
) that are mutually exclusive between them. So if we only load the library but we do not call method of the library, it can still cause errors.
The loaders will be redirected to the proper runtime, reusing the functions and instance of the already running runtime.
- Works for Linux, Windows and MacOS with most of the architectures of each platform: https://github.com/metacall/plthook?tab=readme-ov-file#supported-platforms
- Hooks the functions and prevents runtime instances to be initialized, so it's fully transparent and has no side effects on the runtimes.
- Currently it does not support
-O3
on Linux with GCC compiler, neither/O2
and/Ob2
in Windows with MSVC. It works in MacOS with-O3
and Clang. - It does not work well (kubo/plthook#51) with
aarch64
architecture under Linux with the following flags:- Library:
-shared -fPIC -Wall -Wl,-z,relro,-z,now -O3
- Executable:
-fPIE -pie -Wall -Wl,-z,relro,-z,now -O3
- Library: