Pull to refresh

Пример использования недокументированной функции Windows

Reading time 3 min
Views 15K
Большая часть программ от Sysinternals использует недокументированные функции. Мне хватило этого факта, чтобы заинтересоваться этой темой. Интересно, как крутые дядьки используют неописанные функции в своих не менее крутых программах.

Предполагаем, что мы в нужной степени ленивые программисты, знаем С, в ладах с WinAPI и с архитектурой современной ОС Windows и у нас есть Ida Pro, хе-хе. Хотим красиво, быстро и эффективно выполнить задачу, не изобретая велосипед (и чтоб ещё сильно не перенапрячь руки и голову).

Поищем подопытную функцию. Много вкусного можно найти в ntdll.dll. Сам я пишу из-под Win7 64, но взял 32-битную версию чудесной библиотеки. На всякий случай: %SystemDisk%\Windows\System32\ntdll.dll.

Чтобы было просто, откроем ntdll в Ida и посмотрим, какие функции экспортируются. Если нет Ida, то можно взять любую программу работающую с PE-файлами (например, PETools). Нас интересуют функции с приставкой Rtl (Run Time Library). То есть во время выполнения нашего кода мы можем попросить эту системную функцию об услуге.

После небольшого поиска простенькой функции, таковая нашлась — RtlComputeCrc32.



Двойным щелчком по имени функции получаем её дизассемблированный код. Изучать функцию можно и любым другим дизассемблером вроде HDasm или W32Dasm. Чтобы не тратить место, приведу псевдокод RtlComputeCrc32, любезно предоставленный декомпилятором Ida (в теле функции нажать F5, если Hex-Rays Decompiler имеется в плагинах Edit->Plugins).



Сразу получаем много информации! Надо подумать, что мы, собственно, ищем. Нам нужно:
1) имя функции, чтобы получить её адрес в ntdll;
2) прототип функции, чтобы создать правильный указатель на неё;
3) примерный принцип работы, чтобы передать ей корректные аргументы и правильно обработать результат;

Пункты 1-2 у нас уже есть из псевдокода. Задача наша теперь разобраться в функции и на её основе написать программу, высчитывающую CRC32 от чего-то.

По псевдокоду легко понять, что функция перебирает байты массива a2, размер которого a3, а а1 — инициализирующее значение алгоритма. Проделав вычисления с байтами, получает индекс из таблицы RtlCrc32Table (двойной щелчок покажет монструозную таблицу). Гуглим CRC32 и примеры реализации и понимаем, что всё верно.

Дело за малым — воспользоваться добычей. Я сделал пустое консольное приложение в Visual Studio, но делать, естественно, можно и в любой другой среде.

GetModuleHandle() возвращает хэндл ntdll, GetProcAddress() — адрес функции. Используем указатель на функцию типа UndocFoo для вызова RtlComputeCRC32().

#include <stdio.h>
#include <windows.h>

typedef INT (WINAPI *UndocFoo)(INT accumCRC32, const BYTE* buffer, UINT buflen);

int main()
{
	HMODULE hDLL = GetModuleHandle(TEXT("ntdll.dll"));
	if (hDLL == NULL)
	{
		puts("[-] Failed to find ntdll.dll\n");
		return EXIT_FAILURE;
	}
	puts("[+] Got ntdll.dll handle\n");

	UndocFoo ComputeCrc32 = (UndocFoo)GetProcAddress(hDLL, "RtlComputeCrc32");
	if (ComputeCrc32 == NULL)
	{
		puts("[-] Failed to find RtlComputeCrc32\n");
		return EXIT_FAILURE;
	}
	puts("[+] Found RtlComputeCrc32 address\n");

	puts("[*] Calling RtlComputeCrc32...\n");

	BYTE buffer[] = {0x01, 'a', 7};

	INT iCRC32 = ComputeCrc32(INT(0), (BYTE*)buffer, 3);

	printf("[+] Computed CRC32 --> 0x%x\n\n", iCRC32);
	
	puts("Press any key to quit\n");
	getchar();
	return EXIT_SUCCESS;
}




Успех. Проверить можно с помощью любого онлайн-вычислителя.
Наши байты 016107 дали CRC32 = 0x1c017c60.



То же самое выдал онлайн-вычислитель:

"

Вот так, без траты времени на реализацию собственной функции или использования чужого большого кода, мы сделали такую чудесную программку. Было несложно и весело.
Tags:
Hubs:
+80
Comments 75
Comments Comments 75

Articles