У меня есть программа, которая загружает DLLs, и я должен вызвать одну из неэкспортируемых функций, которые это содержит. Есть ли какой-либо способ, которым я могу сделать это через поиск в отладчике или иначе? Прежде чем любой спросит, да у меня есть прототипы и материал для функций.
Боюсь, что не существует "безопасного" способа сделать это, если указанная библиотека не экспортирует явно свой объект (класс / функция). Потому что вы не будете знать, где в памяти кода отображается требуемый объект.
Однако, используя инструменты RE, вы можете найти смещение для интересующего объекта в библиотеке, а затем добавить его к любому известному адресу экспортируемого объекта, чтобы получить «реальную» ячейку памяти. После этого подготовьте прототип функции и т. Д. И преобразуйте его в вашу локальную структуру для использования.
Да, по крайней мере, вроде как, но это плохая идея.
В C / C ++ указатель на функцию - это адрес в памяти. Итак, если вы каким-то образом можете найти адрес этой функции, вы можете ее вызвать.
Однако позвольте мне задать несколько вопросов: откуда вы знаете, что эта DLL содержит эту функцию? У вас есть исходный код? В противном случае я не знаю, как вы могли узнать наверняка, что эта функция существует и безопасно ли ее вызывать. Но если у вас есть исходный код, просто выставьте функцию. Если писатель DLL не предоставил эту функцию, они никогда не ожидают, что вы ее вызовете, и могут изменить / удалить реализацию в любое время.
Помимо предупреждений, вы можете найти адрес функции, если у вас есть символы отладки, или MAP-файл , вы можете найти смещение в DLL. Если у вас нет ничего, кроме DLL, то нет никакого способа узнать, где эта функция существует в DLL - она не хранится в самой DLL.
Как только у вас есть смещение, вы можете вставить его в код следующим образом:
const DWORD_PTR funcOffset = 0xDEADBEEF;
typedef void (*UnExportedFunc)();
....
void CallUnExportedFunc() {
// This will get the DLL base address (which can vary)
HMODULE hMod = GetModuleHandle("My.dll");
// Calcualte the acutal address
DWORD_PTR funcAddress = (DWORD_PTR)hMod + funcOffset;
// Cast the address to a function poniter
UnExportedFunc func = (UnExportedFunc)funcAddress;
// Call the function
func();
}
Также поймите, что смещение этой функции БУДЕТ ИЗМЕНЯТЬСЯ КАЖДЫЙ РАЗ, когда DLL перестраивается, поэтому это очень хрупко, и позвольте мне еще раз сказать, что не хорошая идея.
Если нужная вам функция не экспортирована, то ее не будет в таблице адресов экспорта. Если предположить, что для создания этой DLL использовалась Visual Studio и у вас есть соответствующий файл PDB (база данных программ), то вы можете использовать API Microsoft DIA (доступ к интерфейсу отладки), чтобы найти нужную функцию либо по имени, либо, приблизительно, по сигнатуре.
Получив функцию (символ) из PDB, вы также получите ее RVA (относительный виртуальный адрес). Вы можете добавить RVA к базовому адресу загруженного модуля, чтобы определить абсолютный виртуальный адрес в памяти, где хранится функция. Затем вы можете выполнить вызов функции по этому адресу.
В качестве альтернативы, если вам нужно сделать что-то одно (т.е. вам не нужно программное решение), вы можете использовать windbg.exe из набора Debugging Tools for Windows для подключения к процессу и обнаружения адреса функции, которая вас интересует. В WinDbg вы можете использовать команду x
для "изучения символов" в модуле.
Например, вы можете выполнить x mymodule!*foo*
, чтобы увидеть все функции, имя которых содержит "foo". Если у вас загружены символы (PDB) для вашего модуля, это покажет вам и неэкспортные функции. Используйте .hh x
для получения справки по команде x
.
Самый общий способ сделать это (и это все еще плохая идея, как уже отмечали все остальные) - сканировать код DLL во время выполнения после его загрузки и искать известный уникальный фрагмент кода в этой функции. , а затем используйте код, аналогичный приведенному в ответе shf301, чтобы вызвать его. Если вы знаете, что DLL никогда не изменится, тогда любое решение, основанное на определении смещения в DLL, должно работать.
Чтобы найти этот уникальный фрагмент кода, дизассемблируйте DLL с помощью дизассемблера, который может показать вам машинный код в дополнение к мнемонике языка ассемблера (я не могу придумать ничего, что бы этого не сделало) и остерегайтесь вызов и инструкции jmp.
Мне действительно пришлось однажды сделать нечто подобное, чтобы применить бинарный патч к исполняемой программе DOS; это было исправление ошибки, и код не находился под контролем версий, так что это был единственный способ исправить это.
Кстати, мне было бы очень любопытно узнать, зачем вам это нужно.