참고 도서 : [리버싱 핵심원리 : 악성 코드 분석가의 리버싱 이야기]
저자 : 이승원
출판사 : 인사이트
참고 문헌 : [리버싱 기초]
저자 : 심준보
작성 날짜 : 2014-01-07
참고 사이트 :
실행 파일을 생성하는 어떠한 프로그래밍 언어라도 빌드 과정을 거치면 모두 기계어로 변환된다. 디버거를 통해서 어떤 실행 파일이라도 어셈블리 언어로 번역해서 볼 수 있기 때문에 리버서는 기본적으로 어셈블리 언어를 잘 알아야 한다. 어셈블리 언어만 잘 익혀놓으면 실행 파일이 어떠한 프로그래밍 언어로 제작되어 있는지 상관없이 디버깅을 통한 코드 분석이 가능해 진다.
Assembler
어셈블러(assembler)는 어셈블리어를 기계어 형태의 오브젝트 코드로 해석해 주는 컴퓨터 언어 번역 프로그램을 말한다. 어셈블러는 기본 컴퓨터 명령어들을, 컴퓨터 프로세서가 기본 연산을 수행하는데 사용할 수 있는 비트 패턴으로 변환시키는 프로그램이다. 몇몇 사람들은 이러한 명령어들을 어셈블러 언어라고 부르며, 어셈블리 언어라고 부르는 사람들도 있다.
어셈블리는 명령 부호를 OP code로 해석할 뿐만 아니라 메모리의 위치들을 이름으로 표시하는 기능, 매크로를 통한 문장 치환 기능 등을 함께 제공한다. 높은 수준의 어셈블러는 고급 제어 구조, 프로시져/함수 선언 및 호출, 자료형 추상화 같은 언어 추상화 기능을 제공하기도 한다.
OP code 명령 코드 (Operation code)는 기계어의 일부이며 수행할 명령어를 나타내는 부호를 의미한다. 이에 대한 규격과 형식은 프로세서 명령어 집합에 나와 있다. 기계어 명령어 (Instruction)은 명령어를 나타내는 opcode를 가지며, 일반적으로 피연산자를 나타내는 하나 이상의 지정자를 가진다. |
Assembly Language
어셈블리어(Assembly language)는 기계어와 일대일 대응이 되는 컴퓨터 프로그래밍의 저급 언어이다. 컴퓨터 구조에 따라 사용하는 기계어가 달라지며, 이 기계어에 대응되어 만들어 지는 어셈블리어도 각각 다르다.
기계어는 실제로 컴퓨터의 CPU가 읽어서 실행할 수 있는 0과 1로 이루어진 명령어의 조합이다. 이러한 각 명령어에 대해 사람이 알아보기 쉬운 니모닉 기호(mnemonic symbol)를 정해 사람이 좀 더 쉽게 컴퓨터의 행동을 제어할 수 있도록 한 것이 어셈블리 언어이다.
예를 들어,
101 10000 01100001 |
는 x86 계열 CPU의 기계어 명령이고, 이것을 어셈블리어로 옮겨쓰면 다음과 같다.
mov al, 061h |
명령어 mov는 영어 move를 변형한 니모닉이며, al은 CPU 안에 있는 변수를 저장하는 레지스터의 하나이다. 그리고, 061h는 16진수 61 (HEX 61 = DEC 97 = BIN 01100001)이다. 즉, 16진수 61을 al 레지스터에 넣으라는 의미이다. 이는 1과 0의 반복인 기계어보다 사람이 혼동없이 이해하기 한결 쉽다.
기본 어셈블리어
| 명령어 | 설명 |
데이터 이동 |
MOV |
피연산자 2를 피연산자 1로 복사 연산 결과에 따라서 ZF, OF, CF가 설정 될 수 있음 |
주소 이동 |
LEA |
지정한 주소를 가져오는 명령어 |
산술 연산 |
ADD |
피연산자 = 피연산자 1 + 피연산자 2 결과가 0이면 ZF가 1로 설정 |
SUB |
피연산자 1 = 피연산자 1 - 피연산자 2 |
|
MUL |
AX * 피연산자 |
|
DIV |
AX / 피연산자 부호가 없는 나눗셈 (DIV : 부호가 있는 나눗셈) 나눠지는 수 : EAX, 나누는 수 : 피연산자 연산 값이 32비트 이상인 경우 EDX가 함께 사용됨 16비트일 경우 나누는 수는 무조건 8비트 결과는 AX 레지스터에 저장 |
|
INC |
피연산자 + 1 연산 결과에 따라 ZF, OF 플래그 값이 셋팅 INC BX |
|
DEC |
피연산자 - 1 연산 결과에 따라 ZF, OF 플래그 값이 셋팅 |
|
비교 연산 |
CMP |
피연산자 1과 피연산자 2 비교 두 연산 결과가 같으면 0 피연산자 1 - 피연산자 2 > 0 이면, CF = 0 피연산자 1 - 피연산자 2 < 0 이면, CF = 1 피연산자 1 - 피연산자 2 = 0 이면, ZF = 1 |
조건 분기 |
Jxx (JMP) |
플래그를 보고 해당 조건이 맞을 경우 분기 JE - Jump, if Equals ZF = 1 JNE - Jump, if not Equals ZF = 0 |
함수 호출, 복귀 |
CALL |
함수 호출 시 사용 스택에 CALL 다음 주소를 PUSH하고 해당 함수로 넘어감 |
RETN |
CALL에서 PUSH한 리턴 주소를 명령어 포인터로 팝업 모두 반환되는 것이 아닌 LEAYE 명령어로 함수에 사용된 스택 값 보내줌 |
|
테스트 |
TEST |
피연산자 1과 피연산자 2를 AND 한 후 결과 값의 상태 정보만 플래그 레지스터에 저장 연산 결과는 변하지 않고 테스트 하여 자신의 상태 값만 반영 MOV 명령어로는 플래그 값을 변경하지 못하므로 TEST 명령을 토앻 FLAG를 갱신, 조건 설정할 때 많이 사용 영향을 받는 플래그는 CF, OF는 초기화 되며, ZF는 설정될 수 있음 |
스택 |
PUSH |
스택에 저장 |
POP |
스택에서 꺼내기 |
아래는 NASM x86 어셈블리어로 구현한 헬로 월드 프로그램이다.
adosseg .model small .stack 100h .data hello_message db 'Hello, World!', 0dh, 0ah, '$' .code main proc mov ax, @data mov ds, ax mov ah, 9 mov dx, offset hello_message int 21h mov ax, 4C00h int 21h main endp end main |
Disassembler
역어셈블러 또는 디스어셈블러 (disassembler)는 기계어를 어셈블리어로 변환하는 컴퓨터 프로그램이다. 역어셈블러는 어셈블리어가 아닌 고급 프로그래밍 언어를 대상으로 하는 역컴파일러와는 구분한다. 역어셈블러의 출력물인 디스어셈블리(disassembly)는 어셈블러로의 입력에 맞추는 것보다 사람이 읽기 쉽도록 형식을 정하는 경우가 있는데, 이것이 바로 리버스 엔지니어링 도구의 역할이다.
대부분의 상호적인 디버거는 디버깅되는 프로그램의 기본적인 역어셈블리를 포함한다. 종종 같은 역어셈블리 툴이 디버거와 함께 독자적인 역어셈블러로 배포되는 경우도 있다.
'Study > 리버싱' 카테고리의 다른 글
Endianness (0) | 2016.06.08 |
---|---|
Stub Code (0) | 2016.06.08 |
OllyDbg (1) (0) | 2016.06.05 |
Debugger (0) | 2016.06.05 |
리버싱 (0) | 2016.06.05 |
댓글