2014-10-31 4 views
4

Я играю с манипуляцией функциями вызова двоичного кода. У меня есть следующий код:Редактирование команды бинарного вызова ELF

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

void myfunc2(char *str2, char *str1); 

    enter code here 

void myfunc(char *str2, char *str1) 
{ 
    memcpy(str2 + strlen(str2), str1, strlen(str1)); 
} 

int main(int argc, char **argv) 
{ 
    char str1[4] = "tim"; 
    char str2[10] = "hello "; 

    myfunc((char *)&str2, (char *)&str1); 

    printf("%s\n", str2); 

     myfunc2((char *)&str2, (char *)&str1); 

    printf("%s\n", str2); 

    return 0; 
} 

void myfunc2(char *str2, char *str1) 
{ 
    memcpy(str2, str1, strlen(str1)); 
} 

Я скомпилированный двоичный и используя readelf или objdump я вижу, что мои две функции находятся по адресу:

46: 0000000000 40072c 52 FUNC GLOBAL DEFAULT 13 myfunc2 * *

54: 0000000000 40064d 77 FUNC GLOBAL DEFAULT 13 MyFunc **

Использование команды objdump -D тест (мой бинарники имя), я могу видеть что main имеет две функции callq. Я попытался отредактировать первый, чтобы указать на myfunc2, используя вышеуказанный адрес 72c, но это не работает; приводит к сбою двоичного файла.

000000000040069a <main>: 
    40069a: 55      push %rbp 
    40069b: 48 89 e5    mov %rsp,%rbp 
    40069e: 48 83 ec 40    sub $0x40,%rsp 
    4006a2: 89 7d cc    mov %edi,-0x34(%rbp) 
    4006a5: 48 89 75 c0    mov %rsi,-0x40(%rbp) 
    4006a9: 64 48 8b 04 25 28 00 mov %fs:0x28,%rax 
    4006b0: 00 00 
    4006b2: 48 89 45 f8    mov %rax,-0x8(%rbp) 
    4006b6: 31 c0     xor %eax,%eax 
    4006b8: c7 45 d0 74 69 6d 00 movl $0x6d6974,-0x30(%rbp) 
    4006bf: 48 b8 68 65 6c 6c 6f movabs $0x206f6c6c6568,%rax 
    4006c6: 20 00 00 
    4006c9: 48 89 45 e0    mov %rax,-0x20(%rbp) 
    4006cd: 66 c7 45 e8 00 00  movw $0x0,-0x18(%rbp) 
    4006d3: 48 8d 55 d0    lea -0x30(%rbp),%rdx 
    4006d7: 48 8d 45 e0    lea -0x20(%rbp),%rax 
    4006db: 48 89 d6    mov %rdx,%rsi 
    4006de: 48 89 c7    mov %rax,%rdi 
    4006e1: e8 67 ff ff ff   callq 40064d <myfunc> 
    4006e6: 48 8d 45 e0    lea -0x20(%rbp),%rax 
    4006ea: 48 89 c7    mov %rax,%rdi 
    4006ed: e8 0e fe ff ff   callq 400500 <[email protected]> 
    4006f2: 48 8d 55 d0    lea -0x30(%rbp),%rdx 
    4006f6: 48 8d 45 e0    lea -0x20(%rbp),%rax 
    4006fa: 48 89 d6    mov %rdx,%rsi 
    4006fd: 48 89 c7    mov %rax,%rdi 
    400700: e8 27 00 00 00   callq 40072c <myfunc2> 
    400705: 48 8d 45 e0    lea -0x20(%rbp),%rax 
    400709: 48 89 c7    mov %rax,%rdi 
    40070c: e8 ef fd ff ff   callq 400500 <[email protected]> 
    400711: b8 00 00 00 00   mov $0x0,%eax 
    400716: 48 8b 4d f8    mov -0x8(%rbp),%rcx 
    40071a: 64 48 33 0c 25 28 00 xor %fs:0x28,%rcx 
    400721: 00 00 
    400723: 74 05     je  40072a <main+0x90> 
    400725: e8 f6 fd ff ff   callq 400520 <[email protected]> 
    40072a: c9      leaveq 
    40072b: c3      retq 

Я подозреваю, что мне нужно что-то сделать с расчетом информации о адресе через относительное местоположение или с помощью инструкций lea/mov.

Любая помощь, чтобы узнать, как изменить вызов функции будет очень ценится - пожалуйста, нет указателей на редактирование строк, как HOWTOs все на большей части интернета ...

+1

Вы также должны скомпилировать код C, например, с 'gcc -fverbose-asm -S', тогда посмотрите на сгенерированный код сборки. Кстати, каков ваш точный вопрос? Вы точно прочитали документацию машинной инструкции 'callq' x86? –

+3

Вместо 'myfunc ((char *) & str2, (char *) &str1);', почему бы вам просто не написать 'myfunc (str2, str1);'? Конечно, это легче читать? Я никогда не пойму, – unwind

+1

Вам также нужно прочитать спецификацию [x86-64 ABI] (http://www.x86-64.org/documentation_folder/abi.pdf). –

ответ

6

Для того, чтобы переписать адреса, то есть точно знать способ кодирования команд callq.

Давайте возьмем разборку выход первого вызова:

4006e1: e8 67 ff ff ff   callq 40064d <myfunc> 
4006e6: ... 

Вы можете ясно видеть, что команда кодируется с 5 байт. Байт e8 - это код операции, а 67 ff ff ff - это адрес для перехода. На данный момент можно задать вопрос: 67 ff ff ff делать с 0x40064d?

Ну, ответ заключается в том, что e8 кодирует так называемый «относительный вызов», и скачок выполняется относительно местоположения следующей команды. Вы должны рассчитать расстояние между и вызываемой функцией, чтобы переписать адрес. Если бы вызов был абсолютным (ff), вы могли бы просто поместить адрес функции в эти 4 байта.

Чтобы доказать, что это так, рассмотрим следующую арифметику:

0x004006e6 + 0xffffff67 == 0x10040064d 
+0

Отлично, хорошо, поэтому выше вы заявили, что если закодированная инструкция имеет другой размер - мне может быть не повезло. Это одна и та же инструкция, так будет ли это хорошо или нет? – mcdoomington

+0

В конкретном случае это не должно вызывать беспокойства - его можно легко закодировать с помощью 5 байтов. Однако в общем случае, если вам нужно заменить инструкцию на более крупную, она сломает весь код ниже этой инструкции. В приведенных ниже инструкциях может использоваться адресация, относящаяся к PC-адресу, или 'call'-адресация, подобная этой, и все их адреса будут компенсированы этой разницей, которую вы вводите. Если новая инструкция меньше, вы можете завершить ее с помощью 'NOP', чтобы обойти эту проблему. –

+0

Итак, чтобы вычислить «относительное положение» - это позиция от вызова myfunc в main на myfunc2? Это немного двусмысленно для меня в данный момент. – mcdoomington

Смежные вопросы