상세 컨텐츠

본문 제목

리버싱 입문 2. 어셈블러

REVERSING/Reversing Study

by koharin 2021. 4. 6. 02:00

본문

728x90
반응형

1. 어셈블러의 개요


1.1 단순한 언어 어셈블러

고급 언어는 데이터를 변수나 클래스에 저장한다.

어셈블러는 데이터를 스택, 레지스터, 메모리에 저장한다. (변수X)

스택 - 넣고, 빼는 기능만

레지스터 - 숫자와 용량 정해져 있다.

메모리 - 주소를 통한 직접 접근

1.2 어셈블러 개요

  • 프로그래밍 언어로 소프트웨어 개발 이유: 쉬운 유지보수, 높은 생산성
  • 소프트웨어 실행 주체는 운영체제- 실행 파일 클릭 시 운영체제가 파일을 메모리에 로딩하고(fetch), 해석하여(decode), 해석한대로 동작을 수행(execute)한다.
  • 운영체제는 0과 1의 조합의 비트를 해석한다.
  • 컴파일러: 고급언어를 OS가 이해할 수 있는 저급 언어로 변환하는 프로그램
  • 어셈블러: 컴퓨터가 이해할 수 있는 기계 코드를 사람이 이해할 수 있도록 만든 메크로 모음
  • 어셈블러와 기계어러와 기계어는 1:1 매칭된다.

 

 

2. 어셈블러 기본 구조


어셈블러 명령어는 최대 3개의 인자를 받아들일 수 있다.

2.1 2개의 인자를 사용하는 명령어

ADD 인자1 인자2

  • 인자1 = 인자1 + 인자2
  • ADD EAX, EBX ; EAX = EAX + EBX
    • 헥사코드: 01D8

2.2 1개의 인자를 사용하는 명령어

INC 인자1

  • 인자1 = 인자1 + 1
  • INC ESI ; ESI = ESI + 1 또는 ESI++
    • 헥사코드: 46

PUSH 인자1

  • 인자1을 스택에 넣는다.
  • PUSH 0
    • 헥사코드: 6A 00

2.3 인자가 없는 명령어

RETN

  • 스택 맨 위의 값을 EIP 레지스터에 저장한다.
    • 스택 맨 위의 값을 EIP에 저장하면 해당 명령어를 실행할 수 있다.
  • EIP(Instruction Register): 다음 실행할 명령어 저장
  • 헥사코드: C3

 

 

3. 어셈블러 명령어 종류


3.1 데이터 이동

명령어 사용법 설명 예시
MOV MOV dst, src src 내용을 dst로 복사 MOV EAX, ECX
before: EAX(00400020), ECX(00400030)
after: EAX(00400030), ECX(00400030)
MOVZX MOVZX dst(32bit), src(16bit)
MOVZX dst(16bit), src(8bit)
src 내용을 dst로 복사하고, dst 나머지 부분을 0으로 채운다.
dst에 32bit 레지스터가 오면 src 내용을 dst로 복사하고, dst 나머지 부분을 0으로 채운다.
dst에 32bit 레지스터가 오면 src에는 16bit 레지스터가 온다.
MOVZX EAX, CX
before: EAX(89ABCDEF), ECX(12345678)
AFTER: EAX(00005678), ECX(12345678)
MOVSX MOVSX dst(32bit), src(16bit)
MOVSX dst(16bit), src(8bit)
src의 내용을 dst로 복사하고, dst 나머지 부분을 src의 맨 처음 비트로 채운다. dst에 32bit 레지스터가 오면 src에 16bit 레지스터가 온다. MOVSVX EAX, CX
BEFORE: EAX(89ABCDEF), ECX(12345678)
AFTER: EAX(00005678), ECX(12345678)
LEA LEA dst(레지스터), src src의 주소를 dst로 복사
dst에는 레지스터만 가능
LEA EAX, [ESP-4]
BEFORE: EAX(89ABCDEF), ESP(0018FF88)
AFTER: EAX(0018FF88), ESP(0018FF88)

3.2 산술 연산자

명령어 사용법 설명 예시
ADD ADD DST, SRC DST와 SRC 내용 더해서 DST에 저장 ADD EAX, ECX
BEFORE: EAX(00000002), ECX(00000001)
AFTER: EAX(00000003), ECX(00000001)
SUB SUB DST, SRC DST = DST - SRC SUB EAX, ECX
BEFORE: EAX(00000004), ECX(00000001)
AFTER: EAX(00000003), ECX(00000001)
MUL MUL SRC 부호 없는 곱셈
EAX 레지스터에 SRC 내용 곱해서 EAX 레지스터에 저장.
결과 값이 커서 목적지에 다 들어가지 않으면 EDX 레지스터에 넘치는 상위 비트 값 저장
MUL ECX
BEFORE: EAX(00077777), ECX(00077777), EDX(00000000)
AFTER: EAX(4861D951), ECX(00077777), EDX(000037C0)
IMUL 1. IMUL DST
2. IMUL DST, SRC
3. IMUL DST, SRC, 정수
부호 있는 곱셈
1. EAX = EAX * DST
(EDX 레지스터에 결과 값이 커서 넘친 값을 저장)
2. DST = DST * SRC
(EDX 레지스터에 결과 값이 커서 넘친 값을 저장)
3. DST = SRC * 정수
(EDX 레지스터에 결과 값이 커서 넘친 값을 저장)
IMUL ECX, EBX, -4
BEFORE: ECX(00000003), EBX(00000004)
AFTER: ECX(FFFFFFF0), EBX(00000004)
DIV DIV SRC(레지스터) 부호 없는 나눗셈
EAX = EAX / SRC
EDX = 나머지
(DIV 연산 전 EDX를 0으로 초기화한다.)
DIV ECX
BEFORE: EAX(0000000A), ECX(00000003), EDX(00000000)
AFTER: EAX(00000003), ECX(00000003), EDX(00000001)
IDIV 1. IDIV SRC(8BIT 레지스터)
2. IDIV SRC(16BIT 레지스터)
3. IDIV SRC(32BIT 레지스터)
부호 있는 나눗셈
1. AX = AX / SRC, AL = 몫, AH = 나머지
2. 크기 커서 AX에 다 못 담으면 DX에 나누어 저장 DX와 AX 값을 SRC 내용으로 나누어 AX에 저장, DX = 나머지
3. EDX와 EAX 레지스터 값을 SRC 내용으로 나눈다. EAX = 몫, EDX = 나머지
IDIV ECX
BEFORE: EAX(FFFFFFF7), ECX(00000002), EDX(00000000)
AFTER: EAX(7FFFFFFB), ECX(00000002), EDX(00000001)
INC INC DST DST = DST + 1 INC ECX
BEFORE: ECX(00000003)
AFTER: ECX(00000004)
DEC DEC DST DST = DST - 1 DEC ECX
BEFORE: ECX(00000003)
AFTER: ECX(00000002)
NEG NEG DST 부호 반전
DST의 데이터 부호를 바꾼다. (2의 보수)
NEG ECX
BEFORE: ECX(FFFFFFFC) -4
AFTER: ECX(00000004)

3.3 부울 연산자

명령어 사용법 설명
AND AND DST, SRC DST = DST & SRC
OR OR DST, SRC DST = DST | SRC
NOT XOR DST, SRC DST = DST ^ SRC

3.4 분기 명령어

명령어 사용법 설명 예시
CMP CMP 인자1, 인자2 인자1 == 인자2: CF=0, ZF=1
CF == 0 && 인자1 != 인자2: CF = 0, ZF = 0
CMP EAX, ECX
BEFORE: EAX(00000001), ECX(00000001), C(1), Z(0)
AFTER: EAX(00000001), ECX(00000001), C(0), Z(1)
JMP JMP DST DST 주소로 이동한다.
복귀 주소 백업 X
JMP 00401018
JE JE DST ZF == 1이면 점프
보통 CMP 뒤에 온다.
JE 00401018
JNE JNE DST ZF == 0이면 점프  
JZ JZ DST ZF == 1 또는 앞 연산 결과가 0이면 점프
보통 DEC나 JMP 뒤에 온다.
 
JNZ JNZ DST ZF == 0 또는 앞 연산 결과가 0이 아니면 점프
보통 DEC나 CMP 뒤에 온다.
 
JA CMP 인수1, 인수2
JA DST
CMP 인수1, 인수2 명령어 뒤에 와서 인수1 > 인수2이면 DST로 점프  
JB CMP 인수1, 인수2
JB DST
CMP 인수1, 인수2 뒤에 와서
인수1 < 인수2 이면 DST로 점프
 

3.5 시프트 연산자

명령어 사용법 설명 예시
SHL SHL DST, 비트 수 DST에 있는 데이터를 비트 수만큼 왼쪽으로 이동
곱셈과 관련
SHL EAX, 2
BEFORE: EAX(00000FFF)
AFTER: EAX(000003FFC)
SHR SHR DST, 비트 수 DST에 있는 데이터를 비트 수만큼 오른쪽으로 이동 SHR EAX, 2
BEFORE: EAX(00000FFF)
AFTER: EAX(000003FF)

3.6 스택 연산자

명령어 사용법 설명
PUSH PUSH 데이터 스택 맨 위에 데이터를 저장.
ESP를 4만큼 감소시킨다.
함수에 인자 전달, 지역변수 위한 공간 할당에 사용
POP POP DST 스택에서 데이터를 꺼내서 DST에 저장
ESP를 4만큼 증가시킨다.
PUSHAD PUSHAD EAX -> ECX -> EBX -> ESP -> EBP -> ESI -> EDI 레지스터 순서대로 스택에 PUSH한다.
ESP 레지스터는 32비트 만큼 감소
POPAD POPAD 스택에 존재하는 값을 EAX, EBX, ECX, EDX, ESI, EDI, ESP, EBP 레지스터로 POP한다.
ESP 레지스터는 32비트 증가

3.7 기타 연산자

명령어 사용법 설명
CALL CALL DST RETURN ADDRESS(EIP 레지스터 값)을 스택에 백업한 후, DST 주소에 있는 서브루틴(함수)로 이동한다.
RETN RETN
RETN 바이트수
백업한 RETURN ADDRESS로 이동한다.
RETN == POP EIP

TEST TEST 인수1, 인수2 인수1과 인수2 내용을 AND 연산한 결과가 0이면 ZF = 1
NULL 체크 시 사용

 

728x90
반응형

관련글 더보기