Не удалось самостоятельно изменить код, скопировать / перейти в кучу

Сначала я сожалею о длине этого поста, но я хотел четко объяснить проблему.

Я пытаюсь написать что-то вроде маленькой самоизменяющейся программы на C, но у меня есть некоторые проблемы, и я не знаю точно, почему.

Plateform: Ubuntu / Linux 2.6.32-40 x86_64, программа построена на основе архитектуры x86, gcc (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3, GNU ld (GNU Binutils для Ubuntu) 2.20.1-system.20100303

Цель программы - создать кусок памяти для чтения / записи / выполнения (с помощью memalign(3) и mprotect (2)), скопировать небольшую функцию под названием p() (определяется в .text сегмент) в этой части памяти, а затем выполнить скопированную функцию через указатель. p() Функция просто отображает сообщение, используя printf(puts),

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

int p() { ... }       <- address where the copy starts
int dummy() { ... }   <- address where the copy stops

Создание и копирование чанка памяти успешно завершено, но при запуске кода в чанке происходит ошибка. Используя gdb ясно, что мы вводим код чанка (тело скопированной функции), но вызов printf не удался. При разборке p() Функция и код в чанке Я ​​вижу, что использование адреса в "вызове" не совпадает.

И я не знаю, почему адрес неверный, когда код копируется, он отображается, и это то же самое, что objdump (или GDB) дал мне, когда я разбираю p() функция.

Двоичный файл создан с -static чтобы избежать потенциальной проблемы с got/plt или с процессом перемещения ld.so, Кажется, не проблема запустить код на heap потому что начало скопированной функции выполняется (проверьте под gdb).

Упрощенная программа:

<... skip include/checks ...>
#define newline()   putchar('\n')

/* - function copied in the chunk */ 
int p()
{
    printf("hello world\n");
    return 0;
}

/* - dummy function to get last address of p() */
int dummy() { return 0; }

int main()
{
    char *s, *code, *chunk;
    unsigned int pagesz, sz;
    int (*ptr)(void);

    pagesz = sysconf(_SC_PAGE_SIZE);
    chunk = (char*)memalign(pagesz, 4 * pagesz);
    mprotect(chunk, 4 * pagesz, PROT_WRITE|PROT_EXEC|PROT_READ);

    /* - get size, display addr */
    sz = (char *)dummy - (char *)p;
    printf("Copy between  : %p - %p\n", (char *)p, (char *)dummy);
    printf("Copy in chunk : %p - %p\n", chunk, chunk + sz, sz);

    /* - copy code (1 byte in supp) */
    printf("Copied code   : ");
    for(s = (char *)p, code = chunk; \
            s <= (char *)dummy; s++, code++) {

       *code = *s;     
       /* - write in console -- to check if code of p() after disas
        * it with objdump(1) is the same, RESULT : ok */
        printf("%02x ", *(unsigned char *)code);
    }
    newline();

    /* - run once orginal function */
    ptr = p;
    ptr();

    /* - run copied function (in chunk) */
    ptr = (int (*)(void))chunk;
    ptr(); 

    newline();
    free(chunk);
    return 0;
}

p() функция разбирается на objdump(1):

080483c3 <p>:
 80482c0:       55                      push   %ebp
 80482c1:       89 e5                   mov    %esp,%ebp
 80482c3:       83 ec 18                sub    $0x18,%esp
 80482c6:       c7 04 24 a8 d9 0a 08    movl   $0x80ad9a8,(%esp)
 80482cd:       e8 7e 0c 00 00          call   8048f50 <_IO_puts>
 80482d2:       b8 00 00 00 00          mov    $0x0,%eax
 80482d7:       c9                      leave  
 80482d8:       c3                      ret    

080483dc <dummy>:
 ....

Когда программа запускается в gdb(1), скопированный код совпадает (шестнадцатеричное значение) с предоставленным выше objdump(1):

# gcc -m32 -o selfmodif_light selfmodif_light.c -static -g -O0

# gdb -q ./selfmodif_light
Reading symbols from /path/.../selfmodif_light...done.

(gdb) list 55
50          /* - run once orginal function */
51          ptr = p;
52          ptr();
53
54          /* - run copied function (in chunk) */
55          ptr = (int (*)(void))chunk;

<<< The problem is here >>>

56          ptr();
57      
58          newline();
59          free(chunk);

(gdb) br 56
Breakpoint 1 at 0x8048413: file tmp.c, line 56.

(gdb) run
Starting program: /path/.../selfmodif_light
Copy between  : 0x80482c0 - 0x80482d9
Copy in chunk : 0x80d2000 - 0x80d2019
Copied code   : 55 89 e5 83 ec 18 c7 04 24 a8 d9 0a 08 e8 7e 0c 00 00 b8 00 00 00 00 c9 c3 55
hello world

Breakpoint 1, main () at tmp.c:56
56          ptr();

Если мы посмотрим в основном, мы перейдем к чанку:

(gdb) disas main
Dump of assembler code for function main:
   0x080482e3 <+0>: push   %ebp
    ... <skip> ...

=> 0x08048413 <+304>:   mov    0x18(%esp),%eax
   0x08048417 <+308>:   call   *%eax

    ... <skip> ...
   0x08048437 <+340>:   ret
End of assembler dump.

Но когда p() и chunk разобраны, мы имеем call 0x80d2c90 в куске памяти вместо call 0x8048f50 <puts> как в функции p()? По какой причине отображаемый адрес не совпадает.

(gdb) disas p
Dump of assembler code for function p:
   0x080482c0 <+0>:     push   %ebp
   0x080482c1 <+1>:     mov    %esp,%ebp
   0x080482c3 <+3>:     sub    $0x18,%esp
   0x080482c6 <+6>:     movl   $0x80ad9a8,(%esp)
   0x080482cd <+13>:    call   0x8048f50 <puts> <<= it is not the same address
   0x080482d2 <+18>:    mov    $0x0,%eax
   0x080482d7 <+23>:    leave  
   0x080482d8 <+24>:    ret
End of assembler dump.
(gdb) disas 0x80d2000,0x80d2019
Dump of assembler code from 0x80d2000 to 0x80d2019:
   0x080d2000:  push   %ebp
   0x080d2001:  mov    %esp,%ebp
   0x080d2003:  sub    $0x18,%esp
   0x080d2006:  movl   $0x80ad9a8,(%esp)
   0x080d200d:  call   0x80d2c90             <<= than here (but it should be ??)
   0x080d2012:  mov    $0x0,%eax
   0x080d2017:  leave  
   0x080d2018:  ret
End of assembler dump.

Когда память проверяется, коды кажутся идентичными. На данный момент я не понимаю, что происходит, в чем проблема? Ошибка интерпретации GDB, копия кода или что?

(gdb) x/25bx p // code of p in .text
0x80482c0 <p>:  0x55    0x89    0xe5    0x83    0xec    0x18    0xc7    0x04
0x80482c8 <p+8>:    0x24    0xa8    0xd9    0x0a    0x08    0xe8    0x7e    0x0c
0x80482d0 <p+16>:   0x00    0x00    0xb8    0x00    0x00    0x00    0x00    0xc9
0x80482d8 <p+24>:   0xc3

(gdb) x/25bx 0x80d2000 // code of copy in the chunk
0x80d2000:  0x55    0x89    0xe5    0x83    0xec    0x18    0xc7    0x04
0x80d2008:  0x24    0xa8    0xd9    0x0a    0x08    0xe8    0x7e    0x0c
0x80d2010:  0x00    0x00    0xb8    0x00    0x00    0x00    0x00    0xc9
0x80d2018:  0xc3

Если точка останова установлена, то выполнение продолжается в блоке памяти:

(gdb) br *0x080d200d
Breakpoint 2 at 0x80d200d
(gdb) cont
Continuing.

Breakpoint 2, 0x080d200d in ?? ()
(gdb) disas 0x80d2000,0x80d2019
Dump of assembler code from 0x80d2000 to 0x80d2019:
   0x080d2000:  push   %ebp
   0x080d2001:  mov    %esp,%ebp
   0x080d2003:  sub    $0x18,%esp
   0x080d2006:  movl   $0x80ad9a8,(%esp)
=> 0x080d200d:  call   0x80d2c90
   0x080d2012:  mov    $0x0,%eax
   0x080d2017:  leave
   0x080d2018:  ret
End of assembler dump.
(gdb) info reg eip
eip            0x80d200d    0x80d200d
(gdb) nexti
0x080d2c90 in ?? ()
(gdb) info reg eip
eip            0x80d2c90    0x80d2c90
(gdb) bt
#0  0x080d2c90 in ?? ()
#1  0x08048419 in main () at selfmodif_light.c:56

Таким образом, на этом этапе либо программа запускается таким образом, и возникает ошибка, либо изменяется $ eip, и программа завершается без ошибок.

(gdb) set $eip = 0x8048f50
(gdb) cont
Continuing.
hello world

Program exited normally.
(gdb)

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

Спасибо за ваши ответы и ваше время

1 ответ

Решение
80482cd:       e8 7e 0c 00 00          call   8048f50

Это относительный CALL (до +0xC7E). Когда вы перемещаете эту инструкцию в другой EIP, вам нужно изменить смещение.

Другие вопросы по тегам