disp $ebp
disp $esp
disp /x $eip
disp /i $pc
disp /4xw $esp
-
set disassembly-flavor intel
=> 문법을 인텔로 변경
(gdb) disas main
Dump of assembler code for function main:
0x080484ad <+0>: push ebp
0x080484ae <+1>: mov ebp,esp
0x080484b0 <+3>: sub esp,0x4
0x080484b3 <+6>: mov DWORD PTR [ebp-0x4],0x1
0x080484ba <+13>: push DWORD PTR [ebp-0x4]
0x080484bd <+16>: push 0x804856c
0x080484c2 <+21>: call 0x8048350 <printf@plt>
0x080484c7 <+26>: add esp,0x8
0x080484ca <+29>: mov eax,0x0
0x080484cf <+34>: leave
0x080484d0 <+35>: ret
intel 문법
- 레지스터에 %가 없고, 즉시값에 $가 없다
- 주소에 [] 대괄호를 사용 ex) DWORD PTR [ebp-0x4]
- att 문법과 피연산자 순서가 뒤바뀌어있음
att가 movl $0x4, $esp 라면
intel은 mov esp, 0x4 가 된다
- intel_syntax noprefix 가 있어야 레지스터에 %를 생략한다
mov (기본적으로 32bit=4바이트 단위)
movb 1바이트
movw 2바이트
movq 8바이트
(gdb) x/4xw $esp
0xffffd444: 0x12345678
(gdb) /i $pc
mov BYTE PTR [esp+0x1],0x01
(gdb) x/4xw $esp
0xffffd444: 0x12345601
(gdb) /i $pc
mov WORD PTR [esp+0x1],0x23 <- 0x23은 0x0023과 같다. 2바이트이기 때문에, 빈곳은 0으로 채워짐
(gdb) x/4xw $esp
0xffffd444: 0x12002301
(gdb) /i $pc
mov WORD PTR [esp+0x2],0x4567
(gdb) x/4xw $esp
0xffffd444: 0x45672301
(gdb) /i $pc
mov DWORD PTR [esp],0x12345678
(gdb) x/4xw $esp
0xffffd444: 0x12345678
movb나 movw에 DWORD PTR을 사용하면 형식이 맞지 않아 에러가 난다
test-intel-mov2.s: Assembler messages:
test-intel-mov2.s:14: Error: conflicting operand size modifiers
shell returned 1
intel 문법으로 문자열 출력하기
.LC0 선언은 같지만,
push OFFSET FLAT:.LC0 으로 참조하는 부분이 달라졌다
LC0이 아니라 다른 이름으로 사용해도 됨
(Local Constant 문자열 상수라는 뜻)
(gdb) x/i $pc
=> 0x80484c1 <main+3>: push 0x80484ad <- .LC0
(gdb) x/4xw $esp
0xffffd444: 0x080484ad
OFFSET FLAT:
=> 특정 주소를 참조할 때 사용한다. 레이블은 메모리 내 특정 위치, OFFSET은 레이블의 실제 주소
FLAT는 메모리 모델을 지정한다
32비트, 64비트 환경에선 보통 평면메모리 모델을 사용한다
평면 메모리 모델에서는 세그먼트 레지스터의 영향을 받지 않고 메모리 공간이 연속적이고 평평하게 배치된다
주로 DOS, 윈도우, 리눅스 등에서 사용
gef 설치
dnf -y install git
git clone https://github.com/hugsy/gef.git
# tree gef/
gef/
├── LICENSE
├── README.md
├── docs
│ ├── api
│ │ └── gef.md
│ ├── api.md
│ ├── commands
│ │ ├── aliases.md
│ │ ├── arch.md
... 하략
# echo "source ~/gef/gef.py" > ~/.gdbinit
=> .gdbinit에 gef 관련 설정 넣어줌
=> gef가 적용된 모습. 기본적으로 intel 문법으로 나타난다
si를 통해 함수 내부로 들어갔을 때 표시됨 func1
복귀주소 0x080484cf
이후 push ebp 되면서 esp가 이동하면서 esp에 이전 ebp값이 담겼고
ebp와 esp의 위치가 같아지면서, ebp에 esp값을 복사해두어 ebp에 ebp의 복귀주소가 담긴다
gef 데이터가 너무 많아서 화면에 다 보이지 않을때엔 context 명령어를 입력하면 조정된다
레지스터만 지정해서 출력
[ Legend: Modified register | Code | Heap | Stack | String ]
=> gef 출력 목록 리스트
실습) for문을 intel 문법으로 작성
기본적인 코드 형태
int main()
{
int i = 1;
for(i = 0; i < 3; i++){
printf("i= %d\n", i);
}
printf("for test end...");
return 0;
}
(gdb) disas main Dump of assembler code for function main: 0x080484c7 <+0>: push ebp 0x080484c8 <+1>: mov ebp,esp 0x080484ca <+3>: sub esp,0x4 0x080484cd <+6>: mov DWORD PTR [ebp-0x4],0x1 0x080484d4 <+13>: mov DWORD PTR [ebp-0x4],0x0 0x080484db <+20>: jmp 0x80484dd <main+22> 0x080484dd <+22>: cmp DWORD PTR [ebp-0x4],0x2 0x080484e1 <+26>: jle 0x80484e5 <main+30> 0x080484e3 <+28>: jmp 0x80484fb <main+52> 0x080484e5 <+30>: push DWORD PTR [ebp-0x4] 0x080484e8 <+33>: push 0x80484ad 0x080484ed <+38>: call 0x8048350 <printf@plt> 0x080484f2 <+43>: add esp,0x8 0x080484f5 <+46>: add DWORD PTR [ebp-0x4],0x1 0x080484f9 <+50>: jmp 0x80484dd <main+22> 0x080484fb <+52>: push 0x80484b6 0x08048500 <+57>: call 0x8048350 <printf@plt> 0x08048505 <+62>: add esp,0x4 0x08048508 <+65>: mov eax,0x0 0x0804850d <+70>: leave 0x0804850e <+71>: ret 0x0804850f <+72>: nop |
컴파일 전에 주소를 알 수 없으므로, 라벨을 붙여 그곳으로 점프한다
실습2)
sub esp, 12로 처음부터 미리 변수 세 자리를 확보해 놓는 경우
printf("%d", i); 의 i 에 대한 복사
push DWORD PTR[ebp-0x04]
=> mov eax, DWORD PTR[ebp-0x04]
mov DWORD PTR[ebp-0x08], eax
mov는 메모리주소에서 메모리주소로 옮길 수 없기 때문에
eax 레지스터로 값을 한 번 옮겨야 한다
printf("%d", i); 의 "%d" 에 대한 복사
push OFFSET FLAT:.LC0
=> mov DWORD PTR[ebp-12], OFFSET FLAT:.LC0 ( [ebp-12] = [ebp-0xc] )