Кто нибудь может привести пример кода, в котором после компиляции функции идут в другом порядке? [add] VS C++
crypto Повторюсь - функция не обязана заканчиваться выходом. Применительно к примеру выше: Код (Text): function() { if(<..>) { for(;;) { print "Уррааа!" } } <код> <характерная конструкция> return; } имеет только один выход. И запросто преобразуется в Код (Text): function() { if(!<..>) { <код> <характерная конструкция> return; } for(;;) { print "Уррааа!" } } По-настоящему прочувствовать все многообразие code flow можно только написав или попытавшись написать декомпилятор
Вставлю-ка и я свои пять копеек =) Вот все говорят - лови RETN, лови RETN... А если наоборот ловить не выход из функции, а следующий за ним идентификатор начала следующей? Насколько мне позволяют судить мои практически никакие знания С, в случае не-VOID функции она должна начинаться с PUSH EBP + MOV EBP,ESP т.е. подготовки кадра стэка. Может быть, можно сыграть на этом? зы. Да, грабли есть и здесь. 1. А вдруг это вообще последняя функция? Тогда, видимо, придётся проверять на ведущие нули в сегменте - если их больше некоего количества - значит функций больше нету. 2. Если всё же у функции нет парамов. Тут возможны лишь две ситуации: две функции разделяет или RETN, или JMP на вечный цикл. Как обрабатывать этот случай - пока не знаю) Скорее всего, компиль всё-таки вставляет между функциями хоть один байт "мусора" типа INT3 (встречалось мне пару раз при дизасмее сишного кода) или db 00, хотя хз, если честно.
wsd А чем "мусор" между функциями хуже твоих 7-11 байт мусора push eax и т.д. Свой мусор ближе к телу ? А ты разве не тыками занимаешься ? В какой доке сказано, что твоя асм-вставка будет расположена непременно в конце функции ? К тому же со вставкой приходится оптимизацию отключать, а на функцию-пустышку достаточно просто сослаться, чтобы линкер ее не выкинул. Со вставкой нужно еще сигнатуру найти и последующий ret, а с функцией - только разность указателей. А последовательное расположение ф-й в коде является вполне естественным, т.к. компилятор компилит все подряд, а линкер лишь выкидывает неиспользуемые функции, не изменяя порядка их следования, т.к. это проще, быстрее и следовательно целесообразнее, поскольку на изменении порядка в общем случае ничего не соптимизируешь
Stiver так если мы сигнатурим последний RET, то соответственно все промежуточные реты туда войдут. а зачем сигнатурить промежуточные реты? т.е. текстуально последний рет включает весь набор ретов.
вставляется уникальная сигнатура в начале функции и в самом конце (6-10 байт хотябы) отключается оптимизация динамически находятся адреса дальше немного анализа, чтобы получить реальные конец и начало функций 100 раз уже делал проблем не было
как тривиально... KISS давайте воспользуемся указателями и их свойством работы с сырой памятью, что позволит нам покопаться в стеке. вся суть приёма состоит в том что мы передадим функции первым параметром указатель, прямо перед которым в стеке будет лежать адрес возврата. в общем, тут не паханое поле возможностей, так что моя пляска с указателями далеко не самый лучший вариант, но для примера, в общем то, я думаю, сгодится. Код (Text): #include "iostream" #include "iomanip" using namespace std; // тихая злоба... мелким что лень было namespace std по дефолту поставить? void drosophila(unsigned long *); int main() { unsigned long tmp, *ptr_tmp = &tmp; cin >> tmp; drosophila(ptr_tmp); return 0; } void drosophila(unsigned long * tmp) { unsigned long ptr_tmp = tmp[-2]; cout << hex << ptr_tmp << endl; char xxx[128]; cout << "Write any text: "; cin >> xxx; // три строки бреда нужны для того что бы мягкая поделка не превратила функцию в inline, любителям const привет! }
и с чего бы это я решил что нужно адрес возврата получить?.. извиняюсь. немножко не туда забрёл. сейчас подумаю как можно исправить. кстати а нельзя ли под конец функции просто считать eip и сделать поправку на размер мусора оставленного компилятором?
Работает ) Код (Text): unsigned int lpEnd_myFunc; int myFunc(void) { __asm { lea eax, end_myFunc mov [lpEnd_myFunc], eax } printf("myFunc: begin = 0x%p, end = 0x%x, size = 0x%x byte \n", &myFunc, lpEnd_myFunc, lpEnd_myFunc - (unsigned int)&myFunc); return 0; end_myFunc:; }
Y_Mur Да, но только один нюанс - по лабелю вычислять можно только внутри этой функции, т.к. он локален внутри неё и снаружи не доступен.
Y_Mur Тоже офигенный вариант )) Будем инжектить функцию, которая кроме того что нам нужно, так, между прочим, еще и какой-то левый код будет выполнять. Так сказать, обфускация ))) А, кстати, куда она будет записывать значение еах?
_Aspire Кстати вполне рабочий вариант: если в функции есть параметр\ы, который в нормальных условиях не может\не должен принимать некоторых значений (например, указатель), то можно в функции проверять его значение и выдавать в качестве рез-та указатель на метку
или как в #53 всегда класть размер в глобальную переменную правда один "холостой" вызов функции всё равно остаётся, да и для инжекта это наверное не совсем хорошо...