Форум программистов, компьютерный форум, киберфорум
Наши страницы
_lunar_
Войти
Регистрация
Восстановить пароль
Оценить эту запись

Правильный прототип LdrLoadDll

Запись от _lunar_ размещена 22.07.2019 в 15:14
Обновил(-а) _lunar_ 23.07.2019 в 11:05

Почему-то везде в интернете ходит один и тот же неправильный прототип 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();
Размещено в Без категории
Просмотров 416 Комментарии 0
Всего комментариев 0
Комментарии
 
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru