CIDY
[Reverse_Engineering] stage2_Binary&Analysis 본문
*프로그램 == 바이너리
연산 장치가 수행해야 하는 동작을 정의한 문서이다. 연산 장치에 프로그램 전달 -> CPU가 적혀있는 명령어들을 처리해 프로그래머가 의도한 동작을 수행함
사용자가 정의한 프로그램을 해석해 명령어를 처리할 수 있는 연산 장치를 programmable하다고 함. (컴퓨터 등)
프로그램이 저장 장치에서 이진 형태로 저장되기에 바이너리(Binary)라고도 한다.
*컴파일러&인터프리터
CPU가 수행할 명령들을 프로그래밍 언어로 작성한 것을 소스코드라고 한다.
소스코드 -> (컴파일 by.compiler) -> 기계어 // 이렇게 컴퓨터가 이해할 수 있도록 기계어로 번역하는 소프트웨어를 컴파일러라고 한다. (gcc, clang, MSVC등)
컴파일러로 한 번 컴파일해두면 프로그램으로 계속 쓸 수 있는데, python이나 javascript같은 언어는 컴파일이 필요없다. 작성된 스크립트를 그때그때 번역해 cpu에 전달하기 때문이다. -> 이 번역 과정은 컴파일이 아닌 인터프리팅(interpreting)이라고 함. (by.interpreter)
컴파일러의 경우 한 번 컴파일해두면 프로그램으로 계속 쓸 수 있지만 한 번 번역하는데 시간이 많이 필요한 반면, 인터프리터의 경우 각 코드를 빠르게 번역할 수 있지만 실행할 때 마다 번역해줘야 한다는 단점이 있다.
*컴파일 과정
C언어로 작성된 코드는 전처리 -> 컴파일 -> 어셈블 -> 링크 과정을 거쳐 바이너리로 번역된다.
(컴파일의 의미는 어떠한 언어로 작성된 것을 다른 언어로 번역하는 것이기 때문에 번역의 각 과정을 모두 컴파일이라고 볼 수 있다. 물론 전체 과정도 컴파일이다.)
// Name: add.c
#include "add.h"
#define HI 3
int add(int a, int b) { return a + b + HI; } // return a+b
// Name: add.h
int add(int a, int b);
-전처리(.c -> .i): 컴파일 전에 필요한 형식으로 가공하는 과정이다. 주석 제거 -> 매크로 치환(#define) -> 파일 병합(여러개의 소스와 헤더파일들을 병합 후 컴파일, 혹은 따로 컴파일해 병합하기도 한다.)
# 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; }
위의 C코드를 전처리하면 이러한 결과가 나온다.
(gcc -E add.c > add.i) -> gcc에서 -E옵션을 사용해 만들어진 파일을 cat하면 전처리 결과를 확인할 수 있다.
우선 주석은 다 사라졌고(주석 제거), HI가 3으로 치환되었다.(매크로 치환) 그리고 add.h헤더파일의 내용이 #include에 의해 병합되었다.(파일 병함)
-컴파일(.i -> .S): C로 작성된 소스코드를 어셈블리어로 번역하는 과정이다. 여기서 소스코드의 문법 검사가 이루어진다. (문법에 문제있으면 컴파일 중단 + 에러 출력)
gcc에서는 -0 -00 -01 -02 -03 -0s -0fast -0g등 옵션을 사용해 최적화를 적용해서 효율적인 어셈블리 코드를 생성할 수 있다.
// 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);
}
위와 같은 코드를 -02옵션으로 최적화하여 컴파일하면 -> 컴파일러는 반복문을 어셈블리어로 옮기는 것이 아니고, 반복문의 결과로 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
처음 예시로 들었던 C코드 -> i파일 해줬던걸 gcc -S add.i -o 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
-어셈블(.S -> .o): 컴파일로 생성된 어셈블리 코드를 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..............|
...
이건 아까 그 .S파일을 -c 옵션으로 오브젝트 파일로 변환한 것을 16진수로 출력한 것이다.
-링크: 여러 오브젝트 파일들을 연결해 실행 가능한 바이너리로 만드는 과정이다.
// Name: hello-world.c
// Compile: gcc -o hello-world hello-world.c
#include <stdio.h>
int main() { printf("Hello, world!"); }
위 코드에서 printf함수를 호출하는데, 이 정의는 립시에 있다. -> 립시는 gcc의 기본 라이브러리 경로에 있는데, 링커는 바이너리가 printf를 호출하면 립시의 함수가 실행될 수 있도록 연결해준다.
링크를 거치면 실행 가능한 상태의 프로그램이 되는 것이다.
gcc add.o -o add -Xlinker --unresolved-symbols=ignore-in-object-files
이렇게 add.o를 링크할 수 있다.
링크 과정에서 링커는 main함수를 찾는데, add의 소스코드에는 main의 정의가 없기 때문에 에러가 발생할 수 있음 -> --unresolved-symbols 을 컴파일 옵션에 추가해 해결 가능하다.
*디스어셈블&디컴파일
바이너리 자체는 기계어라 이해하기 힘들다 -> 디스어셈블(어셈블 역과정)해서 어셈블리어로 볼 수 있다.
objdump -d ./add -M intel
위는 디스어셈블 명령어이다. gdb로 보는 게 편할 것 같다.
어셈블리어도 기계어보단 낫지만 여전히 저수준 언어라 이해가 힘들다. -> 디컴파일해서 고급 언어로 볼 수 있음.
근데 어셈블리어는 사실 저수준 언어라 기계어랑 거의 1대1 대응돼서 거의 정확하게 디스어셈블 해올 수 있는데 높은 수준의 언어는 이러한 대응관계가 없기에 + 코드 작성 시 사용한 변수/함수명 등은 컴파일 과정에서 모두 사라지고, 코드 일부는 위에서 봤던 최적화와 같은 사유로 컴파일 과정에서 완전 변형되기도 하므로 디스어셈블 과정만큼 깔끔한 복구가 어렵다. -> 완벽한 디컴파일은 불가능
그래도 디스어셈블해서 보는 것보다 분석 효율이 훨씬 좋으므로, 디컴파일러는 쓸 수 있으면 써야함. Hex Rays나 Ghidra같은 좋은 디컴파일러 많음. 물론 IDA 프리웨어도..
*정적분석
프로그램을 실행시키지 않고 분석하는 방법이다. -> 전체 구조를 파악하기 쉬움 + 분석 환경의 제약에서 비교적 자유로움 + 악성 프로그램 위협으로부터 안전함(실행하지 않으니까)
but프로그램에 난독화가 적용되면 분석이 매우 어려워진다.
-> IDA로 까보기
*동적분석
프로그램을 실행시켜가며 분석하는 방법이다. -> 상세한 분석 없이 프로그램 동작 파악 가능 + 입/출력을 직접 보면서 동작 추론 가능
but 분석 환경을 구축하기 어려울 수 있다. (프로그램을 실행할 환경이 갖춰지지 못했다거나, 가상머신 혹은 프로그램 실행 장치가 없다거나) + 그리고 정적분석의 난독화처럼 동적 분석을 방해하는 안티디버깅이 있음(ex: 자신이 디버깅당하는 중인지 검사하고, 디버깅 중이라면 프로그램 종료시키기 등)
if (is_debugging()) // 디버깅인지 확인
exit(-1); // 프로그램 종료
Func();
-> x64dbg 돌려보기
'Hack > DreamHack(로드맵)' 카테고리의 다른 글
[Cryptography] stage2_고전 암호 (0) | 2022.07.20 |
---|---|
[Cryptography] stage1_intro (0) | 2022.07.19 |
[System_Hacking] AD: stage6_문제풀이(Bypass IO_validate_vtable) (0) | 2022.07.15 |
[System_Hacking] AD: stage6_문제풀이(_IO_FILE Arbitrary Address Write) (0) | 2022.07.15 |
[System_Hacking] AD: stage6_문제풀이(_IO_FILE Arbitrary Address Read) (0) | 2022.07.14 |