Почему сборка не кажется мне согласованной?

#c #visual-studio #assembly

#c #visual-studio #сборка

Вопрос:

Выгружено из visual Studio:

     CheckPointer(pReceivePin,E_POINTER);
017D616D  cmp         dword ptr [ebp 0Ch],0 
017D6171  jne         CBasePin::Connect 4Dh (17D617Dh) 
017D6173  mov         eax,80004003h 
017D6178  jmp         CBasePin::Connect 1A7h (17D62D7h) 
  

Но фактическое определение:

 #define CheckPointer(p,ret) {if((p)==NULL) return (ret);}
  

Хотя моя сборка не так хороша, я не вижу никакой связи между исходным кодом и asm.

Ответ №1:

Вы упустили достаточно, чтобы трудно было быть уверенным, но та часть, с которой можно разобраться, выглядит разумной. NULL == 0, поэтому:

 017D616D  cmp         dword ptr [ebp 0Ch],0               ; if [ebp 0ch] == 0
017D6171  jne         CBasePin::Connect 4Dh (17D617Dh)    ;     goto 172617dh
017D6173  mov         eax,80004003h                       ; else load 'ret'
017D6178  jmp         CBasePin::Connect 1A7h (17D62D7h)   ;     and return it.
  

Очевидная проблема в том, что вы не показали нам, что находится в 17D617Dh или 17D62D7h, поэтому мы не можем догадаться, что на самом деле делается со значениями.

Комментарии:

1. Настоящий Ollydbg, безусловно, знает! jmp Начинается выполнение кода по указанному адресу. A call делает то же самое, но сначала помещает текущий адрес в стек, чтобы a ret мог продолжить возврат туда.

2. Не текущий адрес, а адрес следующей инструкции, если быть точным.

3. @ollydbg: Я действительно имел в виду что-то вроде «адрес, находящийся в данный момент в EIP», но да, это будет адрес, следующий за текущей инструкцией (хотя в действительно ранних процессорах x86 этого не было — что вызвало проблему с обработкой исключений в нескольких странных угловых случаях).

Ответ №2:

Вероятно

  CheckPointer(pReceivePin,E_POINTER);
 017D616D  cmp         dword ptr [ebp 0Ch],0 
 017D6171  jne         CBasePin::Connect 4Dh (17D617Dh) 
 017D6173  mov         eax,80004003h 
 017D6178  jmp         CBasePin::Connect 1A7h (17D62D7h) 
  

pReceivePin оказывается, что она находится по адресу, хранящемуся в стеке — обычно доступ к ней осуществляется косвенным путем с использованием хранилища значений в ebp .

Это значение сравнивается с null, и если оно равно null ( jne не запускается), фактическое значение E_POINTER перемещается в eax ( eax используется для хранения возвращаемого значения функции), а управление передается функции epilogue, где выполняется очистка, а затем управление возвращается вызывающему ( ret инструкция). Если значение pReceivePin не равно null ( jne срабатывает), управление передается в какое-то другое место, где хранится код, который оказался после CheckPointer , и этот код затем выполняется.

Комментарии:

1. Почему вы упомянули ret , что в моем дампе vs нет такой инструкции, верно?

2. @ollydbg: Это где-то в другом месте. Попробуйте вызвать этот код с pReceivePin значением null — код выполнится jne и попадет в файл, где ret находится.

Ответ №3:

Вам нужно предоставить больше контекста, но, вполне возможно, последняя строка переходит к завершающим частям функции CBasePin::Connect, за которой вскоре следует RET. Это довольно согласуется с логикой вашего макроса. Вторая строка переходит сразу после последней строки, если указатель не равен нулю (т.Е. null).

Ответ №4:

Сборка выглядит совершенно нормально.

cmp dword ptr [ebp 0Ch],0 выполняется ли сравнение pReceivePin с NULL . pReceivePin это локальная переменная внутри вашей функции, поэтому ее адрес является смещением от начала фрейма стека функции. ebp содержит адрес начала фрейма стека. 0Ch это смещение pReceivePin внутри фрейма стека. Все локальные переменные в вашей функции будут адресоваться как [ebp something] , за исключением параметров. Параметры обычно обозначаются как [ebp - something] .

mov eax,80004003h это не что иное, как E_POINTER значение, помещенное в область «возвращаемое значение» функции (для этой цели используется регистр eax ).

В финале jmp управление передается коду эпилога текущей функции ( CBasePin::Connect ), который заканчивается на ret command (и «возвращает» текущее eax значение, т.е. E_POINTER )

Средний jne элемент управления sens в коде сразу после вашего макроса, if pReceivePin не равен E_POINTER .

Комментарии:

1. Почему pReceivePin расширяется до [ebp 0Ch] ?

2. @ollydbg: Она хранится в stack и к ней можно получить доступ через косвенное обращение.

3. В чем разница между jmp xxx и call xxx then?