'll Hacker
Binary 개념 정리 본문
프로그램과 컴파일
프로그램 = 연산 장치가 수행해야하는 동작을 정의한 일종의 문서
프로그램을 연산 장치에 전달하면, CPU는 적혀있는 명령들을 처리하여 프로그래머가 의도한 동작을 수행
(컴퓨터의 대부분이 Stored-Program Computer의 형태로 개발)
= 바이너리
컴파일러와 인터프리터
CPU가 수행해야 할 명령들을 프로그래밍 언어(고급언어)로 작성 = 소스 코드
컴퓨터가 이해할 수 있는 기계어(저급언어)의 형식으로 번역 = 컴파일
컴파일을 해주는 소프트웨어 = 컴파일러 ex) GCC, Clang, MSVC 등
이외 파이썬이나 자바스크립트는 컴파일이 필요없음.
왜냐면 사용자의 입력 또는 사용자가 작성한 스크립트를 그때 그때 번역해서 CPU에 전달하기 때문에(이를 인터프린팅이라고 함, 똑같이 처리해주는 프로그램을 인터프린터라고 함)
컴파일 과정
in C Programming (Preprocess -> Compile -> Assemble -> Link -> Binary)
예제
// Name: add.c
#include "add.h"
#define HI 3
int add(int a, int b) { return a + b + HI; } // return a+b
1. Preprocess
- 주석 제거
- 매크로 치환
- 파일 병합
$ gcc -E add.c > add.i
$ cat add.i
# 1 "add.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "add.c"
# 1 "add.h" 1
int add(int a, int b);
# 2 "add.c" 2
int add(int a, int b) { return a + b + 3; }
cat 결과, 먼저 소스코드의 주석이었던 //return a+b 사라짐(주석제거)
HI가 3으로 치환(매크로 치환), add.h의 내용이 #include 에 의해 병합(파일 병합)
2. Compile
코드에 문법적 오류가 있을땐 에러 출력
컴파일러는 코드를 번역할 때, 몇몇 조건을 만족하면 최적화 기술을 적용하여 효율적인 어셈블리 코드를 생성해줌
gcc에서는 -O, -O0, -O1, -O2, -O3, -Os, -Ofast, -Og 등의 옵션을 사용하여 최적화 적용가능
// Name: opt.c
// Compile: gcc -o opt opt.c -O2
#include <stdio.h>
int main() {
int x = 0;
for (int i = 0; i < 100; i++) x += i; // x에 0부터 99까지의 값 더하기
printf("%d", x);
}
0x0000000000000560 <+0>: lea rsi,[rip+0x1bd] ; 0x724
0x0000000000000567 <+7>: sub rsp,0x8
0x000000000000056b <+11>: mov edx,0x1356 ; hex((0+99)*50) = '0x1356' = sum(0,1,...,99)
0x0000000000000570 <+16>: mov edi,0x1
0x0000000000000575 <+21>: xor eax,eax
0x0000000000000577 <+23>: call 0x540 <__printf_chk@plt>
0x000000000000057c <+28>: xor eax,eax
0x000000000000057e <+30>: add rsp,0x8
0x0000000000000582 <+34>: ret
아래와 같이 -S옵션을 이용하면 소스 코드를 어셈블리 코드로 컴파일 ㄱㄴ
$ gcc -S add.i -o add.S
$ cat add.S
.file "add.c"
.intel_syntax noprefix
.text
.globl add
.type add, @function
add:
.LFB0:
.cfi_startproc
push rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
mov rbp, rsp
.cfi_def_cfa_register 6
mov DWORD PTR -4[rbp], edi
mov DWORD PTR -8[rbp], esi
mov edx, DWORD PTR -4[rbp]
mov eax, DWORD PTR -8[rbp]
add eax, edx
add eax, 3
pop rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size add, .-add
.ident "GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0"
.section .note.GNU-stack,"",@progbits
3. Assemble
컴파일로 생성된 어셈블리어 코드를 ELF 형식의 목적파일로 변환하는 과정
💡ELF 형식? 실행파일, 목적파일, 공유 라이브러리 그리고 코어 덤프를 위한 표준 파일 형식 리눅스의 실행 파일 형식 💡 PE 형식? 윈도우의 실행 파일 형식 |
$ gcc -c add.S -o add.o
$ file add.o
add.o: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
$ hexdump -C add.o
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 |.ELF............|
00000010 01 00 3e 00 01 00 00 00 00 00 00 00 00 00 00 00 |..>.............|
00000020 00 00 00 00 00 00 00 00 10 02 00 00 00 00 00 00 |................|
00000030 00 00 00 00 40 00 00 00 00 00 40 00 0b 00 0a 00 |....@.....@.....|
00000040 55 48 89 e5 89 7d fc 89 75 f8 8b 55 fc 8b 45 f8 |UH...}..u..U..E.|
00000050 01 d0 5d c3 00 47 43 43 3a 20 28 55 62 75 6e 74 |..]..GCC: (Ubunt|
00000060 75 20 37 2e 35 2e 30 2d 33 75 62 75 6e 74 75 31 |u 7.5.0-3ubuntu1|
00000070 7e 31 38 2e 30 34 29 20 37 2e 35 2e 30 00 00 00 |~18.04) 7.5.0...|
00000080 14 00 00 00 00 00 00 00 01 7a 52 00 01 78 10 01 |.........zR..x..|
00000090 1b 0c 07 08 90 01 00 00 1c 00 00 00 1c 00 00 00 |................|
000000a0 00 00 00 00 14 00 00 00 00 41 0e 10 86 02 43 0d |.........A....C.|
000000b0 06 4f 0c 07 08 00 00 00 00 00 00 00 00 00 00 00 |.O..............|
...
4. Link
여러 목적 파일들을 연결하여 실행 가능한 바이너리로 만드는 과정
// Name: hello-world.c
// Compile: gcc -o hello-world hello-world.c
#include <stdio.h>
int main() { printf("Hello, world!"); }
printf함수를 호출하지만, hello-world.c에 없으며, libc라는 공유 라이브러리에 존재.
libc는 gcc의 기본 라이브러리 경로에 있는데, 링커는 바이너리가 printf를 호출하면 libc의 함수가 실행될 수 있도록 연결해줌
$ gcc add.o -o add -Xlinker --unresolved-symbols=ignore-in-object-files
$ file add
add: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, ...
디스어셈블과 디컴파일
디스어셈블(어셈블의 역과정)
$ objdump -d ./add -M intel (디스어셈블된 결과 확인)
...
000000000000061a <add>:
61a: 55 push rbp
61b: 48 89 e5 mov rbp,rsp
61e: 89 7d fc mov DWORD PTR [rbp-0x4],edi
621: 89 75 f8 mov DWORD PTR [rbp-0x8],esi
624: 8b 55 fc mov edx,DWORD PTR [rbp-0x4]
627: 8b 45 f8 mov eax,DWORD PTR [rbp-0x8]
62a: 01 d0 add eax,edx
62c: 5d pop rbp
62d: c3 ret
62e: 66 90 xchg ax,ax
...
디컴파일 (고급언어로 바이너리를 번역)
-> IDA Freeware 사용
'Hacking > Reverse Engineering' 카테고리의 다른 글
Computer Architecture(컴퓨터 구조) (0) | 2024.04.29 |
---|---|
정적분석과 동적분석 (0) | 2024.04.11 |
Reverse Engineering 큰 개념 정리 (0) | 2024.04.11 |
리버스 엔지니어링 정의 및 개요 (0) | 2023.11.16 |
리버싱_전제체션 2주차 과제 (0) | 2023.11.16 |