Есть ли возможность более или менее легально, без грязных хаков, узнать, откуда вызвали функцию?
Предполагаем, что у нас фон-неймановская архитектура, стек растёт вниз — т.е. обычная винда или линукс на не экзотическом железе.
Я краем уха слышал о способах передачи параметров, как стек организован, и тп, и вот что родилось:
1) При вызове функций компилятор складывает параметры на стек, затем вызывает функцию, при этом на стек помещается адрес возврата, т.е он у нас ниже последнего параметра
2) Все параметры кладутся на стек, расширяясь до int'а, как минимум. Тут не очень понятно, а на x64 как оно происходит, до какого размера расширяется параметр, до 4х байт или до 8им?
3) Если у нас единственный аргумент, то отнимаем 4/8 байт от его адреса, и получаем адрес, где лежит адрес возврата, так?
4) Если у нас несколько параметров, и мы в общем случае не знаем порядок их укладки на стек (stdcall/cdecl), то мы берём адреса крайних параметров и выбираем из них меньший, так?
Тут не понятно, а как быть, если у функции нет параметров? Или функция — какая-нибудь fastcall, которая передаёт первый(ые) параметр(ы) в регистрах? Или, вроде, на x64 никакого fastcall для этого специально указывать не надо, там вроде стандартное соглашение о передаче параметров через регистр. Как быть?
С передачей структур/классов по значению тоже не очень понятно, сколько в итоге они там места на стеке занимать будут.
Здравствуйте, пффф, Вы писали:
П>Есть ли возможность более или менее легально, без грязных хаков, узнать, откуда вызвали функцию?
Насколько грязным не должен быть хак? https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html
П>Предполагаем, что у нас фон-неймановская архитектура, стек растёт вниз — т.е. обычная винда или линукс на не экзотическом железе.
Куда растёт стек — пофиг.
П>Я краем уха слышал о способах передачи параметров, как стек организован, и тп, и вот что родилось:
П>1) При вызове функций компилятор складывает параметры на стек, затем вызывает функцию, при этом на стек помещается адрес возврата, т.е он у нас ниже последнего параметра
Почти. Только в соглашениях посовременнее стараются максимум передать в регистрах и только если этого недостаточно — класть на стек.
П>2) Все параметры кладутся на стек, расширяясь до int'а, как минимум. Тут не очень понятно, а на x64 как оно происходит, до какого размера расширяется параметр, до 4х байт или до 8им?
И снова тут.
П>3) Если у нас единственный аргумент, то отнимаем 4/8 байт от его адреса, и получаем адрес, где лежит адрес возврата, так?
Не совсем, см. 16-байтное выравнивание стека при вызове.
П>4) Если у нас несколько параметров, и мы в общем случае не знаем порядок их укладки на стек (stdcall/cdecl), то мы берём адреса крайних параметров и выбираем из них меньший, так?
Что такое "меньший"?
П>Тут не понятно, а как быть, если у функции нет параметров? Или функция — какая-нибудь fastcall, которая передаёт первый(ые) параметр(ы) в регистрах? Или, вроде, на x64 никакого fastcall для этого специально указывать не надо, там вроде стандартное соглашение о передаче параметров через регистр. Как быть?
Это решается или через регистр BP/EBP/RBP, или через отладочную информацию.
П>С передачей структур/классов по значению тоже не очень понятно, сколько в итоге они там места на стеке занимать будут.
Здравствуйте, netch80, Вы писали:
П>>Есть ли возможность более или менее легально, без грязных хаков, узнать, откуда вызвали функцию?
N>Насколько грязным не должен быть хак? N>https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html
Я для MSVC?
П>>Предполагаем, что у нас фон-неймановская архитектура, стек растёт вниз — т.е. обычная винда или линукс на не экзотическом железе.
N>Куда растёт стек — пофиг.
Почему?
П>>Я краем уха слышал о способах передачи параметров, как стек организован, и тп, и вот что родилось:
П>>1) При вызове функций компилятор складывает параметры на стек, затем вызывает функцию, при этом на стек помещается адрес возврата, т.е он у нас ниже последнего параметра
N>Почти. Только в соглашениях посовременнее стараются максимум передать в регистрах и только если этого недостаточно — класть на стек.
Ну вот это как раз вроде про x64, не?
Интересуют вызовы функций из DLL, не произвольный плюсовый код
П>>4) Если у нас несколько параметров, и мы в общем случае не знаем порядок их укладки на стек (stdcall/cdecl), то мы берём адреса крайних параметров и выбираем из них меньший, так?
N>Что такое "меньший"?
Ниже на стеке, ближе к началу адресного пространства. Чего непонятного-то?
Здравствуйте, пффф, Вы писали:
П>Есть ли возможность более или менее легально, без грязных хаков, узнать, откуда вызвали функцию?
Я о такой возможности не знаю (легально и без хаков). Но сама потребость в этом подозрительна. Я в такой ситуации задался бы вопросом: а все ли здесь благополучно с дизайном? По моим представлениям любая зависимость (знание) вызываемой сущности от вызывающего кода не совсем естественна. Разве что, для отладочных целей?
Здравствуйте, rg45, Вы писали:
П>>Есть ли возможность более или менее легально, без грязных хаков, узнать, откуда вызвали функцию?
R>Я о такой возможности не знаю (легально и без хаков).
Ну хорошо, а как нелегально и с хаками?
R>Но сама потребость в этом подозрительна. Я в такой ситуации задался бы вопросом: а все ли здесь благополучно с дизайном?
Причем тут дизайн? Я перехват химичу, и хочу знать, откуда он вызван
Здравствуйте, пффф, Вы писали:
R>>Я о такой возможности не знаю (легально и без хаков).
П>Ну хорошо, а как нелегально и с хаками?
Ну для этого нужно закладываться на какой-то конкретный прокол вызова (cdecl, stdcall, fastcall, etc.), вдобавок нужно быть уверенным, что функция не заинлайнилась, потом, через анализ содержимого регистров (ESP, EBP, etc), с учетом размера стека, занимаемого параметрами и локальными переменными, можно будет вытащить адрес возврата. Я с этим баловался давненько, сейчас сходу всех деталей и не припомню уже. Самый простой путь — написать функцию-прототип и посмотреть асемблерный код, как она устроена.
Здравствуйте, rg45, Вы писали:
R>>>Я о такой возможности не знаю (легально и без хаков).
П>>Ну хорошо, а как нелегально и с хаками?
R>Ну для этого нужно закладываться на какой-то конкретный прокол вызова (cdecl, stdcall, fastcall, etc.), вдобавок нужно быть уверенным, что функция не заинлайнилась,
Функции в DLL-ках, либо stdcall, изредка может быть cdecl, другого, по идее, не должно быть. С инлайном тоже всё понятно — его не будет
R>потом, через анализ содержимого регистров (ESP, EBP, etc) можно будет вытащить адрес возврата. Я с этим баловался давненько, сейчас сходу всех деталей и не припомню уже.
Ну вот как-то безе регистров уж совсем, хотелось бы. И x86/x64 чтобы, желательно поуниверсальнее
Здравствуйте, пффф, Вы писали:
П>Функции в DLL-ках, либо stdcall, изредка может быть cdecl, другого, по идее, не должно быть. С инлайном тоже всё понятно — его не будет
П>Ну вот как-то безе регистров уж совсем, хотелось бы. И x86/x64 чтобы, желательно поуниверсальнее
Можешь добавить в длл-ку функцию-прототип, пройти по ней с отладчиком и заглянуть в ассемлер, чтобы понять, как она устроена. Потом можно будет ассемблерными вставками вытащить адрес возврата.
Здравствуйте, пффф, Вы писали:
R>>Но сама потребость в этом подозрительна. Я в такой ситуации задался бы вопросом: а все ли здесь благополучно с дизайном?
П>Причем тут дизайн? Я перехват химичу, и хочу знать, откуда он вызван
Перезахват чего?
Если эта функция твоя, и вызывать её тоже будешь ты, то сделай ей первый параметр — имя вызывающей функции.
И чтоб два раза не вставать — ну получишь ты адрес вызова. Это 64-разрядное число. Что ты с ним дальше делать будешь?
Здравствуйте, YuriV, Вы писали:
П>>Причем тут дизайн? Я перехват химичу, и хочу знать, откуда он вызван
YV>Если перехват ВинАПИ, то есть Microsoft Detours.
Спасибо, но вопрос был не о том. Перехват я сделал, да, при помощи Detours
Здравствуйте, пффф, Вы писали:
A>>И чтоб два раза не вставать — ну получишь ты адрес вызова. Это 64-разрядное число. Что ты с ним дальше делать будешь? П>Узнаю, в каком модуле этот адрес, и начну дизасмить модуль с того места
Что если я для перехода куда-то подменил адрес возврата в стеке и сделал ret?
Ты не узнаешь откуда "было вызвано".