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

Загрузка динамических библиотек без kernel32.dll

Запись от _lunar_ размещена 22.01.2019 в 13:08
Обновил(-а) _lunar_ 27.01.2019 в 17:13

Решил написать небольшой пример для загрузки DLL средствами NativeAPI (полностью независимого от Kernel32.dll кода).
Самое главное, я напишу именно 64 битный код, т.к. в интернете одно 32 битное старье и то не доделано до конца.
Для этого я буду использовать 64 битный указатель *__ptr64.

Обычно для загрузки библиотек используется пара WinAPI функций - LoadLibrary/GetModuleHandle и GetProcAddress.
Бывает так, что приложения могут сохранять адрес функции до перехвата, и затем вызывать её минуя обработчик.
Поэтому неплохо было бы загружать DLL без лишних юзермодных прослоек. В ntdll для этого есть функция LdrLoadDll.
В коде я не буду использовать ни одного хидера, только фундаментальные типы C/C++ определенные в языке.
Из-за этого, кода будет много, потому что я буду переопределять структуры и константы с помощью WinDBG, но этим мы обезопасим себя от лишнего импорта.

Итак, суть в том чтобы найти адрес по которому загружена ntdll.dll, спарсить её экспорт и найти адрес функции LdrLoadDll.
Для получения адреса ntdll.dll воспользуемся PEB.
Открываем WinDBG, загружаем любой 64 битный файл (или аттачимся к 64 битному процессу) и пишем в командной строке dt nt!_PEB
Отладчик нам выдаст символы 64 битной структуры PEB
Нажмите на изображение для увеличения
Название: 1.png
Просмотров: 155
Размер:	43.0 Кб
ID:	5166

Приведем её к стандарту языка C/C++
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
typedef struct _PEB {
    /*+0x000*/ unsigned char InheritedAddressSpace; // UChar
    /*+0x001*/ unsigned char ReadImageFileExecOptions; // UChar
    /*+0x002*/ unsigned char BeingDebugged; // UChar
    union {
        /*+0x003*/ unsigned char BitField; // UChar
        struct {
            /*+0x003*/ unsigned char ImageUsesLargePages : 1; // Pos 0, 1 Bit
            /*+0x003*/ unsigned char IsProtectedProcess : 1; // Pos 1, 1 Bit
            /*+0x003*/ unsigned char IsImageDynamicallyRelocated : 1; // Pos 2, 1 Bit
            /*+0x003*/ unsigned char SkipPatchingUser32Forwarders : 1; // Pos 3, 1 Bit
            /*+0x003*/ unsigned char IsPackagedProcess : 1; // Pos 4, 1 Bit
            /*+0x003*/ unsigned char IsAppContainer : 1; // Pos 5, 1 Bit
            /*+0x003*/ unsigned char IsProtectedProcessLight : 1; // Pos 6, 1 Bit
            /*+0x003*/ unsigned char IsLongPathAwareProcess : 1; // Pos 7, 1 Bit
        };
    };
    /*+0x004*/ unsigned char Padding0[4]; // [4] UChar
    /*+0x008*/ void *__ptr64 Mutant; // Ptr64 Void
    /*+0x010*/ void *__ptr64 ImageBaseAddress; // Ptr64 Void
    /*+0x018*/ PEB_LDR_DATA *__ptr64 Ldr; // Ptr64 _PEB_LDR_DATA
    /*+0x020*/ RTL_USER_PROCESS_PARAMETERS *__ptr64 ProcessParameters; // Ptr64 _RTL_USER_PROCESS_PARAMETERS
    /*+0x028*/ void *__ptr64 SubSystemData; // Ptr64 Void
    /*+0x030*/ void *__ptr64 ProcessHeap; // Ptr64 Void
    /*+0x038*/ RTL_CRITICAL_SECTION *__ptr64 FastPebLock; // Ptr64 _RTL_CRITICAL_SECTION
    /*+0x040*/ SLIST_HEADER *__ptr64 AtlThunkSListPtr; // Ptr64 _SLIST_HEADER
    /*+0x048*/ void *__ptr64 IFEOKey; // Ptr64 Void
    union {
        /*+0x050*/ unsigned __int32 CrossProcessFlags; // Uint4B
        struct {
            /*+0x050*/ unsigned __int32 ProcessInJob : 1; // Pos 0, 1 Bit
            /*+0x050*/ unsigned __int32 ProcessInitializing : 1; // Pos 1, 1 Bit
            /*+0x050*/ unsigned __int32 ProcessUsingVEH : 1; // Pos 2, 1 Bit
            /*+0x050*/ unsigned __int32 ProcessUsingVCH : 1; // Pos 3, 1 Bit
            /*+0x050*/ unsigned __int32 ProcessUsingFTH : 1; // Pos 4, 1 Bit
            /*+0x050*/ unsigned __int32 ProcessPreviouslyThrottled : 1; // Pos 5, 1 Bit
            /*+0x050*/ unsigned __int32 ProcessCurrentlyThrottled : 1; // Pos 6, 1 Bit
            /*+0x050*/ unsigned __int32 ProcessImagesHotPatched : 1; // Pos 7, 1 Bit
            /*+0x050*/ unsigned __int32 ReservedBits0 : 24; // Pos 8, 24 Bits
        };
    };
    /*+0x054*/ unsigned char Padding1[4]; // [4] UChar
    union {
        /*+0x058*/ void *__ptr64 KernelCallbackTable; // Ptr64 Void
        /*+0x058*/ void *__ptr64 UserSharedInfoPtr; // Ptr64 Void
    };
    /*+0x060*/ unsigned __int32 SystemReserved; // Uint4B
    /*+0x064*/ unsigned __int32 AtlThunkSListPtr32; // Uint4B
    /*+0x068*/ void *__ptr64 ApiSetMap; // Ptr64 Void
    /*+0x070*/ unsigned __int32 TlsExpansionCounter; // Uint4B
    /*+0x074*/ unsigned char Padding2[4]; // [4] UChar
    /*+0x078*/ void *__ptr64 TlsBitmap; // Ptr64 Void
    /*+0x080*/ unsigned __int32 TlsBitmapBits[2]; // [2] Uint4B
    /*+0x088*/ void *__ptr64 ReadOnlySharedMemoryBase; // Ptr64 Void
    /*+0x090*/ void *__ptr64 SharedData; // Ptr64 Void
    /*+0x098*/ void *__ptr64 *__ptr64 ReadOnlyStaticServerData; // Ptr64 Ptr64 Void
    /*+0x0a0*/ void *__ptr64 AnsiCodePageData; // Ptr64 Void
    /*+0x0a8*/ void *__ptr64 OemCodePageData; // Ptr64 Void
    /*+0x0b0*/ void *__ptr64 UnicodeCaseTableData; // Ptr64 Void
    /*+0x0b8*/ unsigned __int32 NumberOfProcessors; // Uint4B
    /*+0x0bc*/ unsigned __int32 NtGlobalFlag; // Uint4B
    /*+0x0c0*/ LARGE_INTEGER CriticalSectionTimeout; // _LARGE_INTEGER
    /*+0x0c8*/ unsigned __int64 HeapSegmentReserve; // Uint8B
    /*+0x0d0*/ unsigned __int64 HeapSegmentCommit; // Uint8B
    /*+0x0d8*/ unsigned __int64 HeapDeCommitTotalFreeThreshold; // Uint8B
    /*+0x0e0*/ unsigned __int64 HeapDeCommitFreeBlockThreshold;  // Uint8B
    /*+0x0e8*/ unsigned __int32 NumberOfHeaps; // Uint4B
    /*+0x0ec*/ unsigned __int32 MaximumNumberOfHeaps; // Uint4B
    /*+0x0f0*/ void *__ptr64 *__ptr64 ProcessHeaps; // Ptr64 Ptr64 Void
    /*+0x0f8*/ void *__ptr64 GdiSharedHandleTable; // Ptr64 Void
    /*+0x100*/ void *__ptr64 ProcessStarterHelper; // Ptr64 Void
    /*+0x108*/ unsigned __int32 GdiDCAttributeList; // Uint4B
    /*+0x10c*/ unsigned char Padding3[4]; // [4] UChar
    /*+0x110*/ RTL_CRITICAL_SECTION *__ptr64 LoaderLock; // Ptr64 _RTL_CRITICAL_SECTION
    /*+0x118*/ unsigned __int32 OSMajorVersion; // Uint4B
    /*+0x11c*/ unsigned __int32 OSMinorVersionl; // Uint4B
    /*+0x120*/ unsigned __int16 OSBuildNumber; // Uint2B
    /*+0x122*/ unsigned __int16 OSCSDVersion; // Uint2B
    /*+0x124*/ unsigned __int32 OSPlatformId; // Uint4B
    /*+0x128*/ unsigned __int32 ImageSubsystem; // Uint4B
    /*+0x12c*/ unsigned __int32 ImageSubsystemMajorVersion; // Uint4B
    /*+0x130*/ unsigned __int32 ImageSubsystemMinorVersion; // Uint4B
    /*+0x134*/ unsigned char Padding4[4]; // [4] UChar
    /*+0x138*/ unsigned __int64 ActiveProcessAffinityMask; // Uint8B
    /*+0x140*/ unsigned __int32 GdiHandleBuffer[60]; // [60] Uint4B
    /*+0x230*/ void *__ptr64 PostProcessInitRoutine; // Ptr64 Void
    /*+0x238*/ void *__ptr64 TlsExpansionBitmap; // Ptr64 Void
    /*+0x240*/ unsigned __int32 TlsExpansionBitmapBits[32]; // [32] Uint4B
    /*+0x2c0*/ unsigned __int32 SessionId; // Uint4B
    /*+0x2c4*/ unsigned char Padding5[4]; // [4] UChar
    /*+0x2c8*/ ULARGE_INTEGER AppCompatFlags; // _ULARGE_INTEGER
    /*+0x2d0*/ ULARGE_INTEGER AppCompatFlagsUser; // _ULARGE_INTEGER
    /*+0x2d8*/ void *__ptr64 pShimData; // Ptr64 Void
    /*+0x2e0*/ void *__ptr64 AppCompatInfo; // Ptr64 Void
    /*+0x2e8*/ UNICODE_STRING CSDVersion; // _UNICODE_STRING
    /*+0x2f8*/ void *__ptr64 ActivationContextData; // Ptr64 _ACTIVATION_CONTEXT_DATA
    /*+0x300*/ void *__ptr64 ProcessAssemblyStorageMap; // Ptr64 _ASSEMBLY_STORAGE_MAP
    /*+0x308*/ void *__ptr64 SystemDefaultActivationContextData; // Ptr64 _ACTIVATION_CONTEXT_DATA
    /*+0x310*/ void *__ptr64 SystemAssemblyStorageMap; // Ptr64 _ASSEMBLY_STORAGE_MAP
    /*+0x318*/ unsigned __int64 MinimumStackCommit; // Uint8B
    /*+0x320*/ void *__ptr64 *__ptr64 FlsCallback; // Ptr64 _FLS_CALLBACK_INFO
    /*+0x328*/ LIST_ENTRY FlsListHead; // _LIST_ENTRY
    /*+0x338*/ void *__ptr64 FlsBitmap; // Ptr64 Void
    /*+0x340*/ unsigned __int32 FlsBitmapBits[4]; // [4] Uint4B
    /*+0x350*/ unsigned __int32 FlsHighIndex; // Uint4B
    /*+0x358*/ void *__ptr64 WerRegistrationData; // Ptr64 Void
    /*+0x360*/ void *__ptr64 WerShipAssertPtr; // Ptr64 Void
    /*+0x368*/ void *__ptr64 pUnused; // Ptr64 Void
    /*+0x370*/ void *__ptr64 pImageHeaderHash; // Ptr64 Void
    union {
        /*+0x378*/ unsigned __int32 TracingFlags; // Uint4B
        struct {
            /*+0x378*/ unsigned __int32 HeapTracingEnabled : 1; // Pos 0, 1 Bit
            /*+0x378*/ unsigned __int32 CritSecTracingEnabled : 1; // Pos 1, 1 Bit
            /*+0x378*/ unsigned __int32 LibLoaderTracingEnabled : 1; // Pos 2, 1 Bit
            /*+0x378*/ unsigned __int32 SpareTracingBits : 29; // Pos 3, 29 Bits
        };
    };
    /*+0x37c*/ unsigned char Padding6[4]; // [4] UChar
    /*+0x380*/ unsigned __int64 CsrServerReadOnlySharedMemoryBase; // Uint8B
    /*+0x388*/ unsigned __int64 TppWorkerpListLock; // Uint8B
    /*+0x390*/ LIST_ENTRY TppWorkerpList; // _LIST_ENTRY
    /*+0x3a0*/ void *__ptr64 WaitOnAddressHashTable[128]; // [128] Ptr64 Void
    /*+0x7a0*/ void *__ptr64 TelemetryCoverageHeader; // Ptr64 Void
    /*+0x7a8*/ unsigned __int32 CloudFileFlags; // Uint4B
    /*+0x7ac*/ unsigned __int32 CloudFileDiagFlags; // Uint4B
    /*+0x7b0*/ char PlaceholderCompatibilityMode; // Char
    /*+0x7b1*/ char PlaceholderCompatibilityModeReserved[7]; // [7] Char
    /*+0x7b8*/ struct LEAP_SECOND_DATA *__ptr64 LeapSecondData; // Ptr64 _LEAP_SECOND_DATA
    union {
        /*+0x7c0*/ unsigned __int32 LeapSecondFlags; // Uint4B
        struct {
            /*+0x7c0*/ unsigned __int32 SixtySecondEnabled : 1; // Pos 0, 1 Bit
            /*+0x7c0*/ unsigned __int32 Reserved : 31; // Pos 1, 31 Bits
        };
    };
    /*+0x7c4*/ unsigned __int32 NtGlobalFlag2; // Uint4B
} PEB;
Я специально комментировал смещения и типы, чтобы было нагляднее приводить структуру в "приемлемый" вид.
Точно также нужно найти все зависимые от PEB остальные структуры (здесь я их переписывать не буду, выложу исходник в конце).
Я не пользуюсь ни Windows.h, ни winternl.h никакими хидерами вообще.

После определения всех необходимых констант воспользуемся регистром rax и прочитаем PEB, содержащий все параметры текущего процесса, доступные из User-Mode
C
1
2
3
4
unsigned __int64 __readgsqword(unsigned __int32 Offset);
#pragma intrinsic(__readgsqword)
 
PEB *__ptr64 peb = (PEB *__ptr64)__readgsqword(0x60); // mov rax, gs:[0x60] (x64)
PEB_LDR_DATA содержит список всех загруженных в данный момент модулей.
Каждый элемент LIST_ENTRY состоит всего из двух указателей вперед (Flink) и назад (Blink).
Чтобы перейти от адреса LIST_ENTRY к адресу структуры, содержащей необходимую информацию, нужно привести указатель к соответствующему типу (ntdll.dll в 99% случаев загружается вторым модулем - сама программа, потом ntdll.dll, и дальше остальное (kernel32, kernelbase, и т.д.), можно конечно сравнить юникод строку и сделать условие)
C
1
2
3
4
5
6
7
8
9
10
11
void *__ptr64 NtDllBase = (void *__ptr64)0;
 
LIST_ENTRY *__ptr64 pList = peb->Ldr->InLoadOrderModuleList.Flink;
for (unsigned __int32 i = 0; i < 2; i++) {
    LDR_DATA_TABLE_ENTRY *__ptr64 pDataTable = (LDR_DATA_TABLE_ENTRY *__ptr64)(pList);
    if (i == 1) {
        NtDllBase = pDataTable->DllBase;
        break;
    }
    else pList = pList->Flink;
}
В pDataTable->BaseDllName лежит имя DLL (в формате UNICODE_STRING), а в pDataTable->DllBase находится адрес, по которому она загружена.
Именно это значение и возвращает функция GetModuleHandle (как тип HMODULE).

Теперь пробежимся по PE заголовку библиотеки ntdll.dll и найдём смещение, по которому лежит функция LdrLoadDll (структуры IMAGE... взяты так же из WinDBG)
C
1
2
3
4
5
6
7
8
9
10
11
12
IMAGE_DOS_HEADER *__ptr64 pDos = (IMAGE_DOS_HEADER *__ptr64)NtDllBase;
IMAGE_NT_HEADERS64 *__ptr64 pNt = (IMAGE_NT_HEADERS64 *__ptr64)((unsigned __int64)NtDllBase + pDos->e_lfanew);
IMAGE_EXPORT_DIRECTORY *__ptr64 pExpDir = (IMAGE_EXPORT_DIRECTORY *__ptr64)((unsigned __int64)NtDllBase + pNt->OptionalHeader.DataDirectory[0].VirtualAddress);
 
unsigned __int32 *__ptr64 AddrFunc = (unsigned __int32 *__ptr64)((unsigned __int64)NtDllBase + pExpDir->AddressOfFunctions);
 
// номер функции ===LdrLoadDll=== в таблице экспорта ntdll.dll = 138
unsigned __int32 *__ptr64 fn_LdrLoadDll = (unsigned __int32 *__ptr64)0;
for (; 138 < pExpDir->NumberOfNames;) {
    fn_LdrLoadDll = (unsigned __int32 *__ptr64)((unsigned __int64)NtDllBase + AddrFunc[138]);
    break;
}
Далее находим в интернете прототип функции LdrLoadDll и определяем его как fn_LdrLoadDll (точно также если бы это делали через GetProcAddress)
C
1
2
3
4
5
6
7
8
9
typedef __int32(__stdcall *__ptr64 _LdrLoadDll)(
    unsigned short *__ptr64 PathToFile,
    unsigned __int32 Flags,
    UNICODE_STRING *__ptr64 ModuleFileName,
    void *__ptr64 *__ptr64 ModuleHandle
    );
_LdrLoadDll LdrLoadDll;
 
LdrLoadDll = (_LdrLoadDll)fn_LdrLoadDll;
И последний штрих - заполняем LdrLoadDll необходимыми данными (для примера взята 64 битная библиотека sqlite3.dll)
C
1
2
3
4
5
6
UNICODE_STRING LibraryName;
LibraryName.Length = sizeof(L"sqlite3.dll") - sizeof(unsigned short);
LibraryName.MaximumLength = sizeof(L"sqlite3.dll");
LibraryName.Buffer = L"sqlite3.dll";
void *__ptr64 hModule = (void *__ptr64)0;
LdrLoadDll((void *__ptr64)0, 0, &LibraryName, &hModule);
В Module будет находится адрес загруженной в память sqlite3.dll (можно проверить любым MemoryViewer'ом).

Вот собственно и всё, код черновой и его можно привести в надлежащий вид.
Исходник вместе с sqlite3.dll (VS 2019 Preview) LdrLoader.rar



Добавлено
В продолжении темы ещё покажу как в загруженной через LdrLoadDll библиотеке найти смещение нужной функции (можно конечно распарсить её PE).
Для этого воспользуемся функцией LdrGetProcedureAddress из всё той же ntdll.dll
Прототип
C
1
2
3
4
5
6
7
typedef __int32(__stdcall *__ptr64 _LdrGetProcedureAddress)(
    void *__ptr64 ModuleHandle,
    STRING *__ptr64 FunctionName,
    unsigned __int16 Oridinal,
    void *__ptr64 *__ptr64 FunctionAddress
    );
_LdrGetProcedureAddress LdrGetProcedureAddress;
Найдём адрес её смещения в ntdll.dll и определяем как fn_LdrGetProcedureAddress
C
1
2
3
4
5
6
7
8
// номер функции ===LdrGetProcedureAddress=== в таблице экспорта ntdll.dll = 129
unsigned __int32 *__ptr64 fn_LdrGetProcedureAddress = (unsigned __int32 *__ptr64)0;
for (; 129 < pExpDir->NumberOfNames;) {
    fn_LdrGetProcedureAddress = (unsigned __int32 *__ptr64)((unsigned __int64)NtDllBase + AddrFunc[129]);
    break;
}
 
LdrGetProcedureAddress = (_LdrGetProcedureAddress)fn_LdrGetProcedureAddress;
Заполняем данные и получаем адрес функции sqlite3_bind_int64 в fnAddress
C
1
2
3
4
5
6
STRING FunctionName;
FunctionName.Length = sizeof("sqlite3_bind_int64") - sizeof(char);
FunctionName.MaximumLength = sizeof("sqlite3_bind_int64");
FunctionName.Buffer = "sqlite3_bind_int64";
void *__ptr64 fnAddress = (void *__ptr64)0;
LdrGetProcedureAddress(hModule, &FunctionName, 0, &fnAddress);


Добавлено
Пожалуй финальным штрихом этого блога будет объяснение как отключить CRT и VC runtime в данном проекте.
Для чего это нужно? Во-первых избавиться от лишнего импорта (когда работаешь достаточно близко к нативу, импорт из юзермодных либ совершенно не желателен). А во-вторых, уменьшиться вес программы (в данном примере релизная сборка уменьшилась с 10кб до 3кб).

Итак, по порядку:
1. Открываем свойства проекта, идём в C\C++ -> Создание кода -> Основные проверки времени выполнения (ставим "По умолчанию"), Проверка безопасности ("Отключить проверку безопасности (/GS-)")
2. C\C++ -> Дополнительно -> Пропускать имена стандартных библиотек (ставим "Да (/ZI)")
3. Переходим Компоновщик -> Ввод -> Игнорировать все стандартные библиотеки (ставим "Да (/NODEFAULTLIB)")
4. Определяем точку входа в коде программы как
C
1
void mainCRTStartup(void) { }
5. Опционально: в C\C++ отключаем проверку SDL, выбираем Без поддержки CLR, соглашение о вызове vectorcall и компилировать как код C.
Размещено в Без категории
Просмотров 316 Комментарии 0
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2019, vBulletin Solutions, Inc.
Рейтинг@Mail.ru