Узнать адрес, откуда вызвали
От: пффф  
Дата: 19.06.23 03:33
Оценка:
Привет!

Есть ли возможность более или менее легально, без грязных хаков, узнать, откуда вызвали функцию?

Предполагаем, что у нас фон-неймановская архитектура, стек растёт вниз — т.е. обычная винда или линукс на не экзотическом железе.

Я краем уха слышал о способах передачи параметров, как стек организован, и тп, и вот что родилось:

1) При вызове функций компилятор складывает параметры на стек, затем вызывает функцию, при этом на стек помещается адрес возврата, т.е он у нас ниже последнего параметра
2) Все параметры кладутся на стек, расширяясь до int'а, как минимум. Тут не очень понятно, а на x64 как оно происходит, до какого размера расширяется параметр, до 4х байт или до 8им?
3) Если у нас единственный аргумент, то отнимаем 4/8 байт от его адреса, и получаем адрес, где лежит адрес возврата, так?
4) Если у нас несколько параметров, и мы в общем случае не знаем порядок их укладки на стек (stdcall/cdecl), то мы берём адреса крайних параметров и выбираем из них меньший, так?

Тут не понятно, а как быть, если у функции нет параметров? Или функция — какая-нибудь fastcall, которая передаёт первый(ые) параметр(ы) в регистрах? Или, вроде, на x64 никакого fastcall для этого специально указывать не надо, там вроде стандартное соглашение о передаче параметров через регистр. Как быть?

С передачей структур/классов по значению тоже не очень понятно, сколько в итоге они там места на стеке занимать будут.

Что посоветуете?
Отредактировано 19.06.2023 3:35 пффф . Предыдущая версия .
Re: Узнать адрес, откуда вызвали
От: netch80 Украина http://netch80.dreamwidth.org/
Дата: 19.06.23 05:09
Оценка: 4 (1) +2
Здравствуйте, пффф, Вы писали:

П>Есть ли возможность более или менее легально, без грязных хаков, узнать, откуда вызвали функцию?


Насколько грязным не должен быть хак?
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, или через отладочную информацию.

П>С передачей структур/классов по значению тоже не очень понятно, сколько в итоге они там места на стеке занимать будут.


И снова там же всё описано.

П>Что посоветуете?


Не изобретать велосипед, насколько возможно.
The God is real, unless declared integer.
Re[2]: Узнать адрес, откуда вызвали
От: пффф  
Дата: 19.06.23 05:19
Оценка:
Здравствуйте, netch80, Вы писали:

П>>Есть ли возможность более или менее легально, без грязных хаков, узнать, откуда вызвали функцию?


N>Насколько грязным не должен быть хак?

N>https://gcc.gnu.org/onlinedocs/gcc/Return-Address.html

Я для MSVC?


П>>Предполагаем, что у нас фон-неймановская архитектура, стек растёт вниз — т.е. обычная винда или линукс на не экзотическом железе.


N>Куда растёт стек — пофиг.


Почему?


П>>Я краем уха слышал о способах передачи параметров, как стек организован, и тп, и вот что родилось:


П>>1) При вызове функций компилятор складывает параметры на стек, затем вызывает функцию, при этом на стек помещается адрес возврата, т.е он у нас ниже последнего параметра


N>Почти. Только в соглашениях посовременнее стараются максимум передать в регистрах и только если этого недостаточно — класть на стек.


Ну вот это как раз вроде про x64, не?

Интересуют вызовы функций из DLL, не произвольный плюсовый код


П>>4) Если у нас несколько параметров, и мы в общем случае не знаем порядок их укладки на стек (stdcall/cdecl), то мы берём адреса крайних параметров и выбираем из них меньший, так?


N>Что такое "меньший"?


Ниже на стеке, ближе к началу адресного пространства. Чего непонятного-то?


П>>Что посоветуете?


N>Не изобретать велосипед, насколько возможно.


Изобретать ничего не хочется. Есть что готовенькое для MSVC?
Re: Узнать адрес, откуда вызвали
От: rg45 СССР  
Дата: 19.06.23 07:02
Оценка: +1
Здравствуйте, пффф, Вы писали:

П>Есть ли возможность более или менее легально, без грязных хаков, узнать, откуда вызвали функцию?


Я о такой возможности не знаю (легально и без хаков). Но сама потребость в этом подозрительна. Я в такой ситуации задался бы вопросом: а все ли здесь благополучно с дизайном? По моим представлениям любая зависимость (знание) вызываемой сущности от вызывающего кода не совсем естественна. Разве что, для отладочных целей?
--
Отредактировано 19.06.2023 7:07 rg45 . Предыдущая версия . Еще …
Отредактировано 19.06.2023 7:05 rg45 . Предыдущая версия .
Re[2]: Узнать адрес, откуда вызвали
От: пффф  
Дата: 19.06.23 07:06
Оценка:
Здравствуйте, rg45, Вы писали:

П>>Есть ли возможность более или менее легально, без грязных хаков, узнать, откуда вызвали функцию?


R>Я о такой возможности не знаю (легально и без хаков).


Ну хорошо, а как нелегально и с хаками?


R>Но сама потребость в этом подозрительна. Я в такой ситуации задался бы вопросом: а все ли здесь благополучно с дизайном?


Причем тут дизайн? Я перехват химичу, и хочу знать, откуда он вызван
Re[3]: Узнать адрес, откуда вызвали
От: rg45 СССР  
Дата: 19.06.23 07:13
Оценка:
Здравствуйте, пффф, Вы писали:

R>>Я о такой возможности не знаю (легально и без хаков).


П>Ну хорошо, а как нелегально и с хаками?


Ну для этого нужно закладываться на какой-то конкретный прокол вызова (cdecl, stdcall, fastcall, etc.), вдобавок нужно быть уверенным, что функция не заинлайнилась, потом, через анализ содержимого регистров (ESP, EBP, etc), с учетом размера стека, занимаемого параметрами и локальными переменными, можно будет вытащить адрес возврата. Я с этим баловался давненько, сейчас сходу всех деталей и не припомню уже. Самый простой путь — написать функцию-прототип и посмотреть асемблерный код, как она устроена.
--
Отредактировано 19.06.2023 7:19 rg45 . Предыдущая версия .
Re[4]: Узнать адрес, откуда вызвали
От: пффф  
Дата: 19.06.23 07:17
Оценка:
Здравствуйте, rg45, Вы писали:

R>>>Я о такой возможности не знаю (легально и без хаков).


П>>Ну хорошо, а как нелегально и с хаками?


R>Ну для этого нужно закладываться на какой-то конкретный прокол вызова (cdecl, stdcall, fastcall, etc.), вдобавок нужно быть уверенным, что функция не заинлайнилась,


Функции в DLL-ках, либо stdcall, изредка может быть cdecl, другого, по идее, не должно быть. С инлайном тоже всё понятно — его не будет


R>потом, через анализ содержимого регистров (ESP, EBP, etc) можно будет вытащить адрес возврата. Я с этим баловался давненько, сейчас сходу всех деталей и не припомню уже.


Ну вот как-то безе регистров уж совсем, хотелось бы. И x86/x64 чтобы, желательно поуниверсальнее
Re[3]: Узнать адрес, откуда вызвали
От: so5team https://stiffstream.com
Дата: 19.06.23 07:23
Оценка: 4 (1) +1
Здравствуйте, пффф, Вы писали:

R>>Я о такой возможности не знаю (легально и без хаков).


П>Ну хорошо, а как нелегально и с хаками?


Может заглянуть в потроха Boost.Stacktrace, вдруг поможет?
Re[5]: Узнать адрес, откуда вызвали
От: rg45 СССР  
Дата: 19.06.23 07:23
Оценка:
Здравствуйте, пффф, Вы писали:

П>Функции в DLL-ках, либо stdcall, изредка может быть cdecl, другого, по идее, не должно быть. С инлайном тоже всё понятно — его не будет


П>Ну вот как-то безе регистров уж совсем, хотелось бы. И x86/x64 чтобы, желательно поуниверсальнее


Можешь добавить в длл-ку функцию-прототип, пройти по ней с отладчиком и заглянуть в ассемлер, чтобы понять, как она устроена. Потом можно будет ассемблерными вставками вытащить адрес возврата.
--
Re: Узнать адрес, откуда вызвали
От: пффф  
Дата: 19.06.23 12:00
Оценка: 15 (1)
Здравствуйте, пффф, Вы писали:

П>Есть ли возможность более или менее легально, без грязных хаков, узнать, откуда вызвали функцию?


В итоге — нашел сам. Для MSVC есть intrinsic _ReturnAddress
Re[3]: Узнать адрес, откуда вызвали
От: alpha21264 СССР  
Дата: 19.06.23 13:20
Оценка:
Здравствуйте, пффф, Вы писали:

R>>Но сама потребость в этом подозрительна. Я в такой ситуации задался бы вопросом: а все ли здесь благополучно с дизайном?


П>Причем тут дизайн? Я перехват химичу, и хочу знать, откуда он вызван


Перезахват чего?
Если эта функция твоя, и вызывать её тоже будешь ты, то сделай ей первый параметр — имя вызывающей функции.
И чтоб два раза не вставать — ну получишь ты адрес вызова. Это 64-разрядное число. Что ты с ним дальше делать будешь?

Течёт вода Кубань-реки куда велят большевики.
Re[4]: Узнать адрес, откуда вызвали
От: пффф  
Дата: 19.06.23 13:38
Оценка:
Здравствуйте, alpha21264, Вы писали:

A>Перезахват чего?


Например, CreateFile


A>Если эта функция твоя, и вызывать её тоже будешь ты, то сделай ей первый параметр — имя вызывающей функции.


Не моя, и вызываю не я, я только хук вешаю


A>И чтоб два раза не вставать — ну получишь ты адрес вызова. Это 64-разрядное число. Что ты с ним дальше делать будешь?


Узнаю, в каком модуле этот адрес, и начну дизасмить модуль с того места
Re[3]: Узнать адрес, откуда вызвали
От: YuriV  
Дата: 19.06.23 21:07
Оценка: 15 (1)
Здравствуйте, пффф, Вы писали:

П>Здравствуйте, rg45, Вы писали:


П>Причем тут дизайн? Я перехват химичу, и хочу знать, откуда он вызван


Если перехват ВинАПИ, то есть Microsoft Detours.
Re: Узнать адрес, откуда вызвали
От: reversecode google
Дата: 19.06.23 21:16
Оценка:
https://stackoverflow.com/a/16777608
https://stackoverflow.com/a/61545433
Re[4]: Узнать адрес, откуда вызвали
От: пффф  
Дата: 20.06.23 00:31
Оценка:
Здравствуйте, YuriV, Вы писали:

П>>Причем тут дизайн? Я перехват химичу, и хочу знать, откуда он вызван


YV>Если перехват ВинАПИ, то есть Microsoft Detours.


Спасибо, но вопрос был не о том. Перехват я сделал, да, при помощи Detours
Re[2]: Узнать адрес, откуда вызвали
От: пффф  
Дата: 20.06.23 00:32
Оценка:
Здравствуйте, reversecode, Вы писали:

R>https://stackoverflow.com/a/16777608

R>https://stackoverflow.com/a/61545433

Это точно ответ на мой вопрос? На этот, а не на предыдущий?
Re[3]: Узнать адрес, откуда вызвали
От: reversecode google
Дата: 20.06.23 08:13
Оценка:
это ответ на ваши прыжки вокруг да около которые вы делаете уже больше недели
Re[5]: Узнать адрес, откуда вызвали
От: 3V Россия  
Дата: 21.06.23 06:32
Оценка:
Здравствуйте, пффф, Вы писали:

A>>И чтоб два раза не вставать — ну получишь ты адрес вызова. Это 64-разрядное число. Что ты с ним дальше делать будешь?

П>Узнаю, в каком модуле этот адрес, и начну дизасмить модуль с того места

Что если я для перехода куда-то подменил адрес возврата в стеке и сделал ret?
Ты не узнаешь откуда "было вызвано".
Re[6]: Узнать адрес, откуда вызвали
От: пффф  
Дата: 21.06.23 06:34
Оценка:
Здравствуйте, 3V, Вы писали:

3V>Что если я для перехода куда-то подменил адрес возврата в стеке и сделал ret?

3V>Ты не узнаешь откуда "было вызвано".

Не узнаю. А скажи, сколько обычных приложений так делают?
Re: Узнать адрес, откуда вызвали
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 21.06.23 10:38
Оценка:
Здравствуйте, пффф, Вы писали:

П>Что посоветуете?

Посмотреть в строну стека исключений
https://habr.com/ru/articles/208006/

В том же .Net можно раскрутить
https://stackoverflow.com/questions/1678816/dumping-the-call-stack-programmatically
и солнце б утром не вставало, когда бы не было меня
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.