Безопасное подключение функций C к сборке - PullRequest
3 голосов
/ 18 августа 2011

Я пытаюсь вставить некоторые C-хуки в некоторый код, который кто-то написал в asm. Я не очень разбираюсь в x86 asm и хочу убедиться, что делаю это безопасно.

Пока у меня так много:

EXTERN _C_func

CALL _C_func

И это похоже на работу, но я уверен, что это опасно как есть. По крайней мере, это может разрушить EAX, а возможно и больше.

Так что я думаю, все, что мне нужно знать, это: какие регистры уничтожает C и как мне обеспечить правильное сохранение регистров? Что еще я могу сделать, чтобы убедиться, что я могу безопасно вставлять хуки в произвольные места?

Я использую Visual Studio для функций C, если это помогает. Спасибо.

UPDATE:

Я посмотрел на «параноидальный» метод, предложенный несколькими людьми, и он выглядит так:

pushfd
pushad
call _C_func
popad
popfd

AD = (A) ll общие регистры

FD = (F) лаги

(Я не уверен, что означает «d», но это означает 32-битные регистры вместо 16-битных.)

Но наиболее эффективный метод показан в ответе Крагена ниже. (Предполагая, что вам не нужно сохранять флаги. Если вам это нужно, добавьте инструкции FD.)

Кроме того, будьте осторожны с распределением большого количества переменных стека в вашей функции C, поскольку это может привести к переполнению стекового пространства подпрограммы asm.

Я думаю, что по этому поводу, спасибо за помощь всем!

Ответы [ 3 ]

3 голосов
/ 18 августа 2011

Если соглашение о вызовах - cdecl, а подпись C_funct - просто:

void C_func(void);

Тогда это совершенно «безопасно», однако регистры EAX, ECX и EDX «доступны для использования внутри функции» и поэтому могут быть перезаписаны.

Чтобы защититься от этого, вы можете сохранить регистры, которые вам нужны, и восстановить их впоследствии:

push eax
push ecx
push edx

call _C_func

pop edx
pop ecx
pop eax

По соглашению регистры EBX, ESI, EDI и EBP не должны изменяться вызываемым пользователем.

Я считаю, что вызываемый может изменять флаги - опять же, если вы хотите сохранить значение флагов, вам следует сохранить их.

См. Страницу Википедии на x86 соглашениях о вызовах .

1 голос
/ 18 августа 2011

Вы должны знать о соглашениях о вызовах. См. статью для обсуждения соглашений о вызовах.

Если вы параноидально относитесь к регистрам, вы всегда можете поместить все регистры в стек перед вызовом функции (функции не должны возвращаться без очистки, за исключением тех регистров, которые несут информацию о возвращении).

0 голосов
/ 18 августа 2011

Это будет зависеть от того, используете ли вы платформу x86 или x86_64 ... соглашения о вызовах для каждого из них, а также регистры сохранения вызывающего абонента и сохранения вызываемого абонента (и даже доступный набор регистров) немного различаются.

При этом регистры сохранения вызывающего абонента должны быть помещены в стек перед вызовом вашей C-функции.Callee-save регистры, о которых вам не нужно беспокоиться, если вы вызываете C-функцию, но вам нужно будет обратить внимание, если вы вызываете процедуру сборки из C-функции.

Для x8632-битная платформа, регистры сохранения вызывающей стороны:

EAX
EDX
ECX

Также имейте в виду, что регистры-указатели стека ESP и EBP также будут изменены в соответствии с соглашением о вызовах cdecl, ноC-функция восстановит эти значения после вызова, предполагая, что вы не сделали что-то, что C-функция не ожидает, что вы будете делать с этими регистрами.

Регистры сохранения вызываемого абонента:

EBX
ESI
EDI

Наконец, возвращаемое значение из вашей C-функции находится в регистре EAX, и аргументы C-функции помещаются в стек перед вызовом функции.Порядок, в котором это будет сделано, будет зависеть от соглашения о вызовах, но для cdecl это будет в порядке справа налево, то есть крайний левый аргумент функции C помещается последним в стеке перед вызовом функции..

Если вы решите просто сохранить все регистры общего назначения в стеке перед вызовом вашей C-функции, а затем вытащить их обратно из стека после C-функции, вы можете сделать это на x86, используяинструкции PUSHAD и POPAD.Эти инструкции не могут быть использованы на x86_64.Также имейте в виду, что если у вас есть возвращаемое значение в EAX, вам нужно будет что-то с этим сделать (например, сохранить его в памяти), прежде чем вы вызовете POPAD.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...