lazy importer 
리버스 엔지니어의 작업을 훨씬 더 어렵게 만들기 위한 간단하고 사용하기 쉬운 헤더 온리 라이브러리입니다.
간단한 예제
LI_FN(OutputDebugStringA)("hello world");
LI_FN(VirtualProtect).in(LI_MODULE("kernel32.dll").cached());
첫 번째 줄을 컴파일했을 때의 IDA 출력
기능
- 메모리에 어떤 문자열도 남기지 않습니다.
- 어떤 메모리도 할당하지 않습니다.
- 쉽게 인라인될 수 있습니다.
- 실행 파일에 어떤 임포트도 남기지 않습니다.
- 매우 작은 어셈블리를 생성합니다.
- 캐싱하지 않는 함수들은 데이터 섹션에 아무것도 남기지 않습니다.
- 기본적인 해시 데이터베이스 공격을 무력화하기 위해 각 컴파일마다 해시가 무작위화됩니다.
문서
LI_FN(function_pointer) -> lazy_function
LI_FN_DEF(function_type) -> lazy_function
LI_MODULE(module_name) -> lazy_module
safe는 함수가 작업을 성공적으로 완료할 수 없을 때 정의되지 않은 동작이 나타나는 대신 0이 반환됨을 나타냅니다.
cached는 결과가 첫 번째 호출 중에만 계산되고 이후에 재사용됨을 나타냅니다.
forwarded는 익스포트 포워딩이 올바르게 해결됨을 나타냅니다.
lazy_module
| function |
safe |
cached |
| 주어진 모듈을 찾으려고 시도하고 그 주소를 반환합니다 |
get<T = void*>() -> T |
:x: |
:x: |
safe<T = void*>() -> T |
:white_check_mark: |
:x: |
cached<T = void*>() -> T |
:x: |
:white_check_mark: |
safe_cached<T = void*>() -> T |
:white_check_mark: |
:white_check_mark: |
| 주어진 LDR_DATA_TABLE_ENTRY 포인터를 사용하여 주어진 모듈을 찾으려고 시도합니다 |
in<T = void*, Ldr>(Ldr ldr_entry) -> T |
:white_check_mark: |
:x: |
in_cached<T = void*, Ldr>(Ldr ldr_entry) -> T |
:white_check_mark: |
:white_check_mark: |
lazy_function<F>
| function |
safe |
cached |
forwarded |
| 주어진 인수를 사용하여 해결된 익스포트를 호출합니다 |
operator()(...) -> result_of<F, ...> |
:x: |
:x: |
:x: |
| 로드된 모든 모듈에서 익스포트를 해결하려고 시도하고 함수 주소를 반환합니다 |
get<T = F>() -> T |
:x: |
:x: |
:x: |
safe<T = F>() -> T |
:white_check_mark: |
:x: |
:x: |
cached<T = F>() -> T |
:x: |
:white_check_mark: |
:x: |
safe_cached<T = F>() -> T |
:white_check_mark: |
:white_check_mark: |
:x: |
forwarded<T = F>() -> T |
:x: |
:x: |
:white_check_mark: |
forwarded_safe<T = F>() -> T |
:white_check_mark: |
:x: |
:white_check_mark: |
forwarded_cached<T = F>() -> T |
:x: |
:white_check_mark: |
:white_check_mark: |
forwarded_safe_cached<T = F>() -> T |
:white_check_mark: |
:white_check_mark: |
:white_check_mark: |
| 주어진 모듈에서 익스포트를 해결하려고 시도하고 함수 주소를 반환합니다 |
in<T = F, A>(A module_address) -> T |
:x: |
:x: |
:x: |
in_safe<T = F, A>(A module_address) -> T |
:white_check_mark: |
:x: |
:x: |
in_cached<T = F, A>(A module_address) -> T |
:x: |
:white_check_mark: |
:x: |
in_safe_cached<T = F, A>(A module_address) -> T |
:white_check_mark: |
:white_check_mark: |
:x: |
ntdll에서 익스포트를 해결하려고 시도하고 함수 주소를 반환합니다 |
nt<T = F>() -> T |
:x: |
:x: |
:x: |
nt_safe<T = F>() -> T |
:white_check_mark: |
:x: |
:x: |
nt_cached<T = F>() -> T |
:x: |
:white_check_mark: |
:x: |
nt_safe_cached<T = F>() -> T |
:white_check_mark: |
:white_check_mark: |
:x: |
추가 구성
#define | effects |
|---|
LAZY_IMPORTER_NO_FORCEINLINE | 강제 인라이닝을 비활성화합니다 |
LAZY_IMPORTER_CASE_INSENSITIVE | 대소문자 구분 없는 비교를 활성화합니다. 포워딩된 익스포트 해결에 필요할 수 있습니다. |
LAZY_IMPORTER_CACHE_OPERATOR_PARENS | lazy_function의 operator()에서 get() 대신 cached()를 사용합니다 |
LAZY_IMPORTER_RESOLVE_FORWARDED_EXPORTS | get()에서 forwarded()를 사용합니다. 경고: nt()와 in()에는 적용되지 않습니다. |
LAZY_IMPORTER_HARDENED_MODULE_CHECKS | 모듈 열거에 추가적인 정상성 검사를 추가합니다. |
LAZY_IMPORTER_NO_CPP_FORWARD | <utility> c++ 헤더에 대한 의존성을 제거합니다. |
예제 출력
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");