리버싱 Write-up 1

챠챠비둘기·2023년 3월 16일
0

리버싱😎☠️

목록 보기
1/7

리버싱이란?

리버스 엔지니어링(reverse engineering)을 합친 말로서, 완성된 제품(또는 코드)을 해체하고 분석하여 구조와 기능, 디자인을 파악하는 기술을 뜻한다. 현재, 리버싱은 각종 악성코드나 불법 프로그램의 분석 및 대응을 위해서 자주 사용되는 효과적인 기술이다.

어셈블리어

리버싱을 배우기 위해서는 반드시...! 어셈블리어를 익혀야 한다. 저급 언어라 어렵겠지만, 차근차근 시작해보자.

프로그램: 컴퓨터가 실행해야 할 명령어의 집합을 뜻함. 바이너리라고도 불리기도 함.

전처리: 소스 코드가 컴파일에 필요한 형식으로 가공되는 과정

컴파일: 소스 코드를 어셈블리어로 번역하는 과정

어셈블: 어셈블리 코드를 기계어로 번역하고, 실행 가능한 형식으로 변형하는 과정

링크: 여러 개의 목적 파일을 하나로 묶고, 필요한 라이브러리와 연결해주는 과정

디스어셈블: 바이너리를 어셈블리어로 번역하는 과정

디컴파일: 바이너리를 고급 언어로 번역하는 과정

어셈블리어에 대해서는 컴퓨터 시스템을 공부하면서 따로 정리해 놓도록 하겠다. 일단은 이정도 개념만 잡아두고 가도록 하자.

정적 분석 & 동적 분석

정적 분석

프로그램을 실행시키지 않고, 분석하는 방법이다.
정적 분석을 사용하면 프로그램의 전체 구조를 파악하기 쉽다. 또한, 분석환경의 제약에서 자유로운 데다가, 바이러스와 같은 악성 프로그램의 위협에서 안전하다. 왜냐하면 정적 분석은 프로그램을 실행시키지 않고 분석하기 때문에 악성 프로그램을 실제로 실행할 일이 없기 때문이다. 하지만 정적 분석은 난독화가 적용되면 분석하기 매우 까다로워지는 데다가, 정적 분석만으로 다양한 동적 요소를 고려하기 어렵다. 프로그램은 실행중에 영향을 주고 받는 여러 함수로 구성된다. 만약 하나의 프로그램이 있다고 가정할 때, 프로그램 안에 A라는 함수와 B라는 함수가 있다고 하자. A라는 함수가 B라는 함수에 들어가는 인자를 결정할 수도 있고, A와 B가 서로 같은 전역변수를 공유할 수도 있다. 따라서 어떤 함수가 특정 시점에 정확히 어떤 인자와 어떤 전역 변수를 가지고 실행될지는 정적으로 알기 어렵다. 이런 문제는 프로그램의 실행 흐름이 복잡할수록 더욱 심각해진다.

대표적인 정적 분석 도구: IDA

동적 분석

프로그램을 실행하여 분석하는 방법이다.
동적 분석을 활용하면 코드를 자세히 분석해보지 않고도 프로그램의 개략적인 동작을 파악할 수 있다.
대개의 프로그램은 많은 함수로 구성되어 있으며, 각각의 함수들이 서로 복잡하게 영향을 주고받는다. 따라서 정적 분석만으로 프로그램을 완전히 이해하기는 일반적으로 매우 어렵다. 그러나 동적 분석은 어떤 입력에 대한 개별 함수 또는 프로그램의 출력을 빠르게 확인할 수 있으므로, 이 출력값들을 기반으로 동작을 추론해 볼 수 있다. 쉬운 예로, math라는 복잡한 알고리즘을 수행하는 함수가 있을 때, 정적으로만 분석하여 이 함수를 파악하는 것은 어려울 수 있다. 그러나 동적 분석으로는 어떤 입력 값을 넣고 결과 값이 math 알고리즘의 결과 값과 같은지 비교하여 해당 함수가 math함수인지를 쉽게 판단할 수 있다.
그러나, 동적 분석은 분석 환경을 구축하기 어려울 수 있다. 만약 프로그램을 실행하지 못할 경우, 동적 분석을 진행할 수 없다. 그래서 다른 환경의 프로그램을 동적 분석할 때에는 번거롭게 가상 머신을 구축하거나 프로그램을 실행할 수 있는 장치를 구매해야 한다.(...😅) 또한, 요즘 동적 분석을 방해하는 여러 기법(대표적으로 안티 디버깅이 있다)들이 개발되어 동적 분석을 매우 어렵게 만들고 있다.

대표적인 동적 분석 도구: x64dbg(일종의 디버거다)

컴퓨터 구조

컴퓨터 구조: 컴퓨터가 효율적으로 작동할 수 있도록 하드웨어 및 소프트웨어의 기능을 고안하고, 이들을 구성하는 방법을 의미함
컴퓨터의 기능 구조에 대한 설계, 명령어 집합구조, 마이크로 아키텍처, 그리고 기타 하드웨어 및 컴퓨팅 방법에 대한 설계 등이 포함됨
명령어 집합 구조: CPU의 명령어에 대한 설계, 대표적으로 ARM, MIPS, AVR, 인텔의 x86 및 x86-64 등이 있음
마이크로 아키텍처(Micro Architecture): CPU의 하드웨어적 설계, 정의된 명령어 집합을 효율적으로 처리할 수 있도록, CPU의 회로를 설계하는 분야

폰노이만 구조

-> 중앙 처리 장치(CPU)
프로그램의 연산을 처리하고 시스템을 제어하는 컴퓨터의 두뇌, 프로세스의 코드를 불러오고, 실행하고, 결과를 저장하는 일련의 모든 과정이 CPU에서 일어남. CPU는 산술/논리 연산을 처리하는 산술논리장치(Arithmetic Logic Unit, ALU)와 CPU를 제어하는 제어장치(Control Unit), CPU에 필요한 데이터를 저장하는 레지스터(Register) 등으로 구성됨.
-> 기억 장치
컴퓨터가 동작하는데 필요한 여러 데이터를 저장하기 위해 사용되며, 용도에 따라 주기억장치와 보조기억장치로 분류됨. 주기억장치는 프로그램 실행과정에서 필요한 데이터들을 임시로 저장하기 위해 사용되며, 대표적으로 램(Random-Access Memory, RAM)이 있음. 이와 반대로 보조기억장치는 운영 체제(OS), 프로그램 등과 같은 데이터를 장기간 보관하고자 할 때 사용됨. 대표적으로 하드 드라이브(Hard Disk Drive, HDD), SSD(Solid State Drive)가 있습니다.
-> 버스
컴퓨터 부품과 부품 사이 또는 컴퓨터와 컴퓨터 사이에 신호를 전송하는 통로
대표적으로 데이터가 이동하는 데이터 버스(Data Bus), 주소를 지정하는 주소 버스(Address Bus), 읽기/쓰기를 제어하는 제어 버스(Control Bus)가 있음. 이 외에도 랜선이나 데이터 전송을 목적으로 하는 소프트웨어, 프로토콜 등도 버스라고 함.

명령어 집합 구조

CPU가 해석하는 명령어의 집합을 의미함. 프로그램의 코드는 기계어로 작성되어 있는데, 프로그램을 실행하면 이 명령어들을 CPU가 읽고, 처리함. 명령어 집합 구조 중에서 인텔의 x86-64가 제일 많은 점유율을 차지함.

x86-64 아키텍처

레지스터

CPU 내부의 저장장치로, CPU가 빠르게 접근하여 사용할 수 있다.
산술 연산에 필요한 데이터를 저장하거나 주소를 저장하고 참조하는 등 다양한 용도로 사용된다.
x64 아키텍처에는 범용 레지스터(General Register), 세그먼트 레지스터(Segment Register), 명령어 포인터 레지스터(Instruction Pointer Register, IP), 그리고 플래그 레지스터(Flag Register)가 존재한다.

-> 범용 레지스터
주용도는 있으나, 그 외 임의의 용도로도 사용될 수 있는 레지스터. x86-64에서 각각의 범용 레지스터는 8바이트를 저장할 수 있으며, 부호 없는 정수를 기준으로 2^64 - 1까지 나타낼 수 있다.

-> 세그먼트 레지스터
거에는 메모리 세그먼테이션이나, 가용 메모리 공간의 확장을 위해 사용했으나, 현재는 주로 메모리 보호를 위해 사용되는 레지스터이다. x64에는 cs, ss, ds, es, fs, gs가 있다.

->명령어 포인터 레지스터
프로그램의 코드는 기계어로 작성되어 있다. 명령어 포인터 레지스터는 이 중에서 CPU가 어느 부분의 코드를 실행할지 가리키는 역할을 한다. 명령어 포인터 레지스터 x64 아키텍처의 명령어 레지스터는 rip이며, 크기는 16바이트이다.

->플래그 레지스터
프로세서의 현재 상태를 저장하고 있는 레지스터이다. 자신을 구성하는 여러 비트들로 CPU의 현재 상태를 표현하는 역할을 한다.

ex> a=3, b=5일 때, a-b의 값은 음수가 나온다. 이 경우, 플래그 레지스터에서 SF가 설정된다. 그러면 CPU는 SF가 설정된 것을 통해 a가 b보다 작다는 것을 알 수 있다.

레지스터 호환


x86-64 아키텍처는 IA-32의 64비트 확장 아키텍처이며, 호환이 가능하다. IA-32에서 CPU의 레지스터들은 32비트 크기를 가지며, 이들의 명칭은 각각 eax, ebx, ecx, edx, esi, edi, esp, ebp이다. 호환성을 위해 이 레지스터들은 x86-64에서도 그대로 사용이 가능하다.
rax, rbx, rcx, rdx, rsi, rdi, rsp, rbp가 위에서 말한 레지스터들의 확장된 형태이며, eax, ebx 등은 확장된 레지스터의 하위 32비트를 가르킨다. 예를 들어, eax는 rax의 하위 32비트를 의미한다.
또한 마찬가지로 과거 16비트 아키텍처인 IA-16과의 호환을 위해 ax, bx, cx, dx, si, di, sp, bp는 eax, ebx, ecx, edx, esi, edi, esi, esp ,ebp의 하위 16비트를 가르킨다.
내용이 많아서 그냥 알고만 넘어가도록 하자.

설명만으로는 헷갈려서 예시 사진을 같이 첨부한다. 이건 차차 익혀나가도록 하자.

profile
개발 + 보안

0개의 댓글