Допустим, у меня есть такая функция (совершенно случайная, я просто написал ее за 30 секунд для примера)
bool exampleAuthetnication(char *a, char *b)
{
bool didAuthenticate = false;
if(strcmp(a, b) == 0)
{
didAuthenticate = true;
}
if(didAuthenticate)
{
return true;
}
else
{
stopExecutable();
return false;
}
}
Как бы я прочитал первые несколько байтов этой функции?
Я придумал это
int functionByteArray[10];
for (int i = 0; i < 10; i++)
{
functionByteArray[i] = *(int*)(((int)&exampleAuthetnication) + (0x04 * i));
}
Логика заключается в том, что мы получаем адрес памяти нашей функции (в данном случае exampleAuthetnication()
) затем мы приводим к указателю int, затем разыменовываем, чтобы получить значение текущей строки байтов, которые мы пытаемся прочитать, а затем сохранить в functionByteArray
, но, похоже, не работает должным образом. Что я делаю неправильно? Возможно ли то, что я пытаюсь достичь?
Теоретически (в соответствии со стандартом C ++ 11) вы даже не можете привести указатель функции в указатель данных (на Гарвардские архитектуры код и данные находятся в разных ячейках памяти и разных адресных пространствах). Некоторые операционные системы или процессоры также могут запрещать чтение исполняемых файлов. сегменты кода (прочитать о Бит NX).
На практике на x86-64 (или 32-битных x86), работающих под управлением какой-либо операционной системы, такой как Linux или Windows, код функции представляет собой последовательность байтов и может быть не выровненным, и находится в (общем) виртуальном адресном пространстве своего процесса. Так что вы должны хотя бы иметь char functionByteArray[40];
и вы могли бы использовать станд :: тетсру от <string>
и сделать некоторые
std::memcpy(functionByteArray, (char*)&exampleAuthetnication,
sizeof(functionByteArray));
Наконец ваш код неверен, потому что -on x86-64 int
не имеют такой же размер, как указатели (так (int)&exampleAuthetnication
теряет старшие байты адреса). Вы должны хотя бы использовать intptr_t
, А также int
имеет более строгие ограничения выравнивания, чем код.
Кстати, вы также можете попросить ваш компилятор показать сгенерированный ассемблерный код. С НКУ скомпилируйте exampleAhtetnication
Код C ++ с g++ -O -fverbose-asm -S
и посмотреть в сгенерированный .s
файл.
Обратите внимание, что компилятор C ++ может оптимизировать до точки «удаления» некоторой функции из сегмента кода (например, потому что эта функция была встроена везде), или разделить код функции на несколько частей, или поместить это
exampleAhtetnication
код «внутри» другой функции …
Исходный код C ++ — это не список инструкций для компьютера; это набор утверждений, которые опишите смысл программы.
Ваш компилятор интерпретирует эти операторы и выдает фактическую последовательность инструкций (через этап сборки), которые фактически могут быть выполнены в нашей физической реальности.
Используемый для этого язык не предоставляет никаких средств для проверки байтов, составляющих скомпилированную программу. Все ваши попытки приведения указателей на функции и тому подобное могут случайным образом дать вам некоторые похожие данные, используя магию неопределенного поведения, но результаты просто так: undefined.
Если вы хотите проверить скомпилированный исполняемый файл, сделайте это снаружи программы. Вы можете использовать шестнадцатеричный редактор, например.