Питание со стеком в блоке и C++

Я хочу сделать следующее:

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

То, что я пытаюсь сделать, для помещения указателя вершины стека, как это было, прежде чем та функция вызвана, и затем назовите мой (как возвращение и сделайте снова то же самое, но с другой функцией). Это не работает прямо, потому что стек становится испорченным. Я полагаю, что, когда я делаю вызов, он заменяет обратный адрес. Так, я сделал шаг для сохранения обратного адреса, сохраняющего его в глобально переменная, и он работает, но это не в порядке, потому что я хочу, чтобы он сопротивлялся к recursitivy, и Вы знаете то, что я имею в виду. Так или иначе я - новичок в блоке так вот почему, я здесь.

Не говорите мне об уже сделанном программном обеспечении делать это, потому что я хочу сделать вещи моим путем.

Конечно, этот код должен быть компилятором и независимой оптимизацией.

Мой код (Если это больше, чем, что приемлемо, говорят мне, как отправить его):

// A function that is not mine but to which I have access and want to patch so that it calls a function of mine with its original arguments
void real(int a,int b,int c,int d)
{

}

// A function that I want to be called, receiving the original arguments
void receiver(int a,int b,int c,int d)
{
 printf("Arguments %d %d %d %d\n",a,b,c,d);
}

long helper;

// A patch to apply in the "real" function and on which I will call "receiver" with the same arguments that "real" received.
__declspec( naked ) void patch()
{
 _asm
 {
  // This first two instructions save the return address in a global variable
  // If I don't save and restore, the program won't work correctly.
  // I want to do this without having to use a global variable
  mov eax, [ebp+4]
  mov helper,eax

  push ebp
  mov ebp, esp

  // Make that the stack becomes as it were before the real function was called
  add esp, 8

  // Calls our receiver 
  call receiver

  mov esp, ebp
  pop ebp

  // Restores the return address previously saved
  mov eax, helper
  mov [ebp+4],eax

  ret
 }
}

int _tmain(int argc, _TCHAR* argv[])
{
 FlushInstructionCache(GetCurrentProcess(),&real,5);

 DWORD oldProtection;
 VirtualProtect(&real,5,PAGE_EXECUTE_READWRITE,&oldProtection);

 // Patching the real function to go to my patch
 ((unsigned char*)real)[0] = 0xE9;
 *((long*)((long)(real) + sizeof(unsigned char))) = (char*)patch - (char*)real - 5;

 // calling real function (I'm just calling it with inline assembly because otherwise it seems to works as if it were un patched
 // that is strange but irrelevant for this
 _asm
 {
  push 666
  push 1337
  push 69
  push 100
  call real
  add esp, 16
 }

 return 0;
}

Печать (и имеет к):

Аргументы 100 69 1337 666

Править:

Код я тестирую следующее предложение Vlad (Все еще не работающий)

// A patch to apply in the real function and on which I will call receiver with the same arguments that "real" received.
__declspec( naked ) void patch()
{
    _asm
    {
        jmp start

        mem:

        nop
        nop
        nop
        nop

        start :

        // This first two instructions save the return address in a global variable
        // If I don't save and restore the program won't work correctly.
        // I want to do this without having to use a global variable
        mov eax, [ebp+4]
        mov mem, eax

        push ebp
        mov ebp, esp

        // Make that the stack becomes as it were before the real function was called
        add esp, 8

        // Calls our receiver 
        call receiver

        mov esp, ebp
        pop ebp

        // Restores the return address previously saved
        mov eax, mem
        mov [ebp+4],eax

        ret
    }
} 
6
задан user246100 12 April 2010 в 21:19
поделиться

4 ответа

Вы должны один раз добавить esp, 8 и один раз добавить esp, 16 . Один из них, должно быть, неправ.

Редактировать:
О, я вижу, после add esp, 8 вы должны были удалить из стека ebp нажали 2 инструкции раньше и вернулись адрес.

В [ebp + 4] должен быть обратный адрес вызова _tmain .

Edit2:
вы можете выделить «внутреннюю» переменную примерно так:

  call next
  dd 0
next:
  pop eax
  mov [eax], yourinfo

Но все еще не ясно, зачем нам вообще нужно сохранять это значение.

Edit3: (удалено, ошибочно)

Edit4:
Другая идея:

__declspec( naked ) void patch()
{
 _asm
 {
  call next
  // here we temporarily save the arguments
  dd   0
  dd   0
  dd   0
  dd   0
next:
  pop eax
  // eax points to the first dd

  // now store the args
  pop edx
  mov [eax], edx
  pop edx
  mov [eax+4], edx
  pop edx
  mov [eax+8], edx
  pop edx
  mov [eax+12], edx

  // now we can push the value
  mov edx, [ebp+4]
  push edx

  // now, push the args again
  mov edx, [eax+12]
  push edx
  mov edx, [eax+8]
  push edx
  mov edx, [eax+4]
  push edx
  mov edx, [eax]
  push edx

  // now continue with the old code
  // --------------------------------
  // restore the arguments    
  push ebp
  mov ebp, esp

  // Make that the stack becomes as it were before the real function was called
  add esp, 8

  // Calls our receiver 
  call receiver

  mov esp, ebp
  pop ebp

  // ----------------------------
  pop edx
  mov [ebp+4], edx

  ret
 }
}

Это решение переживает рекурсию, но не одновременное выполнение из двух разных потоков.

0
ответ дан 17 December 2019 в 20:30
поделиться

Во время нормального выполнения операнды функции вставляются в обратном порядке в стек. При выполнении кода операции call процессор сначала помещает регистр EIP (или CS / IP) в стек. Это обратный адрес. Когда выполнение достигает функции, которую вы хотите заменить, акции выглядят следующим образом:

Return address 1
Operand 1
Operand 2
Operand 2

На этом этапе вы собираетесь вызвать свою собственную функцию, которая будет иметь такой стек:

Return address 2
Return address 1
Operand 1
Operand 2
Operand 3

Ваша функция должна будет знать, что в стеке есть дополнительный DWORD, поскольку он делает то, что вы хотите.С этим легко справиться, если вы также написали свою заменяющую функциональную сборку, просто добавляйте 4 всякий раз, когда ссылаетесь на ESP. Когда вы вызываете RET в своей функции, появляется первый адрес возврата, и выполнение возвращается к функции, которую вы заменяете. Стек снова будет иметь следующий вид:

Return address 1
Operand 1
Operand 2
Operand 3

Вызов RET в этой функции снова вытолкнет адрес возврата из стека и вернет управление вызывающей функции. Это оставляет ваши операнды в стеке, что приводит к повреждению. Я предлагаю вызвать RET с таким количеством операндов функции, как это:

RET 3

Это вытолкнет из стека 3 (в моем примере) или сколько бы там ни было операндов. Вот пара ссылок, которые могут оказаться полезными:

http://pdos.csail.mit.edu/6.828/2009/readings/i386/CALL.htm http://pdos.csail.mit.edu /6.828/2009/readings/i386/RET.htm

0
ответ дан 17 December 2019 в 20:30
поделиться

Я никогда не использовал C ++ для подобных вещей низкого уровня, поэтому я не буду вдаваться в подробности вашего примера, но в целом, если вы хотите перехватывать вызов и поддерживать рекурсию в вашей логике, у вас есть два варианта: либо скопировать весь кадр стека (параметры) и вызвать «подключенный» оригинал с новой копией, либо, если это невозможно, сохранить свой собственный небольшой стек для хранения исходное возвращаемое значение (например, в виде связанного списка с корнем) в структуре данных на основе TLS.

0
ответ дан 17 December 2019 в 20:30
поделиться

Следующие фрагменты кода были проверены с помощью mingw-g ++, но должны работать в VC ++ с небольшими изменениями. Полные исходные коды доступны на Launchpad: 1

Единственный способ безопасно сохранить данные, относящиеся к вызовам, - это сохранить их в стеке. Один из способов - повернуть часть стопки.

отрывок из patch.s (patchfun-rollstack):

sub esp, 4          # allocate scratch space

mov eax, DWORD PTR [esp+4]  # first we move down
mov DWORD PTR [esp], eax    # our return pointer

mov eax, DWORD PTR [esp+8]  # then our parameters
mov DWORD PTR [esp+4], eax
mov eax, DWORD PTR [esp+12]
mov DWORD PTR [esp+8], eax
mov eax, DWORD PTR [esp+16]
mov DWORD PTR [esp+12], eax
mov eax, DWORD PTR [esp+20]
mov DWORD PTR [esp+16], eax

mov eax, DWORD PTR [esp]    # save return pointer
mov DWORD PTR [esp+20], eax # behind arguments

add esp, 4          # free scratch space
call    __Z8receiveriiii

mov eax, DWORD PTR [esp+16] # restore return pointer
mov DWORD PTR [esp], eax

ret

Мы опустили ebp здесь. Если мы добавим это, нам нужно будет использовать 8 байтов рабочего пространства, а также сохранить и восстановить ebp , а также eip . Обратите внимание, что когда мы восстанавливаем указатель возврата, мы перезаписываем параметр a . Чтобы этого избежать, нам придется снова повернуть стопку.


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

patch.s (patchfun-ignorepointers):

push    ebp
mov ebp, esp
call    receiver
leave
ret

Receiver.cc:

void receiver(const void *epb, const void *eip, int a,int b,int c,int d)
{
 printf("Arguments %d %d %d %d\n",a,b,c,d);
}

Здесь я включил epb, если вы удалите его из asm, все, что останется, это вызов и ret , и получателю нужно будет только принять и проигнорировать eip .


Конечно, все это в основном для развлечения и любопытства. На самом деле нет большого преимущества перед простым решением:

void patch(int a,int b,int c,int d)
{
 receiver(a,b,c,d);
}

Сгенерированная сборка будет короче, чем наш stack-roll, но для этого потребуется на 16 байт больше стека, потому что значения копируются в новую область под патчем () кадр стека.

(На самом деле asm, сгенерированный gcc, выделяет в стеке 28 байтов, хотя он использует только 16. Я не уверен, почему. Возможно, дополнительные 12 байтов являются частью какой-то схемы защиты от разбивания стека.)

2
ответ дан 17 December 2019 в 20:30
поделиться
Другие вопросы по тегам:

Похожие вопросы: