제가 직접 간단하게 소스를 만들어 디버깅을 할 생각입니다.
워게임을 풀면서 많이나오는 소스들을 가지고 디버깅을 해볼 생각입니다. (argv, strncpy 인자 불러오기 등등)
물론 IDA 로해야지 쉽고 빠르지만 그것보다 평소 디버깅 실력을 높이기위해 도전해봅니다.
제일 처음으로 GDB 로 디버깅을 하여 강의할 프로그램은 다음과 같습니다.
[gate@localhost tmp]$ cat t1.c
#include<stdio.h>
int main(int argc, char *argv[])
{
printf("%s ", argv[0]);
printf("%s ", argv[1]);
printf("%s ", argv[2]);
printf("%s\n", argv[3]);
return 0;
}
[gate@localhost tmp]$ ./t1 1 2 3 4
./t1 1 2 3
인자 4개를 출력해주는 프로그램 입니다.
바로 디버깅을 시작해보겠습니다.
[gate@localhost tmp]$ gdb -q t1
(gdb) disas main
Dump of assembler code for function main:
0x80483d0 <main>: push %ebp
0x80483d1 <main+1>: mov %esp,%ebp
0x80483d3 <main+3>: mov 0xc(%ebp),%eax
0x80483d6 <main+6>: mov (%eax),%edx
0x80483d8 <main+8>: push %edx
0x80483d9 <main+9>: push $0x8048490
0x80483de <main+14>: call 0x8048308 <printf>
0x80483e3 <main+19>: add $0x8,%esp
0x80483e6 <main+22>: mov 0xc(%ebp),%eax
0x80483e9 <main+25>: add $0x4,%eax
0x80483ec <main+28>: mov (%eax),%edx
0x80483ee <main+30>: push %edx
0x80483ef <main+31>: push $0x8048490
0x80483f4 <main+36>: call 0x8048308 <printf>
0x80483f9 <main+41>: add $0x8,%esp
0x80483fc <main+44>: mov 0xc(%ebp),%eax
0x80483ff <main+47>: add $0x8,%eax
0x8048402 <main+50>: mov (%eax),%edx
0x8048404 <main+52>: push %edx
0x8048405 <main+53>: push $0x8048490
0x804840a <main+58>: call 0x8048308 <printf>
0x804840f <main+63>: add $0x8,%esp
---Type <return> to continue, or q <return> to quit---
0x8048412 <main+66>: mov 0xc(%ebp),%eax
0x8048415 <main+69>: add $0xc,%eax
0x8048418 <main+72>: mov (%eax),%edx
0x804841a <main+74>: push %edx
0x804841b <main+75>: push $0x8048495
0x8048420 <main+80>: call 0x8048308 <printf>
0x8048425 <main+85>: add $0x8,%esp
0x8048428 <main+88>: xor %eax,%eax
0x804842a <main+90>: jmp 0x8048430 <main+96>
0x804842c <main+92>: lea 0x0(%esi,1),%esi
0x8048430 <main+96>: leave
0x8048431 <main+97>: ret
0x8048432 <main+98>: nop
0x8048433 <main+99>: nop
0x8048434 <main+100>: nop
0x8048435 <main+101>: nop
0x8048436 <main+102>: nop
0x8048437 <main+103>: nop
0x8048438 <main+104>: nop
0x8048439 <main+105>: nop
0x804843a <main+106>: nop
0x804843b <main+107>: nop
0x804843c <main+108>: nop
---Type <return> to continue, or q <return> to quit---
0x804843d <main+109>: nop
0x804843e <main+110>: nop
0x804843f <main+111>: nop
End of assembler dump.
(gdb) [gate@localhost tmp]$ clear
[gate@localhost tmp]$ cat t1.c
#include<stdio.h>
int main(int argc, char *argv[])
{
printf("%s ", argv[0]);
printf("%s ", argv[1]);
printf("%s ", argv[2]);
printf("%s\n", argv[3]);
return 0;
}
[gate@localhost tmp]$ ./t1 1 2 3 4
./t1 1 2 3
대부분의 분들이 아시겠지만 모르는분들도 계시고, 복습차로 더해봅니다.
/*
EBP+4 == RET
↓ 4Byte
EBP+8 == Argc == 인자 개수
↓ 4Byte
EBP+12 == Argv[0] == 프로그램 이름 == t1
↓ 4Byte
EBP+16 == Argv[1] == 1
↓ 4Byte
EBP+20 == Argv[2] == 2
Argv[0] Argv[1] Argv[2] == Argc 3
mov A B == B 에 A 의 값을 복사하다, 대입하다, 넣다
*/
[gate@localhost tmp]$ gdb -q t1
(gdb) disas main
Dump of assembler code for function main:
0x80483d0 <main>: push %ebp
0x80483d1 <main+1>: mov %esp,%ebp // 프롤로그
0x80483d3 <main+3>: mov 0xc(%ebp),%eax // EAX 에 (EBP+12) 값 복사 - (EBP+12) == argv[0]
0x80483d6 <main+6>: mov (%eax),%edx // EDX 에 EAX 값 복사
0x80483d8 <main+8>: push %edx // EDX 푸시
0x80483d9 <main+9>: push $0x8048490 // 0x8048490 푸시 - %s
0x80483de <main+14>: call 0x8048308 <printf>
/*
printf("%s ", argv[0]); 호출합니다.
위의 소스를 제대로 분석하셨다면 푸시한 EDX 에는 Argv[0] 이 들어가있는것과
0x8048490 에는 "%s " 가 들어가있다는것을 아실 수 있습니다.
*/
0x80483e3 <main+19>: add $0x8,%esp // ESP+8
0x80483e6 <main+22>: mov 0xc(%ebp),%eax // EAX 에 (EBP+12) 의 값 복사
0x80483e9 <main+25>: add $0x4,%eax // EAX + 4 == 아까 Argv[0] 에서 4를 더해 Argv[1] 이 됩니다.
0x80483ec <main+28>: mov (%eax),%edx // EDX 에 EAX 값을 넣습니다.
0x80483ee <main+30>: push %edx // EDX 를 푸시합니다.
0x80483ef <main+31>: push $0x8048490 // 0x8048490 을 푸시합니다. - 아까와 같은 과정을 거칩니다.
0x80483f4 <main+36>: call 0x8048308 <printf>
// printf("%s ", argv[1]); 굳이 설명하지 않아도 아실겁니다.
0x80483f9 <main+41>: add $0x8,%esp
0x80483fc <main+44>: mov 0xc(%ebp),%eax // EAX 에 (EBP+12) 의 값 복사
0x80483ff <main+47>: add $0x8,%eax // EAX + 8 == 아까 Argv[0] 에서 8을 더해 Argv[2] 가 됩니다.
0x8048402 <main+50>: mov (%eax),%edx
0x8048404 <main+52>: push %edx
0x8048405 <main+53>: push $0x8048490
0x804840a <main+58>: call 0x8048308 <printf>
// printf("%s ", argv[2]);
0x804840f <main+63>: add $0x8,%esp
0x8048412 <main+66>: mov 0xc(%ebp),%eax // EAX 에 (EBP+12) 의 값 복사
0x8048415 <main+69>: add $0xc,%eax // EAX + 12 == 아까 Argv[0] 에서 12을 더해 Argv[3] 가 됩니다.
0x8048418 <main+72>: mov (%eax),%edx
0x804841a <main+74>: push %edx
0x804841b <main+75>: push $0x8048495
0x8048420 <main+80>: call 0x8048308 <printf>
// printf("%s\n", argv[3]);
0x8048425 <main+85>: add $0x8,%esp
0x8048428 <main+88>: xor %eax,%eax
0x804842a <main+90>: jmp 0x8048430 <main+96>
0x804842c <main+92>: lea 0x0(%esi,1),%esi
0x8048430 <main+96>: leave
0x8048431 <main+97>: ret // 에필로그
/* 생략 */
End of assembler dump.
프로그램을 정상적으로 디버깅해보았습니다. 그러면 한번 얻어가는게 있어야하니깐
입력했을때 과연 EDX 에 정상적으로 들어가는지 확인해보겠습니다.
(gdb) b *main+14
Breakpoint 3 at 0x80483de
/*
Argv[0] 을 printf 하는 부분에 브레이크포인터를 걸었습니다.
아까 보셨듯이 Argv[0] 에는 프로그램의 이름이 들어갑니다.
(gdb) r a a a a
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/gate/xodnr/tmp/t1 a a a a
/bin/bash: /home/orc/.bashrc: Permission denied
Breakpoint 3, 0x80483de in main ()
(gdb) x/s $edx
0xbffffc88: "/home/gate/xodnr/tmp/t1" // Argv[0] 참조
그러면 설명을 생략하고 Argv[1] , Argv[2], Argv[3] 에 모두 브포를 걸고 보여드리겠습니다
(gdb) b *main+36
Breakpoint 9 at 0x80483f4
(gdb) r 1 2 3 4
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/gate/xodnr/tmp/t1 1 2 3 4
/bin/bash: /home/orc/.bashrc: Permission denied
Breakpoint 9, 0x80483f4 in main ()
(gdb) x/s $edx
0xbffffca0: "1" // Argv[1] 참조
(gdb) b *main+58
Breakpoint 10 at 0x804840a
(gdb) r 1 2 3 4
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/gate/xodnr/tmp/t1 1 2 3 4
/bin/bash: /home/orc/.bashrc: Permission denied
Breakpoint 10, 0x804840a in main ()
(gdb) x/s $edx
0xbffffca2: "2" // Argv[2] 참조
(gdb) b *main+80
Breakpoint 11 at 0x8048420
(gdb) r 1 2 3 4
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /home/gate/xodnr/tmp/t1 1 2 3 4
/bin/bash: /home/orc/.bashrc: Permission denied
Breakpoint 11, 0x8048420 in main ()
(gdb) x/s $edx
0xbffffca4: "3" // Argv[3] 참조
이렇게 GDB 를 이용한 바이너리 분석 문제 1을 끝마치겠습니다.
'스터디 > system' 카테고리의 다른 글
[GDB-Analysis] GDB 바이너리 디버깅 3 (0) | 2011.08.18 |
---|---|
[GDB-Analysis] GDB 바이너리 디버깅 2 (0) | 2011.08.17 |
[IDA-Analysis] Binary Wargames Level1 (0) | 2011.08.17 |
메모리 기본 구조 STACK_EBP (0) | 2011.08.15 |
[리눅스갤] 공유(동적)라이브러리 와 정적라이브러리 (3) | 2011.03.20 |