Почему-то везде в интернете ходит один и тот же неправильный прототип nt-функции LdrLoadDll
C++ | 1
2
3
4
5
6
7
8
9
| NTSYSAPI
NTSTATUS
NTAPI
LdrLoadDll(
_In_opt_ PWSTR DllPath,
_In_opt_ PULONG DllCharacteristics,
_In_ PUNICODE_STRING DllName,
_Out_ PVOID *DllHandle
); |
|
ведать один написал (ещё со времен какой-нить висты) и все с него бездумно копируют.
Начнём разбирать.
Количество параметров функции. Хотя бы здесь не ошиблись (тут собственно и не ошибёшься, такие функции тянутся ещё с XP и редко меняются) - всего 4 параметра.
Теперь берём IDA и запихиваем в неё KernelBase.dll (именно её, а не kernel32.dll - с Windows 7 эта либа стала заглушкой, которая просто перенаправляет на KernelBase).
Смотрим на LoadLibraryA - банальный механизм через twain ссылается на LoadLibraryExA
Assembler | 1
2
3
4
| xor r8d, r8d ; dwFlags
xor edx, edx ; hFile
mov rcx, rdi ; lpLibFileName
call LoadLibraryExA |
|
переходим к LoadLibraryExA и видим что она вызывает LoadLibraryExW (в ядре нет ansi, только юникод)
Assembler | 1
2
3
4
| mov rcx, [rsp+38h+UnicodeString.Buffer] ; lpLibFileName
mov r8d, ebx ; dwFlags
mov rdx, rdi ; hFile
call LoadLibraryExW |
|
аналогично и для LoadLibraryW (там ещё проще, никакого twain)
Assembler | 1
2
3
| xor r8d, r8d
xor edx, edx
jmp LoadLibraryExW |
|
Итак, конечную функцию перед переходом в NT мы нашли, теперь посмотрим на её прототип
Кликните здесь для просмотра всего текста
C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
| HMODULE __stdcall LoadLibraryExW(LPCWSTR lpLibFileName, HANDLE hFile, DWORD dwFlags)
{
DWORD v3; // ebx
unsigned int v4; // eax
USHORT v5; // cx
signed int v6; // edx
unsigned int v7; // eax
unsigned int v8; // esi
bool v10; // zf
__int64 v11; // [rsp+30h] [rbp-20h]
__int64 v12; // [rsp+38h] [rbp-18h]
UNICODE_STRING Source; // [rsp+40h] [rbp-10h]
int v14; // [rsp+70h] [rbp+20h]
__int64 v15; // [rsp+88h] [rbp+38h]
v3 = dwFlags;
if ( !lpLibFileName || hFile || dwFlags & 0xFFFF0000 || (dwFlags & 0x42) == 66 )
goto LABEL_31;
v4 = RtlInitUnicodeStringEx(&Source, lpLibFileName);
if ( (v4 & 0x80000000) != 0 )
{
BaseSetLastNTError(v4);
return 0i64;
}
v5 = Source.Length;
if ( !Source.Length )
goto LABEL_31;
do
{
if ( Source.Buffer[((unsigned __int64)v5 >> 1) - 1] != 32 )
break;
v10 = v5 == 2;
v5 -= 2;
Source.Length = v5;
}
while ( !v10 );
if ( !v5 )
{
LABEL_31:
BaseSetLastNTError(3221225485i64);
return 0i64;
}
v6 = 0;
v15 = 0i64;
if ( v3 & 0x62 )
{
JUMPOUT(LdrGetDllPath(Source.Buffer, v3 & 0x7F08, &v11, &v12), 0, sub_18009AE5E);
v7 = BasepLoadLibraryAsDataFileInternal(&Source, (__int64)&v15);
v8 = v7;
if ( (signed int)(v7 + 2147483648) >= 0 && v7 != -1073741809 && v3 & 0x20 && v3 & 0x42 )
v8 = BasepLoadLibraryAsDataFileInternal(&Source, (__int64)&v15);
RtlReleasePath(v11);
}
else
{
v14 = 0;
if ( v3 & 1 )
{
v14 = 2;
v6 = 2;
}
if ( (v3 & 0x80u) != 0 )
v14 = v6 | 0x800000;
JUMPOUT(v3 & 4, 0, sub_18009AE65);
JUMPOUT(_bittest((const signed int *)&v3, 0xFu), sub_18009AE70);
v8 = LdrLoadDll(v3 & 0x7F08 | 1i64, &v14, &Source, &v15);
}
if ( (v8 & 0x80000000) == 0 )
return (HMODULE)v15;
BaseSetLastNTError(v8);
return 0i64;
} |
|
Теперь разберёмся что и к чему здесь.
Сначала происходит инициализация юникод-строки RtlInitUnicodeStringEx.
Далее рассмотрим вот этот участок кода
C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
| else
{
v14 = 0;
if (v3 & 1)
{
v14 = 2;
v6 = 2;
}
if ((v3 & 0x80u) != 0)
v14 = v6 | 0x800000;
JUMPOUT(v3 & 4, 0, sub_18009AE65);
JUMPOUT(_bittest((const signed int *)&v3, 0xFu), sub_18009AE70);
v8 = LdrLoadDll(v3 & 0x7F08 | 1i64, &v14, &Source, &v15);
}
if ((v8 & 0x80000000) == 0)
return (HMODULE)v15;
BaseSetLastNTError(v8);
return 0i64; |
|
Итак:
v8 это просто NTSTATUS
v3 (если смотреть на прототип LoadLibraryExW) это dwFlags (v3 = dwFlags)
v14 это целочисленное значение, которое зависит от передаваемого в функцию флага
Source - всё просто, это UNICODE_STRING
v15 это HMODULE, указатель на загружаемую библиотеку.
Флаг, который обрабатывается в LdrLoadDll, является флагом критерия поиска библиотеки (про каждый из них можно прочитать на MSDN)
C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
| #define DONT_RESOLVE_DLL_REFERENCES 0x00000001
#define LOAD_LIBRARY_AS_DATAFILE 0x00000002
// reserved for internal LOAD_PACKAGED_LIBRARY: 0x00000004
#define LOAD_WITH_ALTERED_SEARCH_PATH 0x00000008
#define LOAD_IGNORE_CODE_AUTHZ_LEVEL 0x00000010
#define LOAD_LIBRARY_AS_IMAGE_RESOURCE 0x00000020
#define LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE 0x00000040
#define LOAD_LIBRARY_REQUIRE_SIGNED_TARGET 0x00000080
#define LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR 0x00000100
#define LOAD_LIBRARY_SEARCH_APPLICATION_DIR 0x00000200
#define LOAD_LIBRARY_SEARCH_USER_DIRS 0x00000400
#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
#if (NTDDI_VERSION >= NTDDI_WIN10_RS1)
#define LOAD_LIBRARY_SAFE_CURRENT_DIRS 0x00002000
#define LOAD_LIBRARY_SEARCH_SYSTEM32_NO_FORWARDER 0x00004000
#else
//
// For anything building for downlevel, set the flag to be the same as LOAD_LIBRARY_SEARCH_SYSTEM32
// such that they're treated the same when running on older version of OS.
//
#if (NTDDI_VERSION >= NTDDI_WIN10_RS2)
#define LOAD_LIBRARY_OS_INTEGRITY_CONTINUITY 0x00008000
#endif |
|
Именно поэтому в неправильном прототипе указан тип первого параметра - PWSTR DllPath. Кто-то не до конца понял, что это не путь к Dll, а флаг поиска, который система реализует сама.
И вот теперь, учитывая разбор через IDA, мы можем написать правильный прототип:
C++ | 1
2
3
4
5
6
7
| typedef NTSTATUS(__fastcall* _LdrLoadDll)(
_In_opt_ UINT32 Flags,
_In_opt_ PUINT32 Reserved,
_In_ PUNICODE_STRING ModuleFileName,
_Out_ HMODULE* ModuleHandle
);
_LdrLoadDll LdrLoadDll; |
|
Причём реализовывать его нужно следующим образом
Кликните здесь для просмотра всего текста
C++ | 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
| std::cout <<
"0 - NULL (without flag)\n"
"1 - DONT_RESOLVE_DLL_REFERENCES\n"
"2 - LOAD_LIBRARY_AS_DATAFILE\n"
"3 - LOAD_PACKAGED_LIBRARY (for internal)\n"
"4 - LOAD_WITH_ALTERED_SEARCH_PATH\n"
"5 - LOAD_IGNORE_CODE_AUTHZ_LEVEL\n"
"6 - LOAD_LIBRARY_AS_IMAGE_RESOURCE\n"
"7 - LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE\n"
"8 - LOAD_LIBRARY_REQUIRE_SIGNED_TARGET\n"
"9 - LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR\n"
"10 - LOAD_LIBRARY_SEARCH_APPLICATION_DIR\n"
"11 - LOAD_LIBRARY_SEARCH_USER_DIRS\n"
"12 - LOAD_LIBRARY_SEARCH_SYSTEM32 (for anything building for downlevel)\n"
"13 - LOAD_LIBRARY_SEARCH_DEFAULT_DIRS\n"
"14 - LOAD_LIBRARY_SAFE_CURRENT_DIRS (WIN10_RS1)\n"
"15 - LOAD_LIBRARY_SEARCH_SYSTEM32_NO_FORWARDER (WIN10_RS1)\n"
"16 - LOAD_LIBRARY_OS_INTEGRITY_CONTINUITY (WIN10_RS2)\n"
"Choose: ";
UINT32 temp = 0, flag = 0, temp1 = 0;
INT32 temp2 = 0;
HMODULE hModule = nullptr;
PVOID fnAddress = nullptr;
UNICODE_STRING LibraryName = { 0 };
ANSI_STRING FunctionName = { 0 };
std::cin >> temp;
std::cin.get();
switch (temp) {
case 0:
flag = NULL;
break;
case 1:
flag = DONT_RESOLVE_DLL_REFERENCES;
break;
case 2:
flag = LOAD_LIBRARY_AS_DATAFILE;
break;
case 3:
flag = 0x00000004;
break;
case 4:
flag = LOAD_WITH_ALTERED_SEARCH_PATH;
break;
case 5:
flag = LOAD_IGNORE_CODE_AUTHZ_LEVEL;
break;
case 6:
flag = LOAD_LIBRARY_AS_IMAGE_RESOURCE;
break;
case 7:
flag = LOAD_LIBRARY_AS_DATAFILE_EXCLUSIVE;
break;
case 8:
flag = LOAD_LIBRARY_REQUIRE_SIGNED_TARGET;
break;
case 9:
flag = LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR;
break;
case 10:
flag = LOAD_LIBRARY_SEARCH_APPLICATION_DIR;
break;
case 11:
flag = LOAD_LIBRARY_SEARCH_USER_DIRS;
break;
case 12:
flag = LOAD_LIBRARY_SEARCH_SYSTEM32;
break;
case 13:
flag = LOAD_LIBRARY_SEARCH_DEFAULT_DIRS;
break;
case 14:
flag = LOAD_LIBRARY_SAFE_CURRENT_DIRS;
break;
case 15:
flag = LOAD_LIBRARY_SEARCH_SYSTEM32_NO_FORWARDER;
break;
case 16:
flag = LOAD_LIBRARY_OS_INTEGRITY_CONTINUITY;
break;
default:
break;
}
if (flag & 1) {
temp1 = 2;
temp2 = 2;
}
if ((flag & 0x80u) != 0)
temp1 = temp2 | 0x800000;
RtlInitUnicodeStringEx(&LibraryName, L"ntoskrnl.exe");
LdrLoadDll(flag & 0x7F08 | 1, &temp1, &LibraryName, &hModule);
std::cout << "ntoskrnl.exe = 0x" << hModule << std::endl;
RtlInitAnsiStringEx(&FunctionName, "IoCreateFile");
LdrGetProcedureAddressForCaller(hModule, &FunctionName, NULL, &fnAddress, FALSE, nullptr);
std::cout << "IoCreateFile = 0x" << fnAddress << std::endl;
std::cin.get(); |
|
|