/*
* http://sosal.tistory.com/
* made by so_Sal
*/
가끔 프로그램이, 변수에 어떤 값이 들어가있는지 궁금할 때가 있다.
그때는 심볼이나 디버깅 정보를 삽입하여, gdb에서 watch 명령어나
직접적으로 메모리에 접근하여 정보를 가져오는방법, 레지스터를 확인하는 방법
등이 있는데, 이 포스팅에서 레지스터가 가리키는 문자열 변수에 어떤 값이 들어가있는지
GDB 디버깅 툴을 이용하여 확인하는 방법을 알아보려고 한다.
<간단한 예제를 통해서, 패스워드를 크랙하여보자>
(답) ANUG9LMRKOB^IS_SOSAL
#include<stdio.h>
#include<string.h>
int main(){
char password[] = "MY_PASSWORD_IS_SOSAL";
char input[100] = "";
int length = strlen(password);
int i,j;
for(j=0;j<13;j++){
for(i=0;i<j;i++){
password[i]--;
}
}
printf("password : ");
scanf("%s",input);
if(!strcmp(password,input))
printf("very good!\n");
else
printf("retry!\n");
return 0;
}
물론 위 프로그램을 C언어로 다시 작성하여, password 배열의 값을
출력하면 그만이겠지만, 분석하는 사람이 꼭 프로그래머 이란 법은 없다.
(물론 아이다와 같은 초호화 디버깅 툴이 있지만.. GDB로 한번 해보자)
strcmp() 부분에서, 만들어진 패스워드와 (password) 사용자가 입력한 변수(input)을 비교한다.
비교하기 전에는 각 레지스터에 문자열을 담아 strcmp 함수에 보낼것이다. (매개변수 역할)
strcmp로 보내는 매개변수 (즉 레지스터가 가리키는 변수의 주소)를 체크하면
패스워드를 쉽게 알아낼 수 있게된다.
그럼 gdb로, 어디가 strcmp 부분인지 알아보자.
이부분 역시 아이다와 같은 호화형 디버깅 툴을 쓰면 빨리 찾을 수 있겠지만,
올리디버거나 GDB는 동작중에 제어가 용이하기 때문에 충분히 공부해볼 가치가 있다.
[sosal@localhost binary]$ gdb -q ./strcmp
(no debugging symbols found)
(gdb) disas main <-- disassemble
Dump of assembler code for function main:
0x08048444 <main+0>: lea 0x4(%esp),%ecx
0x08048448 <main+4>: and $0xfffffff0,%esp
0x0804844b <main+7>: pushl -0x4(%ecx)
0x0804844e <main+10>: push %ebp
0x0804844f <main+11>: mov %esp,%ebp
0x08048451 <main+13>: push %edi
0x08048452 <main+14>: push %ecx
0x08048453 <main+15>: sub $0xa0,%esp
0x08048459 <main+21>: mov 0x8048671,%eax
0x0804845e <main+26>: mov %eax,-0x29(%ebp)
............
.. 중략 ..
............
0x08048512 <main+206>: jle 0x80484e6 <main+162>
0x08048514 <main+208>: movl $0x8048650,(%esp)
0x0804851b <main+215>: call 0x8048338 <printf@plt> // <-- 심볼이 살아있기때문에
0x08048520 <main+220>: lea -0x8d(%ebp),%eax // print함수가 바로 보인다.
0x08048526 <main+226>: mov %eax,0x4(%esp)
0x0804852a <main+230>: movl $0x804865c,(%esp)
0x08048531 <main+237>: call 0x8048328 <scanf@plt> // scanf 역시 마찬가지..
0x08048536 <main+242>: lea -0x8d(%ebp),%eax
0x0804853c <main+248>: mov %eax,0x4(%esp)
0x08048540 <main+252>: lea -0x29(%ebp),%eax
0x08048543 <main+255>: mov %eax,(%esp)
0x08048546 <main+258>: call 0x8048358 <strcmp@plt> // 역시 strcmp 함수도 보인다.
0x0804854b <main+263>: test %eax,%eax
0x0804854d <main+265>: jne 0x804855d <main+281>
0x0804854f <main+267>: movl $0x804865f,(%esp)
0x08048556 <main+274>: call 0x8048348 <puts@plt>
0x0804855b <main+279>: jmp 0x8048569 <main+293>
0x0804855d <main+281>: movl $0x804866a,(%esp)
0x08048564 <main+288>: call 0x8048348 <puts@plt>
0x08048569 <main+293>: mov $0x0,%eax
0x0804856e <main+298>: add $0xa0,%esp
0x08048574 <main+304>: pop %ecx
0x08048575 <main+305>: pop %edi
0x08048576 <main+306>: pop %ebp
0x08048577 <main+307>: lea -0x4(%ecx),%esp
0x0804857a <main+310>: ret
End of assembler dump.
그렇다면.. 우리가 유심히 봐야할곳은
0x08048540 <main+252>: lea -0x29(%ebp),%eax
0x08048543 <main+255>: mov %eax,(%esp)
0x08048546 <main+258>: call 0x8048358 <strcmp@plt> // 역시 strcmp 함수도 보인다.
0x0804854b <main+263>: test %eax,%eax
이곳이 아니겠는가?,
mov %eax,(%esp)를 보아 하니
esp가 가리키는곳이 곧, 암호화된 패스워드가 존재하는 곳이고,
그 위치를 eax에다 가져놓으니, eax가 패스워드를 넘기는 매개변수라고 볼 수 있겠다.
그럼 main+258 위치에서, eax라는 레지스터에 패스워드가 저장된다는걸 예상 했으니
직접 문자열을 출력해볼까?
일단 main+258에 break-point를 걸고, 그곳에서 멈춰보자.
(gdb) b *main+258 <-- break-point
Breakpoint 1 at 0x8048546
(gdb) r <-- run
password : sosal
Breakpoint 1, 0x08048546 in main ()
(gdb) info reg <-- 레지스터 값 확인
eax 0xbfb3810f -1078755057
ecx 0xbb4420 12272672
edx 0x0 0
ebx 0xbb3ff4 12271604
.... 이하 생략 .....
드디어 strlen 함수의 매개변수로 넘기는 부분에 도착하했다.
그럼 여기서 당장 레지스터값들의 아스키값을 출력하면
답을 볼 수 있다.
'Linux > Linux_technic' 카테고리의 다른 글
제한시간 1초, 수학문제 프로그램 해킹?! (3) | 2010.11.06 |
---|---|
bof를 이용하여 함수포인터 공략하기 (0) | 2010.11.06 |
Linux :: Strace - 시스템콜과 신호 추적 (1) | 2010.03.04 |
해커스쿨 11~20 정답, 공격코드 (0) | 2010.01.31 |
Linux :: 디버거 GDB 디버깅 테크닉 (2) | 2010.01.31 |