В разделе импорта заголовка исполняемого файла содержится информация о подключаемых библиотеках (DLL) и импортируемых из них функциях. Для подмены одной из DLL необходимо, чтобы имя библиотеки и набор функций совпадали с именем библиотеки и набором функций, описанными в разделе импорта исполняемого файла. Для упрощения разработки подменяемой библиотеки DLL, из всех библиотек выбирают ту, из которой импортируется наименьшее количество функций.
Из предоставленного образа раздела импорта определите имя библиотеки, из которой импортируется наименьшее количество функций.
В ответе укажите имя библиотеки DLL, имена импортируемых из нее функций и количество аргументов каждой из таких функций.
Структура раздела импорта показана на рисунке.
Рисунок. Структура раздела импорта
В конце таблицы записывается 4 нулевых байта (0x00000000).
Все адреса и числовые значения хранятся в формате Little-Endian. Адреса указываются относительно начала раздела импорта, адрес которого считается равным 0x00000000.
файл образа раздела импорта dump_v1.bin.
В HEX-редакторе проще найти ответ.
Для решения задачи необходимо отыскать все импортируемые библиотеки. Далее для каждой из найденных определить количество импортируемых функций. После этого уже возможно определить, какая именно библиотека является наиболее подходящей, и для нее необходимо получить названия всех функций.
Элементами структур являются смещения относительно начала дампа, соответственно для получения необходимых данных нужно корректно определить адреса всех структур LibraryInfo, NamesTable, OrdinalsTable, а также адреса строк. Можно предложить 2 способа решения:
- ручной поиск (наиболее удобно использовать HEX-редактор);
- автоматизированный с использованием программы.
Способ 1.
Откроем предоставленный дамп в HEX-редакторе. Зная формат структуры ImportTable несложно выделить (см. рисунок 4.1-1) смещения трех структур LibraryInfo: 0x000003E8, 0x000004a9 и 0x00000539 (байты расположены в обратном порядке согласно little-endian).
Рисунок 4.1-1 – Структура ImportTable
Используя инструмент перехода по смещению (Search – Go to или горячее сочетание Alt+G) перейдем по адресу первой структуры LibraryInfo.
Рисунок 4.1-2 – Переход по смещению
Зная формат структуры LibraryInfo можно выделить адрес названия библиотеки, адреса таблицы имен и ординалов импортируемых функций.
Рисунок 4.1-3 – Структура LibraryInfo первой библиотеки
На текущем этапе решения нет необходимости получать названия библиотеки, пока достаточно определить только количество импортируемых функций. Для этого нужно перейти либо к таблице имен, либо к таблице ординалов. Перейдем по адресу 0x0000088С и, зная формат NamesTable, посчитаем количество импортируемых из первой библиотеки функций.
Рисунок 4.1-4 – Таблица имен функций первой библиотеки
На рисунке 4.1-4 отмечены смещения имен импортируемых функций, количество которых равно четырем. Синим отмечено число параметров для каждой функции: 4, 2, 5 и 3.
Аналогичным образом отыщем количество импортируемых функций для второй и третьей библиотек.
На рисунке 4.1-5 приведена структура LibraryInfo (смещение 0x000004A9), а на рисунке 4.1-6 – таблица имен второй импортируемой библиотеки (смещение 0x000007D0), из которой импортируется три функций. Синим на рисунке отмечены количества параметров функций: 6, 2, 1.
Рисунок 4.1-5 – Структура LibraryInfo второй библиотеки
Рисунок 4.1-6 – Таблица имен функций второй библиотеки
Осталось проделать те же действия для третьей библиотеки. На рисунке 4.1-7 приведена структура LibraryInfo (смещение 0x00000539), а на рисунке 4.1-8 – таблица имен второй импортируемой библиотеки (смещение 0x000008E4), из которой импортируется пять функций. Синим на рисунке отмечены количества параметров функций: 6, 2, 3, 4 и 2.
Рисунок 4.1-7 – Структура LibraryInfo третьей библиотеки
Рисунок 4.1-8 – Таблица имен функций третьей библиотеки
Таким образом, наименьшее количество функций (три) импортируется из второй библиотеки. Теперь необходимо найти ее название, а также названия и количество аргументов всех функций. На рисунке 4.1-5 в первом прямоугольнике выделено смещение до строки с названием библиотеки. Перейдя по смещению 0x0000278C получим нужное значение (см. рисунок 4.1-9) – cShv.dll.
Рисунок 4.1-9 – Название второй библиотеки
Далее необходимо получить названия импортируемых функций. Соответствующие смещения представлены на рисунке 4.6: 0x00002EA9, 0x000032F0 и 0x0000330D. По указанным смещениям расположены искомые строки: (см. рисунок 4.1-10, рисунок 4.1-11 и рисунок 4.1-12) – swap, GetLastError, GetEvent
Рисунок 4.1-10 – Имя первой функции
Рисунок 4.1-11 – Имя второй функции
Рисунок 4.1-12 – Имя третьей функции
Осталось получить ординалы этих функций. Таблица ординалов расположена по смещению 0x000BF5(третий прямоугольник на рисунке 4.1-5). Зная формат таблицы OrdinalsTable получаем ординалы функций: 2, 7, 16 (см. рисунок 4.1-13).
Рисунок 4.1-13 – Ординалы импортируемых функций
Количество параметров каждой функции можно получить либо из таблицы имен (рисунок 4.1-6), либо из таблицы ординалов (рисунок 4.1-13) (на рисунках отмечены синим овалом): 6, 2, 1. Дублирование количества параметров также позволяет легко проверить правильность решения – значения должны совпадать.
Ответ: Shv.dll – swap(0x02): 6, GetLastError(0x07): 2, GetEvent(0x10): 1.
Способ 2
Для решения задачи также можно написать несложную программу: объявить соответствующие структуры и приведением типов получить нужные значения. Код такой программы на языке программирования C++ приведен в листинге 4.1-1.
Листинг 4.1-1. Листинг программы на языке программирования C++
struct NameInfo
{
int NameOffset;
int ParamsCount;
};
struct OrdinalInfo
{
int Ordinal;
int ParamsCount;
};
struct LibraryInfo
{
int NameOffset;
int NamesTableOffset;
int OrdinalsInfoOffset;
};
struct Import
{
int LibInfoOffset;
};
int main()
{
char buffer[30000];
// Считывание всего файла
auto dumpFile = fopen("dump_v1.bin", "rb");
fread(buffer, 1, 30000, dumpFile);
fclose(dumpFile);
auto libraries = (Import*)buffer;
// Цикл по всем структурам LibraryInfo
while(libraries->LibInfoOffset != 0)
{
auto library = (LibraryInfo*)&buffer[libraries->LibInfoOffset];
// Вывод названия библиотеки
std::cout << "Library: ";
std::cout << &buffer[library->NameOffset] << std::endl;
std::cout << "Functions:" << std::endl;
// Получение таблиц имен и ординалов функций
auto names = (NameInfo*)&buffer[library->NamesTableOffset];
auto ordinals = (OrdinalInfo*)&buffer[library->OrdinalsInfoOffset];
// Цикл по всем импортируемым функциям
while(names->NameOffset != 0)
{
// Вывод имени, количества параметров и ординала
std::cout << "Name: " << &buffer[names->NameOffset] << " ";
std::cout << "Params: " << names->ParamsCount << " ";
std::cout << "Ordinal: " << ordinals->Ordinal << std::endl;
++names;
++ordinals;
}
++libraries;
}
}
В результате выполнения программа выводит на экран следующее.
Library: Dstp.dll
Functions:
Name: OpenFile Params: 4 Ordinal: 6
Name: CreatePipe Params: 2 Ordinal: 7
Name: CreateProcess Params: 5 Ordinal: 22
Name: GetErrorMessage Params: 3 Ordinal: 32
Library: Shv.dll
Functions:
Name: swap Params: 6 Ordinal: 2
Name: GetLastError Params: 2 Ordinal: 7
Name: GetEvent Params: 1 Ordinal: 16
Library: Ppte.dll
Functions:
Name: Search Params: 6 Ordinal: 2
Name: random Params: 2 Ordinal: 8
Name: Print Params: 3 Ordinal: 26
Name: CopyFile Params: 4 Ordinal: 37
Name: CreateThread Params: 2 Ordinal: 41
Однако поскольку количество импортируемых библиотек в представленном дампе файла небольшое, автоматизировать поиск нецелесообразно.