틀린 부분이 있다면 언제든지 댓글 남겨주세요!
리버싱 기초 - 레지스터 기본 명령어
Opcode (명령어)
[명령어] dst, src
Data Movement
- mov: src의 값을 dst로 옮김 (길이가 반드시 일치해야 함 WORD-WORD, BYTE-BYTE)
* MOVSX(부호o), MOVZX(부호x)는 길이가 달라도 가능 (BYTE-WORD, WORD-DWORD) 0비트로 채움
- lea: src의 주소를 dst에 저장
Arithmetic Operations
- inc / dec: dst의 값을 1 증가/감소 = ++dst / --dst
- neg: dst의 값의 부호를 바꿈 (2의 보수) = -dst
- not: dst의 값의 비트를 반전 = ~dst
- add / sub / imul / idiv: dst의 값에 src를 더함/뺌/곱함/나눔 = dst+=src / dsc-=src / dsc*=src
* ADC/SBB: 연산시 carry값을 포함해서 더함/뺌
* 인자가 3개인 경우 명령여 dest source1 source2 ( ex) imul edx, ecx, 0 > ecx * 0 한 값을 edx에 넣음 )
mul / div: EAX 레지스터의 내용에 src의 값을 곱함/나눠 EAX에 저장 (몫은 EAX에 나머지는 EDX에 저장)
- and / or / xor: dst의 값과 src간에 AND/OR/XOR 논리연산한 결과를 dst에 저장 = dst&=src / dsc|=src / dsc^=src
- shl / shr: dst의 값을 k만큼 왼쪽/오른쪽으로 shift (빈자리는 0으로 채움) = dst << k / dst >> k
- sal / sar: dst의 값을 k만큼 왼쪽/오른쪽으로 shift하는데 부호가 유지됨 (즉 최상위 비트는 바뀌지 않음)
Conditional Operations
- test: and연산자와 같이 AND연산을 하지만 결과가 dst에 저장되지 않고 FLAGS의 ZF, SF에 영향
연산 결과가 음수이면(최상위비트가 1이면) SF=1, 양수이면(0이면) ZF=1
* test eax, eax 형태로 주로 사용. eax가 0인지 아닌지 확인하기 위함 (0이면 ZF=1이므로 아래에 je가 있는 경우 분기)
- cmp: sub연산자와 같이 SUB연산을 하지만 결과가 dst에 저장되지 않고 FLAGS의 ZF, CF에 영향
dst=src인 경우 ZF=1/CF=0이고, dst<src인 경우 ZF=0/CF=1이고, dst>src인 경우 ZF=0/CF=0이다
[명령어] location
- jmp: 피연산자가 가리키는곳으로 점프
- 연산 수행 결과에 따라 점프의 수행 여부 결정 (jcc=Jump if condition is met)
명령어 | 의미 |
JE | ZF=1 (equal) (ZF=1) |
JNE | ZF=0 (not equal) (CF=0) |
JG (greater) | > (signed) |
JGE | >= (signed) |
JAE | >= (unsigned) (CF=0) |
JL (less) | < (signed) |
JLE | <= (signed) |
JBE | <= (unsigned) (CF=1 OR ZF =1) |
JA (above) | > (unsigned) (CF=0 AND ZF=0) |
JB (below) | < (unsigned) (CF=1) |
JS | SF=1 (negative) (SF=1) |
JNS | SF=0 (not negative) |
Operand
(64bit에선 맨앞에 r이, 32bit에선 e가 붙음!)
▶ General-Purpose Registers: 연산을 할 때 어떤 목적으로 사용하든 상관 없음
- ESI:복사할때 복사할 source data 저장
- EDI: 복사할 destination data 저장
- EAX(Accumulator for operands and result data): +, -와 같은 연산에 사용, 함수가 끝날때 함수의 반환값(return) 저장
- EDX: eax와 비슷하게 연산에 사용되지만 return값 반환에는 사용되지 않음
- ECX(Counter for string and loop operation): Counter register (for문의 i++과 같은 역할)
- EBX(Pointer to data in the DS Segment): EAX, EDX, ECX가 부족할때 사용하는 여분의 레지스터
- Flag register: 분기값의 상태
레지스터 스택 명령어
- function prologue: 새로운 함수가 시작될 때 스택이 준비
=> rsp에 들어있는 주소에서 충분한 값을 빼 함수 안의 지역변수를 위한 공간 확보
- function epilogue: 함수가 종료될 때 스택이 정리
=> prologue에서 빼준 값 만큼 다시 rsp에 더해 사용한 스택 정리
[명령어] r(e)di
- EBP(=base pointer): stack의 기준점, 즉 시작지점을 지정할때 사용
- ESP(=stack pointer): stack의 끝 지점, 즉 가장 위쪽 주소로 마지막으로 데이터가 추가된 위치
* 아키텍처에 따라 스택이 쌓이는 방향이 다름. Intel x86-64 아키텍처에서는 스택이 낮은 주소(=더 작은 숫자)로 쌓이기 때문에 rsp에 저장된 메모리 주소는 점점 낮아짐
- push: 스택에 새로운 데이터 추가
= sub rsp, 8 mov [rsp], rdi
- pop: 스택의 최상단에 있는 데이터 뺌
= mov rdi, [rsp] add rsp, 8
- call: 실행할 함수의 주소
= push retaddr jmp location
* caller standard register: eax, ecx, edx
* CALL과 JMP의 차이: CALL은 스택에 다음 주소를 저장함. 즉 CALL함수를 빠져나온 후 돌아가야할 주소가 저장됨. 반면 JMP는 그냥 그 위치로 가고 끝!
- ret: 함수 종료 후 돌아갈 주소로 돌아가는 역할
= pop rip jmp rip
내가 참고하려고 아무렇게나 추가하는 분석하면서 찾아본 명령어
PUSHAD :레지스터 값을 스택에 저장
순서: EDI-ESI-EBP-ESP-EBX-EDX-ECX-EAX
이때 레지스터의 크기(32BYTE=4BYTE*8)만큼 ESP감소
NOP: 아무일도 하지 않음
NEG: 2의 보수
SHL: 왼쪽 shift
SHR: 오른쪽 shift
SAL: 왼쪽 산술 shift
SAR: 오른쪽 산술 shift
ROL: 왼쪽 회전 shift
ROR: 오른쪽 회전 shift
RCL: 왼쪽 캐리포함 회전
RCR: 오른쪽 캐리포함 회전
SHLD: 2배 정밀도 왼쪽 shift
SHRD: 2배 정밀도 오른쪽 shift
LODS(load string): (E)SI주소가 가리키는 값을 (E)AX(AL, RAX)에 로드, ESI의 값은 다음 주소값으로 로드.
STOS :EDI가 가리키는 주소에 EAX값을 로드, EDI값은 EAX의 크기만큼 증가.
LEA와 MOV의 차이: LEA는 좌변(레지스터만 가능)에 우변의 주소값(상수 불가)을 로드하고 MOV는 우변의 메모리값(상수 가능)을 로드한다.
XCHG: 두 피연산자의 데이터 교환. 즉 temp같은 역할
DWORD PTR DS[](StackSegment) :data영역의 주소 4바이트 공간
DWORD PTR SS[](DataSegment) :stack영역의 주소 4바이트 공간
DWORD PTR ES[](ExtraSegment) :목적지 또는 DS의 보조
AL: EAX의 하위 8bit
TEST AL, AL : AL이 0이 아니면 ZF=0, JNE(not equal)분기
STC(Set Carry Flag): 캐리 플래그(CF) =1
CLC(Clear Carry Flag): 캐리 플래그(CF) =0
STD(Set Direction Flag): 방향 플래그(DF) =1
* 방향플래그가 1이면 스트링 인스트럭션 수행 후 ESI/EDI값이 감소
(BYTE -1, WORD -2, DWORD -4)
CLD(Clear Direction Flag): 방향 플래그(DF) =0
* 방향플래그가 0이어야 스트링 인스트럭션 수행 후 ESI/ED값 증가
STI(Set Interrupt Flag): 인터럽트 플래그(IF) =1
CLI(Clear Interrupt Flag): 인터럽트 플래그(IF) =0
REPE (Repeat String Equal) =REPZ
문자열 관련 명령을 ECX>0인 동안 ECX -1 하면서 반복. 즉 ECX 를 카운터로 사용하여 문자열을 비교하는 명령어.
같으면 반복해라. ZF=0 이거나 ECX=0이면 멈춤.
= REP(E) CMPS [destination], [source]
* CMPS(CoMPare String). CMPSB(Byte)/W(Word)/D(Double Word)
REPNE SCAS: 간단하게 해당 위치의 문자열 길이를 구한다고 생각.
CMOV : Conditional Move.
ex) CMOVZ ecx, esi인 경우 ecx가 0(ZF==0)이면 ecx에 esi 대입
SETNE: not equal zero, ZF=0 (zero가 아님) 이면 1을 가짐.
SETE: equal zero, ZF=1 (zero임) 이면 1을 가짐.
'Security > Reversing' 카테고리의 다른 글
PEB TEB (0) | 2021.02.02 |
---|---|
Abex crackme #2 / ollydbg 단축키 (0) | 2021.01.25 |
Reversing Basic Challenge #1 (0) | 2020.12.20 |
Reversing Basic Challenge #0 (0) | 2020.12.19 |
x64dbg 기본 사용법 (0) | 2020.12.17 |
댓글