lazy importer ![](https://camo.githubusercontent.com/da7464a135ae6d34698e7c61c58c95daa4ff7dea3c781eec863a786f014bbecc/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f76657273696f6e2d322e302e342d677265656e2e737667)
A simple and easy to use header only library to make the life of a reverse engineer much harder.
LI_FN(OutputDebugStringA)("hello world");
LI_FN(VirtualProtect).in(LI_MODULE("kernel32.dll").cached());
IDA output when compiling first line
- Does not leave any strings in memory.
- Does not allocate any memory.
- Can be easily inlined.
- Does not leave any imports in the executable.
- Produces extremely small assembly.
- Non caching functions do not leave anything in data sections.
LI_FN(function_pointer) -> lazy_function
LI_FN_DEF(function_type) -> lazy_function
LI_MODULE(module_name) -> lazy_module
safe
indicates that when function cannot complete its task successfully 0 is returned instead of undefined behaviour manifesting.
cached
indicates that the result is only computed during the first call and later reused.
forwarded
indicates that export forwarding will be correctly resolved.
function |
safe |
cached |
Attempts to find the given module and returns its address |
get<T = void*>() -> T |
❌ |
❌ |
safe<T = void*>() -> T |
✅ |
❌ |
cached<T = void*>() -> T |
❌ |
✅ |
safe_cached<T = void*>() -> T |
✅ |
✅ |
function |
safe |
cached |
forwarded |
calls resolved export using given arguments |
operator()(...) -> result_of<F, ...> |
❌ |
❌ |
❌ |
attempts to resolve an export in all loaded modules and returns the function address |
get<T = F>() -> T |
❌ |
❌ |
❌ |
safe<T = F>() -> T |
✅ |
❌ |
❌ |
cached<T = F>() -> T |
❌ |
✅ |
❌ |
safe_cached<T = F>() -> T |
✅ |
✅ |
❌ |
forwarded<T = F>() -> T |
❌ |
❌ |
✅ |
forwarded_safe<T = F>() -> T |
✅ |
❌ |
✅ |
forwarded_cached<T = F>() -> T |
❌ |
✅ |
✅ |
forwarded_safe_cached<T = F>() -> T |
✅ |
✅ |
✅ |
attempts to resolve an export in the given module and returns the function address |
in<T = F, A>(A module_address) -> T |
❌ |
❌ |
❌ |
in_safe<T = F, A>(A module_address) -> T |
✅ |
❌ |
❌ |
in_cached<T = F, A>(A module_address) -> T |
❌ |
✅ |
❌ |
in_safe_cached<T = F, A>(A module_address) -> T |
✅ |
✅ |
❌ |
attempts to resolve an export in ntdll and returns the function address |
nt<T = F>() -> T |
❌ |
❌ |
❌ |
nt_safe<T = F>() -> T |
✅ |
❌ |
❌ |
nt_cached<T = F>() -> T |
❌ |
✅ |
❌ |
nt_safe_cached<T = F>() -> T |
✅ |
✅ |
❌ |
extra configuration
#define |
effects |
LAZY_IMPORTER_NO_FORCEINLINE |
disables force inlining |
LAZY_IMPORTER_CASE_INSENSITIVE |
enables case insensitive comparison. Might be required for forwarded export resolution. |
LAZY_IMPORTER_CACHE_OPERATOR_PARENS |
uses cached() instead of get() in operator() of lazy_function |
LAZY_IMPORTER_RESOLVE_FORWARDED_EXPORTS |
uses forwarded() in get() . WARNING does not apply to nt() and in() . |
for ( i = *(_QWORD **)(*(_QWORD *)(__readgsqword(0x60u) + 24) + 16i64); ; i = (_QWORD *)*i )
{
v1 = i[6];
v2 = *(unsigned int *)(*(signed int *)(v1 + 60) + v1 + 136);
v3 = (_DWORD *)(v2 + v1);
if ( v2 + v1 != v1 )
{
LODWORD(v4) = v3[6];
if ( (_DWORD)v4 )
break;
}
LABEL_8:
;
}
while ( 1 )
{
v4 = (unsigned int)(v4 - 1);
v5 = -2128831035;
v6 = (char *)(v1 + *(unsigned int *)((unsigned int)v3[8] + 4 * v4 + v1));
v7 = *v6;
v8 = (signed __int64)(v6 + 1);
if ( v7 )
{
do
{
++v8;
v5 = 16777619 * (v5 ^ v7);
v7 = *(_BYTE *)(v8 - 1);
}
while ( v7 );
if ( v5 == -973690651 )
break;
}
if ( !(_DWORD)v4 )
goto LABEL_8;
}
((void (__fastcall *)(const char *))(v1
+ *(unsigned int *)(v1
+ (unsigned int)v3[7]
+ 4i64 * *(unsigned __int16 *)(v1 + (unsigned int)v3[9] + 2 * v4))))("hello world");