Reversing/Reversing2017. 6. 27. 03:47

OllyDbg 기본 명령어

 

Restart [Ctrl+F2]: 처음부터 디버깅 다시 시작 (디버깅을 당하는 프로세스를 종료하고 재실행)

Step Into [F7]: 하나의 OP Code 실행(CALL 명령을 만나면, 그 함수 코드 내부로 따라 들어감)

Step Over [F8]: 하나의 OP Code 실행(CALL 명령을 만나면, 따라 들어가지 않고 그냥 함수 자체를 실행함)

Execute till Return [Ctrl+F9]: 함수 코드 내에서 RETN 명령어까지 실행(함수 탈출)

Execute till Cursor [F4]: 커서의 위치까지 실행 (디버깅 하고 싶은 위치까지 바로 갈 수 있음)

 

※ OllyDbg를 켜면 동작중인 디버거가 발견되었습니다. 디버거를 종료한 후 다시 실행하시기 바랍니다라는 경고문구가 뜨는 경우가 있는데, 제어판에서 Delfino를 언인스톨해주면 정상적으로 OllyDbg를 사용할 수 있다.

 

 

OllyDbg 디버거 동작 명령

 

Go to [Ctrl+G]: 원하는 주소로 이동(코드/메모리를 확인할 때 사용, 실행은 되지 않음)

Execute till Cursor [F4]: cursor 위치까지 실행(디버깅하고 싶은 주소까지 바로 갈 수 있음)

Comment [;]: 코멘트 추가

User-defined Comment: 마우스 우측 메뉴 Search for User-defined Label

Set/Reset Breakpoint [F2]: BP설정/해제

Run [F9]: 실행(BreakPoint가 걸려있으면 그 지점에서 실행이 정지)

Show the Current EIP [*]: 현재 EIP의 위치를 보여줌

Show the previous Cursor [-]: 직전 커서의 위치를 다시 보여줌

Preview CALL/JMP Adress [Enter]: 커서가 CALL/JMP등의 명령어에 위치해 있다면, 해당 주소를 따라가서 보여줌(실행되지 않음, 간단히 함수의 내용을 확인할 때 유용)

Edit Data [Ctrl+E]: 데이터 편집

Assemble [Space]: 어셈블리 코드 작성 및 수정


 

 

Assembly 기초 명령어

 

CALL _ADDR_: ADDR 주소의 함수를 호출

JMP _ADDR_: ADDR 주소로 점프

PUSH _VALUE_: 스택에 VALUE 저장

RETN: 스택에 저장된 복귀 주소로 점프

INC _REG_: 해당 레지스터(REG)에 값을 1 증가

DEC _REG_: 해당 레지스터에 값을 1 감소

JMP SHORT _ADDR_: ADDR 주소로 점프(짧은 거리, JMP와 다른점은 추후 서술)

CMP A B: A와 B의 값을 비교

JE _ADDR_: Jump if Equal. 직전 CMP문에서 같으면 ADDR 주소로 점프, 아니면 그대로 진행

 

 

용어

 

VA: Virtual Address, 프로세스의 가상 메모리

OP Code: Operation Code, CPU 명령어. Byte Code라고도 함.

PE: Portable Executable, Windows 실행파일(exe, dll, sys )

 

'Reversing > Reversing' 카테고리의 다른 글

Calling Convention (함수 호출 규약)  (0) 2017.07.02
Abex’ Crackme #2  (0) 2017.06.30
Stack Frame  (0) 2017.06.27
Abex' Crackme #1  (0) 2017.06.27
Hello, World! 디버깅하기  (0) 2017.06.26
Posted by BinZIP
Reversing/Etc2017. 6. 27. 01:27

스택 (Stack)

프로세스에서 스택의 역할은 다음과 같다.

1.     함수 내의 로컬 변수 임시 저장

2.     함수 호출 시 파라미터 전달

3.     복귀 주소 저장

위와 같은 역할을 수행하기에 스택의 FILO(First In Last Out) 구조가 아주 유용하다.

 

스택의 특징

프로세스에서 스택 포인터(ESP)의 초기값은 Stack Bottom, 즉 스택의 가장 아래쪽에 가깝다. PUSH 명령에 의해서 스택에 값이 추가되면 스택 포인터는 Stack Top, 즉 스택의 위쪽으로 움직이고, POP 명령에 의해 스택에 값이 빠지면 스택 포인터는 Stack Bottom을 향해 움직인다. , “스택은 거꾸로 자란다.

 

실제로 스택이 어떻게 동작하는지 이해를 돕기 위해 OllyDbg를 이용해 간단한 예제를 살펴보자.

다음은 stack.exe OllyDbg에 로드한 초기 모습이다.

스택 포인터(ESP)의 값은 18FF8C이다. 스택 창을 보면 ESp가 가리키는 주소와 그 값을 보여준다.

Step Into PUSH 100 명령을 실행시키면 다음과 같은 결과를 볼 수 있다.

ESP의 값이 18FF88로 변해 4바이트만큼 줄어들었다. , 명령을 실행했더니 ESP가 메모리의 상단으로 이동하고 있다는 것을 확인했다. 그리고 ESP가 가리키는 주소에 들어있는 값을 확인해보면 100이 저장되어 있는 것을 알 수 있다. 다시 한 번 Step Into를 실행해본다.

ESP의 값은 4바이트 증가하여 12FF8C, 스택은 초기 상태와 똑같아졌다. , 스택에서 값을 꺼냈더니 ESP는 아래 방향으로 이동했다.

이를 통해, ‘스택에 값을 입력하면 ESP(스택 포인터)는 감소하고, 스택에서 값을 꺼내면 ESP는 증가한다를 확인할 수 있었다.

 

'Reversing > Etc' 카테고리의 다른 글

Data Recording & Register Basic  (0) 2017.06.26
Posted by BinZIP
Reversing/Etc2017. 6. 26. 23:33
Little Endian & Big Endian

바이트 오더링은 데이터를 저장하는 방식을 말한다. 크게 빅 엔디언(Bin Endian)과 리틀 엔디언(Little Endian)으로 나눌 수 있다. 리틀 엔디언은 데이터를 뒤부터 순차적으로 기록하고, 빅 엔디언은 앞에부터 순차적으로 기록한다. 아래의 예를 보자.

BYTE b = 0x12;

WORD w = 0x1234;

DWORD dw = 0x12345678;

char str[] = “abcde”;

 

TYPE

NAME

SIZE

Big Endian

Little Endian

BYTE

b

1

12

12

WORD

w

2

12 34

34 12

DWORD

dw

4

12 34 56 78

78 56 34 12

char []

str

6

61 62 63 64 65 00

65 64 63 62 61 00

위의 표에서 알 수 있듯이, 빅 엔디언은 데이터를 앞부터 순차적으로 저장한다. 그에 반해, 리틀 엔디언은 데이터를 뒤부터 순차적으로 저장한다.

빅 엔디언은 데이터를 순서대로 저장하기 때문에 사람이 보기에 직관적이라는 장점이 있다. 대형 UNIX 서버에 사용되는 RISC 계열의 CPU에서 많이 사용되며, 네트워크 프로토콜에도 사용된다.

리틀 엔디언은 Intel x86 Processor에 사용된다. 데이터를 역순으로 저장시키는 것은 산술 연산과 데이터의 타입이 확장/축소될 때 더 효율적이라는 장점을 가지고 있다.

 

IA-32 Register Basic

레지스터(Register) CPU 내부에 존재하는 다목적 저장 공간이다. CPU 내부에 존재하기 때문에 물리적으로 가까이 위치하고 있어 데이터 처리를 고속으로 할 수 있다.

우리가 레지스터를 알아야 하는 이유는 디버거가 디스어셈블 해준 결과물인 어셈블리어의 대부분이 레지스터를 조작하고 확인하는 명령어들이기 때문이다. 레지스터를 모르면 명령어 자체도 이해하기 힘들다.

 

Intel Architecture 32bit(IA-32) Register

IA-32는 지원하는 기능도 많고 그만큼 레지스터 수도 많다. 많은 레지스터들 중에서 우리가 이번에 알아볼 레지스터는 ‘Basic Program Execution Registers’이다.

Basic Program Execution Registers는 다시 4개의 그룹으로 나누어져 있다.

General Purpose Registers (32bit, 8)

Segment Registers (16bit, 6)

Program Status and Control Register (32bit, 1)

Instruction Pointer (32bit, 1)

 

 

General Purpose Registers (범용 레지스터)

범용 레지스터는 이름처럼 범용적으로 사용되는 레지스터들이다. 각 레지스터의 크기는 32비트이고, 보통은 상수/주소 등을 저장할 때 주로 사용되며, 특정 어셈블리 명령어에서는 특정 레지스터를 조작하기도 한다. 또한, 어떤 레지스터들은 특수한 용도로 사용되기도 한다.

 

위 사진은 범용 레지스터이다. 각 레지스터들은 16비트 하위 호환을 위하여 몇 개의 구획으로 나누어진다. EAX를 기준으로 설명하면 다음과 같다.

 

EAX: 0~31까지 구간의 32비트

AX: 0~15까지 구간의 EAX 하위 16비트

AH: 8~15까지 구간의 AX 상위 8비트

AL: 0~7까지 구간의 AX 하위 8비트

 

, 4바이트를 다 사용하고 싶을 때는 EAX를 사용하고, 2바이트만 사용할 때는 AX를 사용하면 된다. 1바이트만 사용하고 싶을 때는 AH 혹은 AL을 사용한다. 이렇게 상황에 따라 8비트, 16비트, 32비트로 유연하게 사용할 수 있다.

각 레지스터의 이름은 다음과 같다.

 

EAX: Accumulator for operands and results data

EBX: Pointer to data in the DS segment

ECX: Counter for string and loop operations

EDX: I/O pointer

 

위의 레지스터들을 주로 산술연산(ADD, SUB, XOR, OR ) 명령어에서 상수/변수 값의 저장 용도로 많이 사용된다. 어떤 어셈블리 명령어(MUL, DIV, LODS )들은 특정 레지스터를 직접 조작하기도 한다. 이런 명령어가 실행된 이후에 특정 레지스터들의 값이 변경된다.

그리고 추가적으로, ECX EAX는 특수한 용도로 사용된다. ECX는 반복문 명령어(LOOP)에서 반복 카운트(loop count)로 사용된다. 루프를 돌 때마다 ECX의 값을 1씩 감소시키는 형태로 동작한다. EAX는 일반적으로 함수의 리턴 값에 사용된다. 모든 Win32 API 함수들은 리턴 값을 EAX에 저장한 후 리턴한다.

 

참고: Win32 API 함수들은 내부적으로 ECX EDX를 사용하기 때문에 호출된 직후 ECX EDX의 값이 바뀐다. 따라서 ECX EDX에 중요한 값이 저장되어 있다면 API를 호출하기 전에 다른 레지스터나 스택에 백업해두어야 한다.

 

 

Other General Purpose Register

나머지 범용 레지스터들의 이름은 다음과 같다.

EBP: Pointer to data on the stack (in the SS segment)

ESI: source pointer for string operations

EDI: Destination pointer for string operations

ESP: Stack pointer (in the SS segment)

 

4개의 레지스터들은 주로 메모리 주소를 저장하는 포인터로 사용된다.

ESP는 스택 메모리 주소를 가리킨다. 어떤 명령어들(PUSH, POP, CALL, RET) ESP를 직접 조작하기도 한다. 스택 메모리 관리는 프로그램에서 매우 중요하기 때문에 ESP를 다른 용도로 사용하지 말아야 한다.

EBP는 함수가 호출되었을 때 그 순간의 ESP를 저장하고 있다가, 함수가 리턴하기 직전에 다시 ESP에 값을 되돌려줘서 스택이 깨지지 않도록 한다. (이런 방법을 Stack Frame 기법이라고 하며, 리버싱에서 중요한 개념이다.) ESI EDI는 특정 명령어들 (LODS, STOS, REP MOVS )과 함께 주로 메모리 복사에 사용된다.

 

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

세그먼트(Segment) IA-32의 메모리 관리 모델에서 나오는 용어이다.

IA-32 보호 모드에서 세그먼트란 메모리를 조각내어 각 조각마다 시작 주소, 범위, 접근 권한 등을 부여해서 메모리를 보호하는 기법을 말한다. 또한 세그먼트는 페이징(Paging) 기법과 함께 가상 메모리를 실제 물리 메모리로 변경할 때 사용된다. 세그먼트 메모리는 Segment Descriptor Table(SDT)이라고 하는 곳에 기술되어 있는데, 세그먼트 레지스터는 바로 이 SDT index를 가지고 있다.

각 세그먼트 레지스터의 이름은 아래와 같다.

 

CS: Code Segment

SS: Stack Segment

DS: Data Segment

ES: Extra(Data) Segment

FS: Data Segment

GS: Data Segment

 

이름 그대로 CS는 프로그램의 코드 세그먼트를 나타내며, SS는 스택 세그먼트, DS는 데이터 세그먼트를 나타낸다. ES, FS, GS 세그먼트는 추가적인 데이터 세그먼트이다.

FS 레지스터는 애플리케이션 디버깅에도 자주 등장하는데 SHE(Structured Exception Handling), TEB(Thread Environment Block), PEB(Process Environment Block) 등의 주소를 계산할 때 사용되며 고급 리버싱을 할 때 주제이다.

 

Program Status & Control Register

EFLAGS: Flag Register

플래그(Flag) 레지스터의 이름은 EFLAGS이며 32비트(4바이트) 크기이다. 다른 레지스터와 마찬가지로 예상할 수 있듯이, FLAGS 레지스터(16비트, 2바이트)의 확장 형태이다.

EFLAGS 레지스터는 각각의 비트마다 의미를 가지고 있다. 각 비트는 1 또는 0의 값을 가지는데, 이는 On/Off 혹은 True/False를 의미한다. 일부 비트는 시스템에서 직접 세팅하고, 일부 비트는 프로그램에서 사용된 명령의 수행 결과에 따라 세팅된다.

EFLAGS 레지스터의 32개의 각 비트 의미를 전부 이해한다는 것은 상당히 어려운 일이다. 리버싱 입문 단계에서는 Application Debuging에 필요한 3개의 flag(ZF, OF, CF)만 잘 이해하면 된다.

 

Zero Flag(ZF): 연산 명령 후에 결과 값이 0이 되면 ZF 1(True)로 세팅된다.

Overflow Flag(OF): 부호 있는 수(signed integer)의 오버플로우가 발생했을 때 1로 세팅된다. 그리고 MSB(Most Significant Bit)가 변경되었을 때도 1로 세팅된다.

Carry Flag(CF): 부호 없는 수(unsigned integer)의 오버플로우가 발생했을 때 1로 세팅된다.

 

 

Instruction Pointer

Instruction pointer CPU가 처리할 명령어의 주소를 나타내는 레지스터이다. EIP의 크기는 32비트이고, 마찬가지로 16비트인 IP 레지스터의 확장된 형태이다. CPU EIP에 저장된 메모리 주소의 명령어(instruction)를 하나 처리하고 자동으로 그 명령어의 길이만큼 EIP의 길이를 증가시킨다. 이런 식으로 계속 명령어를 처리한다.

범용 레지스터와는 다르게 EIP는 그 값을 직접 변경할 수 없도록 되어 있어서 다른 명령어를 통하여 간접적으로 변경해야 한다. EIP를 변경하고 싶을 때는 특정 명령어(JMP, JCC, CALL, RET)를 사용하거나 인터럽트, 예외를 발생시켜야 한다.

 

'Reversing > Etc' 카테고리의 다른 글

Stack  (0) 2017.06.27
Posted by BinZIP