마지막으로 FAT32 FileSystem을 다룬것이 작년 10월 경이니, 어느새 1년 가까운 시간이 지나버렸습니다. 지난 1년간 블로그를 그냥 두려고 했던 것은 아니나, 본의아니게(?) 실컷 놀아버린 꼴이 되어버렸습니다.

정신을 차리고보니 이도 저도 아닌 제 모습이 보여서.. 뭐라도 해야 할 것 같았습니다. 그래서 매번 공부해야지 해야지 하고 못했던.. 리버스 엔지니어링을 시작해보고자 합니다. 

Reverse Engineering, 주로 리버싱이라고 불리는 이 역공학 방식은 소프트웨어 공학의 한 분야로 '이미 만들어진 시스템을 역으로 추적하여 처음의 문서나 설계자료를 얻어내는 일'을 말합니다.[1] 역공학이라고 불리우는 이유는, 보통 소프트웨어 생명주기의 마지막 단계에서 초기의 자료등을 얻어내기 때문에 기존 소프트웨어 공학의 역방향으로 흐르기에 역공학이라고 지칭되고 있습니다.

리버싱은 단순히 소프트웨어 공학에만 쓰이는 것이 아니라, 보안쪽에서도 많이 사용되고 있습니다. 주로 백신사나 침해사고 대응 업무에서 많이 사용되고 있는데 침해 PC나 Web 상에서 수집 된 악성코드를 역공학을 이용해 분석 후 탐지될 수 있는 Singnature를 추출하거나 특정 Software를 분석하여 취약점을 찾는데 사용되고 있습니다.


리버스 엔지니어링을 할 줄 안다는 것은 그만큼 여러 분야에 도움이 될 수 있다는 장점이 될 수 있지만 반면 수요가 그리 많지 않다는 단점도 존재합니다. Anti-Virus 생산 업체나 일부 특수한 조건을 제외하고는 해당 분야에 필요한 인재를 뽑지 않거나, 뽑더라도 적은 수를 뽑기 때문입니다. 

필자도 악성코드 분석가가 되고 픈 마음은 굴뚝같지만, 리버싱을 잘 하지 못하기 때문에 지원하지 못하고 있습니다.(이 블로그는 제가 잘 알아서 쓰는게 아니라, 제가 공부한 것을 정리하는 개념으로 작성하는 부분입니다)

따라서 여러분께 이 블로그를 통해 악성코드 분석의 비법을 전달해 드린다기 보다는 '필자는 이렇게 공부했다' 라는 내용을 보여드리는 쪽으로 진행 될 것 같습니다. File System 때와 마찬가지로 부족한 부분이 많겠지만 보시고 많은 질책과 격려 부탁드립니다.

시간이 지나면 여러 악성코드도 분석하고 할 기회가 있겠지만, 이 [Reverse Engineering] 의 시작은 Lena's Tutorial로 시작 할 예정입니다.

그럼 많은 관심 부탁드립니다. 아! 그리고 본격적인 설명 및 분석에는 File System 때와 마찬가지의 어법이 쓰일 예정입니다. 지금처럼 완전 존대가 되지 않으니 참고 하시기 바랍니다.


[참고]

[1] https://terms.naver.com/entry.nhn?docId=1180896&cid=40942&categoryId=32837

Posted by Latte_
,

지난번에 말씀드린 대로 오늘은 범용 레지스터(General Purpose Register)에 대해 포스팅 할 예정입니다.

범용 레지스터는 말 그대로 범용적으로 사용되는 '막 쓰는 레지스터' 라고 생각하시면 편합니다. IA-32에서 범용 레지스터의 크기는 32비트(4바이트)로 보통 상수/주소 등을 저장할 때 사용되기도 하고 특정 어셈블리 명령어에서는 특정 레지스터를 조작하는데 사용되기도 합니다.

아래 그림을 보면 범용 레지스터들을 표현한 것을 볼 수 있습니다.


그림 1.1 범용 레지스터

31

16

15

8

7

0

16-bit

32-bit

 

AH

AL

AX

EAX

 

BH

BL

BX

EBX

 

CH

CL

CX

ECX

 

DH

DL

DX

EDX

 

BP

 

EBP

 

SI

 

ESI

 

DI

 

EDI

 

SP

 

ESP

그림 1.1에서 볼 수 있듯이 각 레지스터들은 16비트 하위호환을 위해 몇개의 구역으롤 나뉘어 있습니다. 32bit EAX를 기준으로 간단히 설명하겠습니다.

EAX : (0~31) 32bit

AX : (0~15) EAX이 하위 16bit

AH: (8~15) AX의 상위 8bit

AL: (0~7) AX의 하위 8bit

위에서 볼 수 있는 것처럼 하나의 32비트 레지스터는 상황에 맞게 8비트, 16비트, 32비트로 알뜰살뜰 사용 할 수 있으니 구획 개념을 확실히 잡고가면 앞으로 디버깅 할 어셈블리 코드를 보다 쉽게 볼 수 있습니다.


각 레지스터의 이름은 아래와 같습니다.

EAX(Extended Accumulator Register) : Accumulator for operands and results data (연산과 결과 Data를 위한 누산기)

EBX(Extended Base Register) : Pointer to data in the DS segment (DS 세그먼트의 데이터 주소)

ECX(Extended Counter Register) : Counter for string and loop operations (문자열과 반복 명령을 카운트하기 위한 레지스터)

EDX(Extended Data Register) : I/O pointer (보통은 EAX와 함께 쓰이며 부호 확장 명령등에 사용)


보통 위의 4개의 레지스터는 산술 연산(ADD, SUB, XOR, OR등)명령어에서 상수/변수 값의 저장 용도로 많이 사용된다. 특정 명령어는 특정 레지스터를 직접 조작하기도 하니, 알아두면 좋다.(여기서는 언급하지 않도록 하겠다.)

그리고 ECX와 EAX는 조금 특수한 용도로도 사용되는데, ECX는 반복 명령어(LOOP)가 나오면 반복 카운트로 사용된다.(한번 반복될 때 마다 ECX 값이 1 감소한다.) 

EAX는 함수 리턴 값에 사용되는데 모든 Win32 API 함수는 리턴값을 EAX에 저장한 후 리턴 한다.


나머지 범용 레지스터들은 아래와 같다.

EBP(Extended Basse Pointer) : Pointer to data on the stack(in the SS segment) (스텍 프레임의 시작 지점 주소 - 스택의 처음을 가르킴)

ESI(Extended Source Index) : Source pointer for string operations (문자열 명령어 조작을 위한 Source Data의 주소가 저장)

EDI(Extended Destination Index) : Destination pointer for string operation (문자열 명령어 조작을 위한 목적지 주소가 저장)

ESP(Extended Stack Pointer) : Stack pointer(in the SS segment) (스텍 프레임의 끝 주소가 저장)


앞에서 설명한 4개의 범용 레지스터가 주로 산술연산에 사용 된다면, 방금 언급한 4개의 범용 레지스터는 주로 메모리 주소를 저장하는 포인터로 사용된다.

먼저 ESP는 스텍 메모리 주소를 가리키는데 특정 명령어들(PUSH, POP, CALL, RET)은 ESP를 직접 조작하기도 한다. 한가지 주의할 점이 있다. 스택 메모리 관리는 프로그램에서 굉장히 중요한 역할을 하기 때문에 ESP를 다른 용도로 사용해서는 안될 것이다.

EBP는 함수가 호출될 때 그 순간의 ESP를 저장하고 있다가 함수가 리턴하기 직전에 다시 ESP값을 돌려줌으로써 스택이 깨지지 않도록 돕는 역할을 합니다. 이를 스텍프레임(Stack Frame)이라고 합니다.

ESI와 EDI는 특정 명령어들과 함께 메모리 복사에 사용되므로, 해당 명령어들과 함께 공부하면 도움이 될 것입니다.


범용 레지스터는 '막 사용하는' 레지스터 라고 말씀드리긴 했습니다만.. 그렇다고 무턱대고 '막' 쓰면... 안되는거 아시지요 ? ㅎㅎㅎ 특정 레지스터는 그 레지스터만의 역할이 따로 있기에.. 잘 확인 하고 사용해 주셔야 합니다.


다음 포스팅에는 세그먼트 레지스터(Segment Register)에 대해서 알아보도록 하겠습니다.

Posted by Latte_
,

레지스터에 대해서는 두 세번정도로 나누어 포스팅을 할 예정입니다.

레지스터 자체가 굉장히 기본이 되는 부분이기도 하고, 스택등과 더불어 툴을 쓸 때 가장 많이 볼 부분이 그 부분이기 때문입니다.

이번 포스팅에서는 레지스터가 무엇인지 알아보는 시간을 갖도록 하겠습니다.


레지스터(Register)란?

 레지스터는 CPU에 존재하는 다목적 저장 공간 입니다. 우리가 흔히 메모리라고 이야기하는 RAM(Random Access Memory,이하 램)와는 조금 다른 성격을 가집니다.

레지스터와 램은 데이터와 명령어를 저장하는 점에서는 동일한 성격을 지닙니다. 다만 램은 CPU와 한몸이 아니라 하나 또는 그 이상의 마이크로 칩으로 이루어져 있으며, CPU와 물리적으로 가까운곳에 위치합니다.

그래서 램에 있는 데이터에 접근(Access)하기 위해서는 물리적으로 먼 길을 돌아가기 때문에 시간이 오래 걸리게 되는데 반해 레지스터는 CPU와 함께 붙어있기 때문에 고속으로 데이터를 처리 할 수 있습니다.


레지스터는 왜 알아야 하는가?

 지금 이 글을 포스팅하는 저를 포함한 많은 리버스 엔지니어링을 공부하는 사람들은 초급단계로 디스어셈블을 통한 어셈블리 명령어를 해석하게 됩니다.

그런데 이 어셈블리 명령어라는 녀석들은.. 대부분 레지스터를 조작하고, 내용을 검사하는 일을 합니다. 즉, 레지스터에 대해 알지 못하면 리버싱을 할 수 없다는 소리가 됩니다. 물론 볼 때 마다 하나하나 찾아가며 하는 방법도 있겠습니다만.. 효율적인 면이나 능률적인 면에서 보면 공부해서 아는 것이 육체적으로나 정신적으로나 큰 도움이 될거라고 생각 됩니다.


IA-32(Intel Architecture 32bit)는 수많은 기능을 제공하며, 또 그만큼 많은 수의 레지스터를 보유하고 있습니다. 앞으로 이어질 세개의 포스팅에서는 오늘 간단하게 언급하고 넘어갈 레지스터인 Basic program execution register에 대해 이야기 할 예정입니다.

물론 이외에도 Memory Management Register, Machine specific register, Control register 등이 존재하지만.. 이는 향후에 좀더 깊게 설명할 때 다시 언급하기로 한다.


다음 포스팅에 설명할 Basic program execution register의 4개 그룹을 간단하게 언급하고 다음 포스팅에서 뵙도록 하겠습니다.

 - General Purpose Registers(범용 레지스터)

 - Segment Register(세그먼트 레지스터)

 - Program Status and Control Register(프로그램 상태 및 컨트롤 레지스터)

 - Instruction Pointer(명령어의 주소)


다음 포스팅에서는 범용 레지스터에 대해서 포스팅 할 계획입니다.

Posted by Latte_
,

바이트 오더링(Byte Ordering)은 컴퓨터에서 메모레이 데이터를 저장하는 방식을 의미한다.

바이트 오더링은 크게 두종류로 나뉘는데 리틀 엔디언(Little Endian), 빅 엔디언(Big Endian)으로 구분 할 수 있다.


간단하게 쉽게 설명부터 하자면, 빅 엔디언 방식은 데이터를 입력한 순서대로 저장합니다. 주로 대형 UNIX 서버에서 사용되는 RISC(*1)(Reduced Instruction Set Computer) CPU 또는 네트워크 프로토콜에 사용 합니다.

빅 엔디언의 장점은 데이터를 순서대로 저장하기 때문에 직관적이라는 점입니다. 뒤에서 다시 설명하겠지만 빅 엔디언과 리틀 엔디언의 가장 큰 차이점은 일부 자료형에 따른 저장방식이 다르다는 점입니다.

빅 엔디언의 경우 전 자료형에 대해 입력한 순서대로 저장하지만, 리틀 엔디언의 경우는 WORD형과 DWORD 자료형에 대해서는 입력한 순서와 반대로 저장합니다.

아래의 예를 들어 빅 엔디언을 설명하겠습니다.

- 코드(1.1)

BYTE b = 0x12;

WORD w  = 0x1234;

DWORD dw = 0x12345678;

char str[] = "abcde"; 

- 빅 엔디언 표기(표 1.1)

 Type

Name

Size 

빅 엔디언 표기

BYTE

b

1

 [12]

WORD

w

2

[12][34] 

DWORD

dw

4

[12][34][56][78]

char[]

str

6

[61][62][63][64][65][00]

표 1.1에서 보는것과 같이 빅 엔디언은 값을 입력한 대로 저장한다. 마지막 char형의 경우 마지막에는 NULL값이 들어가기 때문에 [00]이 포함된다. 

그럼 동일한 값을 리틀 엔디언 표기법으로 저장하면 어떻게 될까?


-리틀 엔디언 표기(표 1.2)

 Type

Name

Size 

리틀 엔디언 표기 

BYTE

[12]

WORD

[34][12] 

DWORD 

dw 

[78][56][34][12]

char[] 

str 

[61][62][63][64][65][00] 

표 1.2에서 보는것과 같이 리틀 엔디언은 WORD형과 DWORD형에서는 입력된 순서와 반대로 값을 저장한다.


리틀 엔디언 방식의 경우 바이트 자체는 입력한 순서대로 저장이 되지만, WORD나 DWORD처럼 멀티 바이트(Multi-BYTE)의 경우에는 각 바이트가 역순으로 저장이 된다. 빅 엔디언이 직관적인 면에서 더 우월하기 때문에 소프트웨어 디버그가 편한 반면, 리틀 엔디언 방식은 산술 연산 및 데이터 타입의 확장/축소 시 더 편한 장점을 가지고 있다.

표 1.2를 보면, 문자열은 빅 엔디언과 동일한 방식으로 저장이 됨을 볼 수 있는데 이는 문자열은 결국 char 배열이기 때문에 각 바이트를 하나씩 연속해서 저장하기 때문에, 리틀 엔디언도 문자열 자체는 빅 엔디언과 동일하게 저장한다고 보면 된다.


*1) RISC : Reduced Instruction Set Computer의 약자로 말 그대로 풀어쓰면 '명령어 세트 컴퓨터' 라고 한다. 기존 CICS(*2)에 존재하는 수많은 명령어와 주소모드 중 실제 사용되는 명령어는 몇개 되지 않는다는 사실을 바탕으로 적은 수의 명령어만으로 명령어 집합을 구성한 방식이다. 

*2) CICS : Complex Instruction Set Computer의 약자로 복잡한 명령어 집합을 갖는 CPU 아키텍처. 주로 메인프레임이나 X86 호환 프로세에서 이 방식을 사용하고 있다.

Posted by Latte_
,

공부를 하면서 블로그를 하겠다!! 

라는 당찬 다짐을 하며 블로그를 시작했건만..

꼴랑 글을 하난가 두갠가를 쓰고서는 더이상 블로그를 잡지 않았네요..

작심 삼일도 아닌 것이..

그간 제대로 공부를 못한것도 있고.. 제 스스로도 조금 나태해진 모양입니다..

이제는 마음을 다 잡고 해야겠어요!!


잡소리는 여기까지.. 근데 왜 하필이면 리버싱 정복기 일까..

과연 리버싱을 완전정복 할 수 있을까..

지금 이 글을 쓰면서도 하는 고민입니다만..

결국엔 완정정복이란건 없다 하더라도..

누구에게.. 리버싱을 모르는 사람에게.. 비 전공자에게..

리버싱에 대해 설명했을 때 그사람이 아.. 그렇구나 라고 이해할 정도로 설명 할 수 있게 되면..

그때가 되면 이 공부를 한게.. 정말 잘 했다고 말할 수 있지 않을까 싶네요.


그날을 위해서.. 지금부터 시작합니다. 리버싱을 정복하러!!

Posted by Latte_
,