일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
Tags
- Multimedia
- N0Named
- disk
- 윈도우 프로세스
- pythonchallenge
- 윈도우 프로세스 종류
- slack space
- 디지털포렌식
- 포렌식
- Window process
- 24시간의 전사
- 생활코딩 html
- CTF-D
- blkls
- 정보처리기사 필기 합격
- 디지털포렌식 with CTF
- Multimeida
- 는 하지마...
- 네트워크 보안
- 2022시작
- vinetto
- network
- html
- 생활코딩
- memory
- 실기
- 슬퍼유
- ftz
- ZIP 파일구조
- 네트워크관리사2급
Archives
- Today
- Total
ssoL2 TISTORY
intel x86 어셈블리, 스택, 콜링 컨벤션 본문
intel x86
- 인텔이 개발한 cpu
- 다양한 종류 많음 8086, 80186 등
- 근데 x86이 제일 널리 쓰임
- 64 bit cpu를 요즘 많이 쓰는데 그건 x64 혹은 amd64라고 불림
어셈블리어
- cpu가 이해하는 기계어와 1:1로 매칭되는 저급언어 -> c, java 등 고급언어
- cpu 아키텍처마다 어셈블리 다름 ex) x86, x64, arm, mips 등
- 문법은 두개임 Intel, AT&T
- 구조 => [명령 오퍼랜드1, 오퍼랜드2]
레지스터
- cpu에 존재
- eax : accumulator esgister (함수 return 값 저장)
- ebx : base
- ecx : count
- edx : data register
- esi : source index
- edi : destination index
- esp : stack pointer (스택 포인터)
- ebp : base pointer (스택 프레임의 기준)
- eip : instruction pointer (현재 실행 주소)
어셈블리 명령어
- mov destination, source : source를 destination에 대입
- lea destination, [source] : source의 주소를 destination에 대입
- add destination source : destination += source
- sub destination source : destination -= source
- inc eax : 레지스터 값을 1 올린다. (increase)
- dec ebx : 레지스터의 값을 1 내린다. (decrease)
- xor destination, source : destination = destination XOR source
- and destination, source : destination = destination AND source
- or desination, source : destination = destination OR source
- cmp destination, source : destination과 source 비교 => destination이 source보다 ~할 때 성립
- jmp offset : offset주소로 eip 지정
- je (jump equal) : 두 값이 같으면 jump
- jb (jump below) : destination < source 일 때 jump (unsigned)
- ja (jump above) : destination > source 일 때 jump (unsigned)
- jl (jump less) : destination< source 일 때 jump (signed)
- jg (jump greater) : destination > source 일 때 jump (signed)
- jne (jump not equal) : 두 값이 같지 않으면 jump
- call offest : 현재 eip를 memory에 저장 후 offset으로 jmp
- push ebp : esp를 감소시키고 ebp를 esp가 가리키고 있는 곳에 저장
- pop ebp : esp가 가리키고 있는 값을 ebp 레지스터로 복원
- ret : esp가 가리키고 있는 값을 eip로 바꿈
memory 구조
- Stack (스택) : 함수들이 사용할 공간으로 지역변수 위치
- Heap (힙) : 동적할당을 위한 공간으로 malloc() 함수 할당
- Data (데이터) : 문자열이나 정적 변수 존재
- BSS : 전역 변수 존재
- Text (Code) : 실제 프로그램의 코드 존재 (opcode)
Strack frame
- 스택안에 프레임 단위로 공간 할당
- 함수가 끝나면 스택 메모리에 저장해놓은 Return Address를 참조해 이전 함수로 돌아감
- push : esp -= 4 하고 지정한 값을 스택에 저장
- pop : esp가 가리키고 있는 값(포인터)을 지정 레지스터에 저장 후 esp += 4
- ret : 현재 esp가 가리키고 있는 값을 eip에 저장 후 esp += 4
- -> pop eip와 같음
- call : push eip; jmp offset
- leave : mov esp, ebp; pop ebp
어셈블리 분석 예시
#include <stdio.h>
int add(int x, int y);
int main()
{
int a,b;
a=10;
b=20;
printf("%d\n", add(a,b));
}
int add(int x,int y)
{
return x+y;
}
1. main 함수
- call <add> 함수 발견
2. add 함수
- push rbp; mov rbp, rsp => 함수 프롤로그
- pop rbp; ret => 함수 에필로그
- 참고로 64 bit proces라서 r로 시작함. 32 bit는 e로 시작
- DWORD : 4 Byte
- 위에 어셈블리 전체적으로 한번 과정을 돌려보면 재밌음 >> 필수
- 대충 최종적으로 아래 그림과 같음 + 레지스터 eax, ebx, ecx, edx, esp, ebp, eip 같이 사용
SFP
- "Stack Frame Pointer"
- 이전 함수의 EBP를 스택에 저장해놓음
- 약간 스택 프레임을 구분하기 위한 ebp 저장 포인터
Little Endian
- 메모리 주소에 4 Byte 씩 거꾸로 배치됨
Hand-lays
- 지역 변수 할당함
- -> ex) mov DWORD PTR [ebp-xx], yy
- 함수 호출 전 인자 정리함
- -> ex) push DWORD PTR [ebp-xx] , 이때 거꾸로 push됨
- 매개변수 전달 받는 방식
- -> ex) call 함수 안에서, mov edx, DWORD PTR[ebp+xx], 이땐 순서대로
- 함수 종료 전 리턴 값 지정
- -> ex) add eax, edx , return값은 eax에 저장
Hand-lays 예시
(gdb) disas main
Dump of assembler code for function main:
0x080483db <+0>: push ebp
0x080483dc <+1>: mov ebp,esp
0x080483de <+3>: sub esp,0x10
0x080483e1 <+6>: mov DWORD PTR [ebp-0x8],0x0
0x080483e8 <+13>:mov DWORD PTR [ebp-0x4],0x0
0x080483ef <+20>: mov DWORD PTR [ebp-0x8],0x0
0x080483f6 <+27>: jmp 0x8048402 <main+39>
0x080483f8 <+29>: mov eax,DWORD PTR [ebp-0x8]
0x080483fb <+32>: add DWORD PTR [ebp-0x4],eax
0x080483fe <+35>: add DWORD PTR [ebp-0x8],0x1
0x08048402 <+39>:cmp DWORD PTR [ebp-0x8],0x9
0x08048406 <+43>:jle 0x80483f8 <main+29>
0x08048408 <+45>:mov eax,0x0
0x0804840d <+50>: leave
0x0804840e <+51>:ret
End of assembler dump.
(gdb)
--------------------------------------------------------------
#include <stdio.h>
int main()
{
int a=0;
int b=0;
for(a=0; a<=9; a++)
{
b+=a;
}
return 0;
}
(gdb) disas main
Dump of assembler code for function main:
0x0804845b <+0>: push ebp
0x0804845c <+1>: mov ebp,esp
0x0804845e <+3>: sub esp,0x4
0x08048461 <+6>: mov DWORD PTR [ebp-0x4],0x0
0x08048468 <+13>: lea eax,[ebp-0x4]
0x0804846b <+16>: push eax
0x0804846c <+17>: push 0x8048530
0x08048471 <+22>: call 0x8048340 <__isoc99_scanf@plt>
0x08048476 <+27>: add esp,0x8
0x08048479 <+30>: mov eax,DWORD PTR [ebp-0x4]
0x0804847c <+33>: cmp eax,0x10
0x0804847f <+36>: jne 0x8048490 <main+53>
0x08048481 <+38>: push 0x8048533
0x08048486 <+43>: call 0x8048320 <puts@plt>
0x0804848b <+48>: add esp,0x4
0x0804848e <+51>: jmp 0x804849d <main+66>
0x08048490 <+53>: push 0x804853b
0x08048495 <+58>: call 0x8048320 <puts@plt>
0x0804849a <+63>: add esp,0x4
0x0804849d <+66>: mov eax,0x0
0x080484a2 <+71>: leave
0x080484a3 <+72>: ret
End of assembler dump.
(gdb) x/s 0x08048530
0x8048530: "%d"
(gdb) x/s 0x08048533
0x8048533: "Correct"
(gdb) x/s 0x0804853b
0x804853b: "Wrong"
(gdb)
----------------------------------------------------------
#include <stdio.h>
int main(void)
{
int a=0;
scanf("%d", &a);
if(a==16)
{
puts("Wrong")
}
else
{
puts("Correct")
}
}
(gdb) disas main
Dump of assembler code for function main:
0x080483db <+0>: push ebp
0x080483dc <+1>: mov ebp,esp
0x080483de <+3>: sub esp,0x10
0x080483e1 <+6>: mov DWORD PTR [ebp-0x8],0x7a69
0x080483e8 <+13>:mov DWORD PTR [ebp-0x4],0x0
0x080483ef <+20>: lea eax,[ebp-0x8]
0x080483f2 <+23>: mov DWORD PTR [ebp-0x4],eax
0x080483f5 <+26>: mov eax,DWORD PTR [ebp-0x4]
0x080483f8 <+29>: mov eax,DWORD PTR [eax]
0x080483fa <+31>: lea edx,[eax+0xa]
0x080483fd <+34>: mov eax,DWORD PTR [ebp-0x4]
0x08048400 <+37>:mov DWORD PTR [eax],edx
0x08048402 <+39>:mov eax,0x0
0x08048407 <+44>:leave
0x08048408 <+45>:ret
End of assembler dump.
(gdb)
------------------------------------------------------------
#include <stdio.h>
int main()
{
int a=0x7a69;
int *b=0;
b=&a
*b=*b+0xa
}
(gdb) disas main
Dump of assembler code for function main:
0x080483db <+0>: push ebp
0x080483dc <+1>: mov ebp,esp
0x080483de <+3>: sub esp,0x20
0x080483e1 <+6>: mov DWORD PTR [ebp-0x14],0x7b
0x080483e8 <+13>:lea eax,[ebp-0x14]
0x080483eb <+16>:add eax,0x4
0x080483ee <+19>:mov DWORD PTR [eax],0x64636261
0x080483f4 <+25>: mov WORD PTR [eax+0x4],0x65
0x080483fa <+31>: mov eax,0x0
0x080483ff <+36>: leave
0x08048400 <+37>:ret
End of assembler dump.
(gdb)
-----------------------------------------------------------
#include <stdio.h>
#include <string.h>
typedef struct _obj
{
int a;
char b[16];
}obj;
int main()
{
obj o;
o.a = 123;
strcpy(o.b, "abcde");
}
<Hand-lay 느낀점>
- return 0 쓰든 안쓰든 마지막은 mov eax, 0x0으로 같다.
- for문의 방식 1) mov 첫번째인자 2) jmp 3) cmp+je 비교로 두번째인자 4) jmp 후 for문 내용 5) 세번째 인자
- if문같은 jmp에서 코드에 먼저 쓰여진 것부터 어셈블리도 쓰여짐. 따라서 점프가 위로하고 아래로 하고 항상 다름.
- 함수 call 이전에 인자 전달 방식 1) lea eax, [ebp-0x4] 2) push offset으로 string 전달
- 포인터랑 일반 변수 선언 똑같이 생김. 뒤에 주소 담는 것보고 분별
- 포인터 가리키는 법 1) lea로 주소 담고, 2) mov PTR에 담음
- 포인터 덧셈에서 '포인터'를 암시하기 위해 두 번 거치는 것이다. 만약 일반 변수였다면 한 번만 했겠지(나만이해ㅋ)
- 구조체는 일반 변수처럼 선언하는 와중에 변수 주소+0x4를 이용한다 -> 구조체
- 구조체 변수들의 사이즈 아는 방법은 맨 처음 사용된 일반 변수에서 사용한 사이즈만큼 뺌
- 구조체 char 배열에 string 저장은 할당연산자로 안되므로 'strcpy' 함수를 사용함 (이때 gdb에도 안나타남)
BY 센빠이
'sec > reversing' 카테고리의 다른 글
OEP (0) | 2021.03.06 |
---|---|
디버거 이론, Ollydbg, ELF, gdb (0) | 2021.03.05 |
디버거, IDA (0) | 2021.03.05 |
Comments