Вы можете вывести количество аргументов, которые функция использует в 32-битном коде x86 при некоторых обстоятельствах.
Если код был скомпилирован для использования framepointers , тогда стековый фрейм данной функции простирается между (самый высокий адрес) EBP
и (самый низкий адрес / вершина стека) ESP
.Сразу над концом стека в EBP
вы найдете обратный адрес , и снова выше, чем у вас, если ваш код использует соглашение о вызовах C (cdecl
), последовательно, arg[0...]
.
Это означает: arg[0]
в [EBP + 4]
, arg[1]
в [EBP + 8 ]
и т. д.
Когда вы разбираете функцию, ищите инструкцииссылаясь на [EBP + ...]
, и вы знаете, что они получают доступ к аргументам функции.Максимальное используемое значение смещения говорит вам, сколько их.
Это, конечно, несколько упрощено;Аргументы с размерами, отличными от 32-битных, код, который не использует cdecl
, например, fastcall
, код, в котором был оптимизирован указатель кадра, вызывает отключение метода, по крайней мере, частично.
Другой вариант, опять же для cdecl
функции, это посмотреть на обратный адрес (расположение call
в интересующем вас функционале) и разобрать там;во многих случаях вы найдете последовательность push argN; push ...; push arg0; call yourFunc
и сможете определить, сколько аргументов было передано в этом случае.Фактически это единственный способ (из одного кода) проверить, сколько аргументов было передано функциям, таким как printf()
в конкретном случае.Опять же, не идеально - в наши дни компиляторы часто предварительно выделяют пространство стеков, а затем используют mov
для записи аргументов вместо их выдвижения (на некоторых процессорах это лучше, поскольку последовательности команд push
имеют зависимости друг от друга из-за каждого измененияуказатели стека).
Поскольку все эти методы эвристические, для автоматизации требуется немало кодирования.Если сгенерированная компилятором отладочная информация доступна, используйте ее - это быстрее.
Редактировать: Есть еще одна полезная эвристика, которую можно сделать;Генерируемый компилятором код для вызова функции часто выглядит следующим образом:
...
[ code that either does "push arg" or "mov [ESP ...], arg" ]
...
call function
add ESP, ...
Инструкция add
предназначена для очистки стека, используемого для аргументов .Из размера непосредственного операнда вы знаете, сколько места заняли аргументы, которые этот код дал function
, и косвенно (если предположить, что они все 32-битные, например), вы знаете, сколько их было.
Это особенно просто, учитывая, что у вас уже есть адрес указанной add
инструкции, если у вас есть рабочий код возврата - инструкция на обратном адресе это add
.Таким образом, вы часто можете просто попытаться разобрать (одиночную) инструкцию по адресу возврата и посмотреть, является ли она add ESP, ...
(иногда это sub ESP, -...
), и если это так, вычислить количество аргументов, переданных из непосредственногооперанд.Код для этого гораздо проще, чем загружать полную библиотеку дизассемблирования.