본문 바로가기
스터디/system

[GDB-Analysis] GDB 바이너리 디버깅 3

by 깝태 2011. 8. 18.

이번에는 저번에 두개로 했던 것들과 strcat 함수를 곁들여 컴파일해 디버깅을 해보도록 하겠습니다.
다음부터는 버퍼 오버플로우와 관련된 함수를 주제로 디버깅 연습을 해갈 생각입니다.

[gate@localhost tmp]$ cat t3.c
#include<stdio.h>
#include<stdlib.h>

int main(int argc, char *argv[])
{
        char buffer[40];

        printf("%d  ", argc); 
        printf("%s  ", argv[0]);
        printf("%s  ", argv[1]);
        printf("%s  ", argv[2]);
        printf("%s\n", argv[3]);

        strcpy(buffer, argv[1]);
        printf("%s\n", buffer);

        strcat(argv[2], argv[3]);
        printf("%s\n", argv[2]);

        return 0;
}

[gate@localhost tmp]$ ./t3 1 2 3 4
5  ./t3  1  2  3
1 // Argv[1] == Buffer
23 // Argv[2] + Argv[3] 

[gate@localhost tmp]$ gdb -q t3
(gdb) disas main
Dump of assembler code for function main:
0x8048440 <main>:       push   %ebp
0x8048441 <main+1>:     mov    %esp,%ebp // 프롤로그
0x8048443 <main+3>:     sub    $0x28,%esp // 변수 40바이트 선언
0x8048446 <main+6>:     mov    0x8(%ebp),%eax // EAX 에 (EBP+8) 값 복사 - (EBP+8) == Argc
0x8048449 <main+9>:     push   %eax // EAX 푸시
0x804844a <main+10>:    push   $0x8048560 // 0x8048560 푸시 - %d
0x804844f <main+15>:    call   0x8048364 <printf>
//        printf("%d  ", argc); 

0x8048454 <main+20>:    add    $0x8,%esp // ESP + 8
0x8048457 <main+23>:    mov    0xc(%ebp),%eax // EAX 에 (EBP+12) 값 복사 - (EBP+12) == Argv[0]
0x804845a <main+26>:    mov    (%eax),%edx // EDX 에 EAX 값 복사
0x804845c <main+28>:    push   %edx // EDX 푸시
0x804845d <main+29>:    push   $0x8048565 // 0x8048565 푸시 - %s
0x8048462 <main+34>:    call   0x8048364 <printf>
//        printf("%s  ", argv[0]);

0x8048467 <main+39>:    add    $0x8,%esp // ESP + 8
0x804846a <main+42>:    mov    0xc(%ebp),%eax // EAX 에 (EBP+12) 값 복사 - Argv[0]
0x804846d <main+45>:    add    $0x4,%eax // EAX + 4 -  Argv[0] + 4 == Argv[1]
0x8048470 <main+48>:    mov    (%eax),%edx // EDX 에 EAX 값 복사
0x8048472 <main+50>:    push   %edx // EDX 푸시
0x8048473 <main+51>:    push   $0x8048565 // 0x8048565 푸시
0x8048478 <main+56>:    call   0x8048364 <printf>
//        printf("%s  ", argv[1]);

0x804847d <main+61>:    add    $0x8,%esp
0x8048480 <main+64>:    mov    0xc(%ebp),%eax // EAX 에 (EBP+12) 값 복사 - Argv[0]
0x8048483 <main+67>:    add    $0x8,%eax // Argv[0] + 8 == Argv[2]
0x8048486 <main+70>:    mov    (%eax),%edx
0x8048488 <main+72>:    push   %edx
0x8048489 <main+73>:    push   $0x8048565
0x804848e <main+78>:    call   0x8048364 <printf>
//        printf("%s  ", argv[2]);

0x8048493 <main+83>:    add    $0x8,%esp
0x8048496 <main+86>:    mov    0xc(%ebp),%eax // EAX 에 (EBP+12) 값 복사 - Argv[0]
0x8048499 <main+89>:    add    $0xc,%eax // Argv[0] + 12 == Argv[3]
0x804849c <main+92>:    mov    (%eax),%edx
0x804849e <main+94>:    push   %edx
0x804849f <main+95>:    push   $0x804856a
0x80484a4 <main+100>:   call   0x8048364 <printf>
//        printf("%s  ", argv[3]);

0x80484a9 <main+105>:   add    $0x8,%esp // ESP + 8
0x80484ac <main+108>:   mov    0xc(%ebp),%eax // EAX 에 (EBP+12) 값 복사 - Argv[0]
0x80484af <main+111>:   add    $0x4,%eax // EAX + 4 == Argv[1]
0x80484b2 <main+114>:   mov    (%eax),%edx // EDX 에 EAX 값 복사
0x80484b4 <main+116>:   push   %edx // EDX 푸시
0x80484b5 <main+117>:   lea    0xffffffd8(%ebp),%eax // EAX 에 (EBP-40) 주소 값 복사 - Buffer
0x80484b8 <main+120>:   push   %eax // EAX 푸시
0x80484b9 <main+121>:   call   0x8048374 <strcpy>
//        strcpy(buffer, argv[1]);
                      EAX   , EDX

0x80484be <main+126>:   add    $0x8,%esp
0x80484c1 <main+129>:   lea    0xffffffd8(%ebp),%eax // EAX 에 (EBP-40) 주소값 복사 - Buffer
0x80484c4 <main+132>:   push   %eax // EAX 푸시
0x80484c5 <main+133>:   push   $0x804856a // 0x804856a 푸시 - %s\n
0x80484ca <main+138>:   call   0x8048364 <printf>

0x80484cf <main+143>:   add    $0x8,%esp // ESP + 8
0x80484d2 <main+146>:   mov    0xc(%ebp),%eax // EAX 에 (EBP+12) 값 복사 - Argv[0]
0x80484d5 <main+149>:   add    $0xc,%eax // EAX + 12 == Argv[3]
0x80484d8 <main+152>:   mov    (%eax),%edx // EDX 에 EAX 값 복사
0x80484da <main+154>:   push   %edx // EDX(1) 푸시
0x80484db <main+155>:   mov    0xc(%ebp),%eax // EAX 에 (EBP+12) 값 복사 - Argv[0]
0x80484de <main+158>:   add    $0x8,%eax // EAX + 8 == Argv[2]
0x80484e1 <main+161>:   mov    (%eax),%edx // EDX 에 EAX 값 복사
0x80484e3 <main+163>:   push   %edx // EDX(2) 푸시
0x80484e4 <main+164>:   call   0x8048354 <strcat>
//        strcat(argv[2], argv[3]);
                     EDX(2), EDX(1)

0x80484e9 <main+169>:   add    $0x8,%esp
0x80484ec <main+172>:   mov    0xc(%ebp),%eax // EAX 에 (EBP+12) 값 복사 - Argv[0]
0x80484ef <main+175>:   add    $0x8,%eax // EAX + 8 == Argv[2]
0x80484f2 <main+178>:   mov    (%eax),%edx
0x80484f4 <main+180>:   push   %edx
0x80484f5 <main+181>:   push   $0x804856a
0x80484fa <main+186>:   call   0x8048364 <printf>
//        printf("%s\n", argv[2]);

0x80484ff <main+191>:   add    $0x8,%esp
0x8048502 <main+194>:   xor    %eax,%eax // EAX 초기화
0x8048504 <main+196>:   jmp    0x8048506 <main+198>
0x8048506 <main+198>:   leave
0x8048507 <main+199>:   ret // 에필로그
/* 생략 */
End of assembler dump.

메모리 구조를 확인해봅시다.

BUFFER (40Byte) | SFP (기준) |    RET    |     Argc     |     Argv[0] - Argv[1] - Argv[2] - Argv[3]     |
       EBP-40                   EBP          EBP+4     EBP+8         +12         +16       +20         +24

그래서 인자를 확인할떄는 먼저 Argv[0] 으로 (EBP+12) 이동 한 후 4 씩 늘려가지만
Buffer 을 참조해야할때는 (EBP-40) 의 주소 값을 복사하는겁니다.

Strcat 함수를 디버깅 해보았는데 그다지 어렵지 않았고 매우 쉬웠습니다.
다만 인자를 거꾸로 참조한다는것만 알아두면 될것같습니다. 그런데 인자를 거꾸로 넣는건 함수는
원래 다그렇습니다. cdcel 방식 때문에 그렇다는데 자세한 설명은 다음기회로 미루겠습니다.