ImportTable – таблица импорта. Хранится в начале раздела импорта. Содержит адреса (4-х байтовые) на структуры LibraryInfo. В конце таблицы записывается 4 нулевых байта (0x00000000).
LibraryInfo – структура, содержащая информацию о библиотеке. Содержит поля:
Name – адрес строки (4 байта), содержащей имя библиотеки. Строка заканчивается нулевым байтом ('\0');
NamesTable – адрес таблицы имен импортируемых функций (4 байта);
OrdinalsTable – адрес таблицы идентификаторов импортируемых функций (4 байта).
NamesTable – таблица имен импортируемых функций. Каждая запись содержит следующие параметры:
Fun_Name – адрес строки (4 байта), содержащей имя функции. Строка заканчивается нулевым байтом ('\0');
Fun_ParamsCount – количество аргументов функции (4 байта).
В конце таблицы записывается 4 нулевых байта (0x00000000).
OrdinalsTable – таблица идентификаторов импортируемых функций. Каждая запись содержит следующие параметры:
Fun_Ordinal – идентификатор функции (4 байта);
Fun_ParamsCount – количество аргументов функции (4 байта).
В конце таблицы записывается 4 нулевых байта (0x00000000).
Для решения задачи необходимо отыскать все импортируемые библиотеки, для каждой из них определить количество импортируемых функций. После этого уже возможно определить, какая именно библиотека является наиболее подходящей, и для нее необходимо получить названия всех функций.
Элементами структур являются смещения относительно начала дампа, соответственно для получения необходимых данных нужно корректно определить адреса всех структур LibraryInfo, NamesTable, OrdinalsTable, а также адреса строк. Можно предложить 2 способа решения:
- ручной поиск (наиболее удобно использовать HEX-редактор);
- автоматизированный с использованием программы.
Способ 1.
Откроем предоставленный дамп в HEX-редакторе. Зная формат структуры ImportTable несложно выделить (см. рисунок 4.2-1) смещения трех структур LibraryInfo: 0x000003E8, 0x0000044A и 0x000004EA (байты расположены в обратном порядке согласно little-endian).
Рисунок 4.2-1 – Структура ImportTable
Используя инструмент перехода по смещению (Search – Go to или горячее сочетание Alt+G) перейдем по адресу первой структуры LibraryInfo.
Рисунок 4.2-2 – Переход по смещению
Зная формат структуры LibraryInfo можно выделить адрес названия библиотеки, адреса таблицы имен и ординалов импортируемых функций.
Рисунок 4.2-3 – Структура LibraryInfo первой библиотеки
На текущем этапе решения нет необходимости получать названия библиотеки, пока достаточно определить только количество импортируемых функций. Для этого нужно перейти либо к таблице имен, либо к таблице ординалов. Перейдем по адресу 0x0000094E и, зная формат NamesTable, посчитаем количество импортируемых из первой библиотеки функций.
На рисунке 4.2-4 отмечены смещения имен импортируемых функций, количество которых равно пяти. Синим на рисунке отмечены количества параметров функций: 3, 3, 3, 4 и 2.
Рисунок 4.2-4 – Таблица имен функций первой библиотеки
Аналогичным образом отыщем количество импортируемых функций для второй и третьей библиотек.
На рисунке 4.2-5 приведена структура LibraryInfo (смещение 0x0000044A), а на рисунке 4.2-6 – таблица имен второй импортируемой библиотеки (смещение 0x00000870), из которой импортируется четыре функций. Синим на рисунке отмечены количества параметров функций: 3, 6, 4, 2.
Рисунок 4.2-5 – Структура LibraryInfo второй библиотеки
Рисунок 4.2-6 – Таблица имен функций второй библиотеки
Осталось проделать те же действия для третьей библиотеки. На рисунке 4.2-7 приведена структура LibraryInfo (смещение 0x000004EA), а на рисунке 4.2-8 – таблица имен второй импортируемой библиотеки (смещение 0x000007D0), из которой импортируется три функции. Синим на рисунке отмечены количества параметров функций: 6, 1, 6.
Рисунок 4.2-7 – Структура LibraryInfo третьей библиотеки
Рисунок 4.2-8 – Таблица имен функций третьей библиотеки
Таким образом, наименьшее количество функций (три) импортируется из третьей библиотеки. Теперь необходимо найти ее название, а также названия и количество аргументов всех функций.
На рисунке 4.2-7 в первом прямоугольнике выделено смещение до строки с названием библиотеки. Перейдя по смещению 0x0000291C получим нужное значение (см. рисунок 4.2-9) – Ppte.dll.
Рисунок 4.2-9 – Название второй библиотеки
Далее необходимо получить названия импортируемых функций. Соответствующие смещения представлены на рисунке 4.2-8: 0x000034C2, 0x00003374 и 0x00002B27. По указанным смещениям расположены искомые строки: (см. рисунок 4.2-10, рисунок 4.2-11 и рисунок 4.2-12) – Connect, CreateMutex, Terminate.
Рисунок 4.2-10 – Имя первой функции
Рисунок 4.2-11 – Имя второй функции
Рисунок 4.2-12 – Имя третьей функции
Осталось получить ординалы этих функций. Таблица ординалов расположена по смещению 0x000C99 (третий прямоугольник на рисунке 4.2-7). Зная формат таблицы OrdinalsTable получаем ординалы функций: 4, 6, 9 (см. рисунок 4.2-13). Синим на рисунке отмечены количества параметров функций: 6, 1, 6.
Рисунок 4.2-13 – Ординалы импортируемых функций
Количество параметров каждой функции можно получить либо из таблицы имен (рисунок 4.2-8), либо из таблицы ординалов (рисунок 4.2-13): 6, 1, 6. Дублирование количества параметров также позволяет легко проверить правильность решения – значения должны совпадать.
Ответ: Ppte.dll – Connect(0x04): 6, CreateMutex(0x06): 1, Terminate(0x09): 6.
Способ 2
Для решения задачи также можно написать несложную программу: объявить соответствующие структуры и приведением типов получить нужные значения. Код такой программы на языке программирования C++ приведен в листинге 4.2-1.
Листинг 4.2-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_v2.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: Vmxops.dll
Functions:
Name: LoadLibrary Params: 3 Ordinal: 8
Name: OpenFile Params: 3 Ordinal: 9
Name: SubscribeForEvent Params: 3 Ordinal: 10
Name: strcpy Params: 4 Ordinal: 12
Name: GetErrorMessage Params: 2 Ordinal: 20
Library: Trsx.dll
Functions:
Name: swap Params: 3 Ordinal: 3
Name: CreateProcess Params: 6 Ordinal: 7
Name: random Params: 4 Ordinal: 9
Name: CopyFile Params: 2 Ordinal: 26
Library: Ppte.dll
Functions:
Name: Connect Params: 6 Ordinal: 4
Name: CreateMutex Params: 1 Ordinal: 6
Name: Terminate Params: 6 Ordinal: 9
Однако поскольку количество импортируемых библиотек в представленном дампе файла небольшое, автоматизировать поиск нецелесообразно.