amd64 또는 x64 에서의 기계어 분석 #1 asm

최종목표는 디스어셈블러를 만드는게 되겠지만 일단 지금은 간단한거 부터 시작한다.

레지스터도 범용으로 쓰이는거 말고 딴거도 많지만 지금은 16개에만 집중한다.

  • 레지스터 번호
번호 레지스터 번호 레지스터
0 AL,AX,EAX,RAX 8 R8B,R8W,R8D,R8
1 CL,CX,ECX,RCX9R9B,R9W,R9D,R9
2 DL,DX,EDX,RDX 10R10B,R10W,R10D,R10
3 BL,BX,EBX,RBX 11 R11B,R11W,R11D,R11
4 (AH)SPL,SP,ESP,RSP 12 R12B,R12W,R12D,R12
5 (CH)BPL,BP,EBP,RBP 13 R13B,R13W,R13D,R13
6 (DH)SIL,SI,ESI,RSI14R14B,R14W,R14D,R14
7 (BH)DIL,DI,EDI,RDI 15R15B,R15W,R15D,R15

괄호친 부분 AH,CH,DH,BH 는 64비트에서 사용하지 않는다.

이 레지스터의 이름에 CPU의 역사가 녹아있다.

8비트 시절은 잘 모르겠고 16비트 시절에는 AX,BX,CX,DX,SP,BP,SI,DI 로 불렸다.

이 네개중 ABCD 네개는 각각 반으로 나눠서 각각에 이름까지 붙였다

AX = AH + AL 
BX = BH + BL
CX = CH + CL
DX = DH + DL

L  이 붙은건 b0 부터 b7 까지 하위 8개의 비트를 말하고 (Low)
H 가 붙은건 b8 부터 b15 까지의 상위 8개의 비트를 말한다. (High)

SP,BP,SI,DI 는 반으로 나누지 않았다. 설계가 복잡해지고 돈이 많이 들고 ABCD 네개만으로 충분했기 때문에 그랬을거라고 생각해본다. 

그러다 32비트 시절을 맞았다.

레지스터의 크기를 16비트에서 32비트로 키웠다. 하위 호환성을 위해서 16비트시절의 이름은 그대로 두고 새로운 이름을 각각의 레지스터에 붙였다. 간단하게 E 하나를 앞에 붙였다. 

그래서 EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI 가 생겨났다.

16비트 시절에 사용했던 AX 나 AH, AL 같은걸 그대로 사용했다. 하위 호환성..

EAX 밑에 AX가 있고 AX 밑에 AH와 AL이 있다. 나머지도 같은식.

이제 64비트시절..

일단 기존에 사용하던 건 다 사용할 수 있다. 하위 호환성

레지스터 크기를 64비트로 늘이고 이름은 E 를 떼어내고 R 을 붙였다.

RAX,RBX,RCX,RDX,RSP,RBP,RSI,RDI 

거기에다가 8개의 레지스터를 추가했다.

R8 부터 R15 까지.

그리고 기존에 쪼개지 않았던 16비트 크기의 SP,BP,SI,DI를 반으로 쪼개어 하위 8비트만 사용하게 했다.

이름은 SPL, BPL, SIL, DIL 로 붙였다.

위의 표에서 레지스터 4,5,6,7번에 나타나있다. 64비트 모드일때만 사용할 수 있다.

새로 생긴 R8 부터 R15번 까지 레지스터도 나누어서 사용할 수 있다.

R9를 예로 들면,

하위 8비트(바이트)는 R9B
하위 16비트(워드)는 R9W
하위 32비트(더블워드)는 R9D

이처럼 세월따라 레지스터의 크기가 커졌는데 그러면 명령어에도 변화가 있지 않았을까?

mov 라는 명령어로 예를 들면 이해하기 좋다.

move 라는 말처럼 이쪽에 있는 데이타를 저쪽으로 옮기는 명령이다. 원본을 지우지 않으니 복사라는 말이 어울릴텐데도 어쨌든 명령어 이름은 mov 이다. 사용법은 다음과 같다.

mov 저쪽, 이쪽 
mov destination, source

이쪽을 출발지라고 보고 저쪽을 목적지라고 말할 수 있겠는데 출발지와 목적지가 레지스터이냐 메모리이냐에 따라 명령이 갈리고 메모리의 위치를 지정할 때에도 지정하는 방식에 따라 여러가지로 갈린다. 16비트 에서는 예를 들면 이런식이다.

1011 w reg data1 data2

mov 의 출발지는 숫자이고 목적지는 레지스터일때 사용하는 형식이다.

w 가 0 이면 8비트 레지스터
w 가 1 이면 16비트 레지스터
reg 는 레지스터 번호(3비트로 표현)
data1과 data2는 숫자

8비트 2번 레지스터에 8비트크기의 숫자 12h(16진수 12)를 넣어라 라는 명령은 다음과 같이 인코딩된다.

1011 : mov
0     : 8비트 레지스터
010  : 2번 레지스터
0001 0010 : 12h

이어서 적으면 

1011 0010 0001 0010
16진수로 적으면 다시 적으면 
b2 12 

16비트 2번 레지스터에 16비트크기의 숫자 1234h 를 넣어라. 이것은?
크기를 나타내는 w가 1이된다. 그 뒤에 숫자 2바이트가 붙는다.

1011 1 010 data1 data1
ba 34 12 가 된다.

인텔에서는 숫자를 적을때 하위바이트를 먼저 적는 방식을 택했다. 그래서 34를 먼저 적는다. 이런 표기를 리틀엔디언(Littel Endian) 방식이라고 한다. 

8비트크기의 숫자를 넣는냐. 16비트크기의 숫자를 넣느냐에 따라 명령어의 길이가 달라졌다. 16비트 숫자 자체가 2바이트라서 그런거니까 이해못할바는 아니다.

그러면 32비트 cpu 에서는 이 명령어가 어떻게 바뀌었을까?

완전히 새로 뜯어고치면 아무 문제가 안되지만 하위호환성으로 전혀 뜯어고치지 않아야 하니까 골치가 아파진다.
16비트에서의 명령어 ba를 32비트에서 쓰고 16비트명령어 앞에는 16진수 66 을 앞에 붙이는 걸로 해결했다. 

그래서 32비트 2번 레지스터에 32비트 크기의 숫자 12345678h 를 넣어라는 이렇게 쓰게된다.

ba 78 56 34 12

비교를 위해서 8비트 부터 쭉 적어보면

1) b2 12
2) 66 ba 34 12
3) ba 78 56 34 12

니모닉으로 다시 적어보면 

1) mov dl, 12h
2) mov dx, 1234h
3) mov edx, 12345678h

mov dx,1234h 는 cpu에 따라 다르게 기계어로 바뀌는걸 알 수 있다.

16비트 cpu에서는 ba 34 12
32비트 cpu에서는 66 ba 34 12

명령어 앞에 붙이는 66 을 operand size override prefix 라고 부른다. 오퍼랜드 크기를 강제로 바꾸는 접두어 정도로 말할 수 있겠다. 32비트 cpu에서 16비트 크기의 레지스터를 쓸때 붙이는 단순한 접두어였는데 의미를 조금 확장해서 위와 같이 용어를 붙였다. 왜냐하면 미래 언젠가 설명되겠지만 128비트 크기의 레지스터를 쓸때도 66 을 앞에 붙였기 때문이다. 32비트 cpu에서 기본은 32비트 레지스터인데 그 크기이외의 다른 크기의 레지스터가 쓰일때 66이라는 접두어를 붙이고 보니 이 용어가 적합했던 것이다. 그렇다면 64비트 cpu에서는 어떻게 되었을까? 64비트 크기 이외의 레지스터을 쓸때 모두다 66을 붙였을까? 16비트크기와 32비트의 크기 둘다에 66을 붙이면 그 둘을 구별할 수 없는데 정말 그랬을까?

64비트 시스템에서는 다른방식으로 이 문제를 해결했다.

32비트cpu가 16비트 cpu에 했던 약탈을 하지 않고 상위4비트가 4로 고정된 한바이트 숫자를 붙임으로써 32비트 레지스터와 구별한 것이다.

0100 xxxx 

xxxx에서 x는 경우에 따라 0 이 되기도 1이 되기도 한다. 이 숫자를 rex 라고 부른다.

64비트 2번 레지스터에 64비트숫자 1234567890123456h 을 넣어라는 명령은 다음과 같이 된다.

48 ba 56 34 12 90 78 56 34 12

4로 시작하니까 48이 rex가 된다.

또 다른 예를 들어보자.

메모리의 특정번지에 있는 데이타를 레지스터 읽어올 때 이다.

16비트 cpu 에서는 주소를 지정할때 8비트가 아니라 16비트 레지스터로 주소를 지정하고 8개의 16비트 레지서터중에서 bx,bp,si,di 이 네개의 레지스터만 사용해야한다는 제한을 뒀다.

32비트 cpu 는 32비트의 메모리주소를 가질 수 있기에 32비트의 레지스터로 주소를 지정하도록하고 모든 32비트 레지스터는 다 사용할 수 있게 했다. 16비트 레지스터로는 주소를 지정할 수 없다.

<16비트 cpu>
mov ax,[si]
8b 06

<32비트 cpu>
mov  eax,[esi]
8b 06

64비트 cpu 는 64비트 레지스터로 주소를 지정하지만 32비트로도 주소를 지정할 수 있게했다. 32비트로 주소를 지정할때 67을 붙인다.이 숫자를 address size override prefix라고 부른다. 

<64비트 cpu>
mov  eax, [rsi]
8b 06

mov eax, [esi]
67 8b 06

8b 06 이라는 기계어는 cpu의 비트수에 따라 다른 니모닉을 만들어낸다. 

66과 67의 쓰임에 대해 정리를 해보자.(16비트 cpu는 제외한다.)

1) 둘다 레지스터의 크기에 관한것이다.
2) 67은 64비트 cpu에서 32비트 레지스터를 주소로 사용할때 붙인다.
3) 66은 16비트 레지스터를 사용할때 붙인다.

명령어 형식


prefix opcode mod r/m  sibdisplacement 
 disp 변위
immediate
imm 즉치

48      8B     84     96     34 12 00 00     없다  

  • mov rax, [ rsi + rdx*4 + 1234h] 
  • 48 8B 84 96 34 12 00 00

prefix 와 opcode의 종류는 많고도 많아서 잠시 접어두고 여기서는 

prefix중 rex 그리고 mod r/m 과 sib 에 집중하자. 

셋다 각각 한바이트 크기다.

먼저 rex 

64비트모드로 동작하겠다는 뜻이다.

이게 붙어있어야 새로 만들어진 r8 이라든가 rax 같은 64비트모드 레지스터에 접근할 수 있다.

64비트 크기의 레지스터만 사용하는게 아니라 r10w 같은 16비트 크기의 레지스터도 사용할 수 있다.


속을 들여다 본다.

b7                         
 0  1  0  0 
 w  r 
 x  b 

상위 4비트는 0100b = 4 로 고정.

w 는 오퍼랜드 크기를 결정한다. (Width)

1 이면 64비트크기 

mov eax, ebx 가 있었을때 w가 1이 되면 

mov rax, rbx 로 바뀐다.

기계어를 분석하며 레지스터를 고를때 이 w비트가 1이면 64비트크기의 레지스터를 택한다는 말이다.
rex 바이트가 앞에 붙어있으면 64비트 모드를 나타니까

r,x,b 비트 각각이 1 이면 64비트에서 새로 생긴 r8 부터 r15 중 하나를 가르킨다.

다음에 설명한다.

modR/m 바이트의 구조.

b7
m1m0r2r1
r0b2
b1b0


b7과 b6은 mod를 나타낸다. 4개의 모드를 나타낼수있다. rax를 예로 들면 다음과 같다.

00[레지스터][rax]
01[레지스터 + 변위8비트][rax + 12h]
11[레지스터 + 변위32비트] [rax + 12345678h]  
11레지스터rax

모드 0 와 모드3이 헷갈리려나?

대괄호가 붙어있느냐 없느냐에 따라 의미가 달라진다.

rax 의 값이 100 이라면 [rax]는 100번지에 있는 데이타의 값이다.

모드3 일때만 레지스터를 나타내고 그밖의 모드는 메모리주소를 나타낸다.

조금 뒤에 다룰 r/m 에 직접적인 영향을 미친다.


r2 r1 r0 는 reg/op 를 나타내는 3개의 비트다. 레지스터를 나타낼수도 있고 명령어(opcode)를 나타낼수도 있다.

3개의 비트니까 8개의 레지스터를 가리킬수있다. 64비트에서는 16개의 레지스터가 있으니까 비트 하나가 모자란다. 그래서 비트하나를 rex 바이트에서 가져온다. 그게 r 비트다 . 이 r 비트와 3개의 비트를 합쳐 16개의 레지스터중 하나를 가리킨다.

처음에 표로 만든 레지스터표와 같다. r 비트가 1 이면 0번 레지스터는 8번 레지스터가 된다.

+8을 하면 된다.

0000번 레지스터al,ax, eax,rax
0011번 레지스터cl,cx,ecx,rcx
0102번 레지스터dl,dx,edx,rdx
0113번 레지스터bl,bx,ebx,rbx
1004번 레지스터spl,sp,esp,rsp
1015번 레지스터bpl,bp,ebp,rbp
1106번 레지스터sil,si,esi,rsi
1117번 레지스터dil,di,edi,rdi

이 reg/op가 reg가 아니라 op즉 명령어로 쓰일때는 다음과 같다.

먼저 그림을 보자.



group1의 opcode가 80 인 곳을 보면 modR/M의 reg의 값에 따라 명령어가 갈린다.

reg가 0일때는 80 이 add로
reg가 1일때는 80 이 or가 된다.

reg/op 가 레지스터를 가리키지 않으니까 이 명령어의 오퍼랜드는 레지스터가 하나밖에 없다

op reg ( inc eax ) 

op reg imm16 ( xor eax, 1234h ) 

이런건 가능해도 

op reg1 reg2 같은건 안된다.

그런데 xor eax, ebx 같은 명령은 존재하는데 이건 xor 의 기계어코드가 80/6 일때가 아니라 31 일때 이다. 즉 xor에는 여러개의 기계어코드가 배정되어있는데 ( 30 - 35, 80/6, 81/6, 83/6 ) 그중 80/6 으로는 두개의 레지스터를 지정할 수 없다.

이런종류의 명령어는 1바이트 명령이 아니라 1.3 바이트 명령어라고 할 수가 있을게다.

1바이트 명령은 push 같은걸 들수있다. opcode 68은 reg의 값에 관계없이 push를 의미하니까.

명령어는 크게 두개로 나뉜다. 

1.3바이트 명령어 인가, 아닌가(?)

능력이 되면 외워도 되겠지만 명령어표를 참조하자.

방대한 문서라 찾기 쉽지 않을듯하여 빨간박스를 쳐봤다.

volume2 의 appendix A.3 에 있다.


b2 b1 b0는 r/m 을 의미하는 3개의 비트다

r/m 은 register/memory . 레지스터 또는 메모리.

r 은 레지스터
m은 [레지스터]

언제 r 이 되고 m 이 되는지는 modR/m의 mod의 값이 결정한다.

mod의 값이 3 이면 r
그 밖의 값일때는 m

r 이 3이면 레지스터 3번. 즉 bl,bx,ebx,rbx 중 하나를 의미하고

m 이 3이면 [ebx], [rbx] 중 하나를 의미한다. 

32비트에서는 [ebx], 64비트에서는 [rbx] 

ebx의 크기는 32비트이고 rbx의 크기는 64비트이니까 당연하다.

3비트니까 8개의 레지스터를 가리킬 수 있다. reg/op 에서 처럼 rex 에서 비트 하나를 가져와야 16개의 레지스터를 가리킬수있다. 그 비트가 b비트이다. rex의 b 비트가 1이면 r8 부터 r15중 하나을 가리킨다. reg/op에서와 같은 사용법이다.

두가지 예외가 있다. 

rex.b의 값에 관계없이 r/m의 값이 100b = 4  이고 mod 의 값이 3이 아닐때
rex.b의 값에 관계없이 r/m의 값이 101b = 5  일때이다.

4 = reg.4 = rsp 를 가리키는게 아니라 modR/m 바이트 바로 뒤에 sib 바이트가 있다는걸 의미한다.
rsp의 의미는 완전히 잃는다.
그러나 mod가 3일때 즉, r/m이 레지스트로 동작할때는 rsp가 살아난다.

sib이 뭔지는 뒤에서 설명한다.

그리고 5일때 

이건 좀 더 엄격하다. 

r/m의 값이 5이고 mod가 0일때 

즉 원래대로 라면 [rbp] 이어야하는데 그게 아니라 [rip + 상대변위32] 가 된다.

rip의 값이 100 이고 원하는 번지가 300번지라면 [rip + 200] 으로 표현하는것을 말한다.


연습을 좀 해보자.

몇개의 가정이 필요하다.

가정1) 1.3 바이트 명령어를 opx 라 하고 이외의 명령은 op라고 하자.

가정2) 이 명령어의 첫번째 오퍼랜드는 reg 이고 두번째는 r/m 이다

가정3) 이 명령어의 오퍼랜드 크기는 32비트 또는 64비트이다


40 op 4d 12 34 56 78 = 0100 0000  op 0100 1101 .....

modR/m 을 다시 적어보면 01 001 101 로 적을 수 있고

mod          01 = [base + 변위8비트]

reg/op       001 = reg.1 = ecx (rex.w == 0 이고 reg.r == 0)

r/m           101 = reg.5 = rbp ( rex가 있으니까 64비트모드)

op ecx, [rbp + 12h] 가 된다.


49 op 8d 12 34 56 78 = 0100 1001  op 1000 1101 .....

modR/m 을 다시 적어보면 10 001 101 로 적을 수 있고

mod          10 = [base + 변위 32비트]

reg/op       001 = reg.1 = rcx ( rex.w == 1 이고 rex.r == 0 )

r/m           101 = reg.5  => r13 ( rex.w == 1 이고 rex.b == 1 이니까 1101 = reg.5 + 8 )

op rcx, [r13 + 78563412h] 가 된다.


rex바이트가 없는경우

op 8d 12 34 56 78 = op 1000 1101 .....

modR/m 을 다시 적어보면 10 001 101 로 적을 수 있고

mod          10 = [base + 변위 32비트]

reg/op       001 = reg.1 = ecx

r/m           101 = reg.5 = ebp (32비트모드일 경우)
r/m           101 = reg.5 = rbp (64비트모드일 경우)

op ecx, [ebp + 78563412h] (32비트 모드)(숫자는 리틀엔디언 방식으로 읽는다)
op ecx, [rbp + 78563412h] (64비트 모드)


op d7 12 34 56 78 = op 1101 0111 .....

modR/m 을 다시 적어보면 11 010 111 로 적을 수 있고

mod          11 = reg

reg/op       010 = reg.2 = edx

r/m           111 = reg.7 = edi (32비트모드일 경우)
r/m           111 = reg.7 = rdi (64비트모드일 경우)

op edx, edi (32비트 모드)
op edx, rdi (64비트 모드)


opx d7 12 34 56 78 = op 1101 0111 .....

modR/m 을 다시 적어보면 11 010 111 로 적을 수 있고

mod          11 = reg

reg/op       010 = 2 =  opx/2

r/m           111 = reg.7 = edi (32비트모드일 경우)
r/m           111 = reg.7 = rdi (64비트모드일 경우)

opx/2 edi (32비트 모드)
opx/2 rdi (64비트 모드)

  • sib 바이트
이 바이트를 사용할려면 위의 r/m의 값이 100b = 4 이어야하고
mod가 11b = 3 이 아니어야한다.

그래서 modR/m의 값이 01 010 100 이라면 

mod 01 = [base + disp8]
reg  010 = reg.2 = edx
r/m 100 = reg.4 = esp

op edx, [esp + disp8] 이런식으로 해석해선 안된다는 걸 뜻한다.

b7
s1s0x2x1
x0b2
b1b0

s 는 Scale
x 는 indeX
b는 Base

주소지정방식에 이런게 있다.

[base + index*scale + imm]

[ rbp + rsi*4 + 100h] 

스케일값은 0 부터 3까지 인데 2의 승수를 나타낸다.

0  =>  1
1  =>  2
2  =>  4
3  =>  8

index 레지스터는 rex의 x 비트와 합쳐져 4개의 비트가 된다.

base 레지스터는 rex의 b비트와 합쳐져 4개의 비트가 된다.

imm은 숫자인데 modR/m의 mod 에 따라 없을수도 있고 8비트또는 32비트값이 된다.


rex = 48 이고 sib 의 값이 d3 이라면

d3 = 11010011 = 11 010 011 

scale = 11 = 3 ==> 8
index = 0010 = 2 = reg.2 = rdx
base = 0011 = 3 = reg.3 = rbx

[ rbx + rdx*8 + imm] 이 된다.

두개의 예외가 있다.

  • index의 값이 0100 = 4 일때
index와 scale 항목이 없어지고 base항목만 남는다

rex = 48 이고 sib 의 값이 e3 일 경우

e3 = 11100011 = 11 100 011

scale = 11 = 3 ==> 8
index = 0100 = 4  ==> 사라진다.
base = 0011 = 3 = reg.3 = rbx

[ rbx + imm] 이 된다.

  • base의 값이 5 이고 mod의 값이 0 일때
base 항목이 사라지고 그대신에 disp32 를 읽어온다.

예를들면 이런식이다.

8b 04 f5 12 34 56 78

modR/m : 04 
0000 0100 = 00 000 100
mod    : 00 
reg/op : 000 = reg.0 = eax 
r/m    : 100 = 4 (sib)

sib    : 11 110 101 
scale  : 11 = 3 => 8
index  : 110 = 6 = esi
base   : 101 = 5 

mod 가 0 이고 base가 5 이다.
disp32를 읽어온다

mov eax, [esi*8 + 78563412h]


전체실습.(64비트 모드)

4C 8D 4C 24 64 4C 8D 44 24 44 48 8B 94 24 A8 06 00 00

이것을 해석해보자

4C = REX = 0100 1100
8D = LEA Gv,M
4C = modR/M
0100 1100 = 01 001 100
mod = 01 = [base + disp8]
reg/op = 001 => 1001 (rex.r) = 9 = r9
mod != 3 고 r/m의 값이 4이므로 sib필요
24 = sib
0010 0100 = 00 100 100
index가 4이므로 값이 없어진다.
스케일 00은 의미상실
base = 100 = 4 = reg.4 = rsp 
64 = disp8
lea r9, [rsp + 64] 

4C = rex = 0100 1100
8D = lea Gv, M
44 = modR/M
0100 0100 = 01 000 100
mod = 01 = 1 = [base + disp8]
reg/op = 000 => 1000 ( rex.b) = 8 = r8
mod != 3 이고 r/m = 100 = 4 = sib 필요
24 = sib
0010 0100 = 00 100 100
scale = 00 ==> 1
index = 100 ==> 사라짐
base = 100 = reg.4 = rsp
44 = disp8
lea  r8, [rsp + 48]

48  = rex = 0100 1000 
8B = mov Gv, Ev
94 = modR/m 
1001 0100 = 10 010 100
mod = 10 = [base + disp32]
reg/op = 010 = reg.2 = rdx
mod != 3 이고 r/m = 100 = 4 ==> sib 필요
24 = sib 
0010 0100 = 00 100 100
scale = 00 => 1
index = 100 = 4 => 사라짐
base = 100 = 4 = reg.4 = rsp
a8 06 00 00 = disp32
mov rdx, [ rsp + 06a8h]

그림으로 한번 그려봤다.
이건 가장 일반적인 경우.
op/r 인 경우

mod == 0 이고 r/m == 5 인 경우
mod != 3 이고 r/m == 4  sib을 읽어온 경우
index == 4 인 경우
mod == 0 이고 base == 5
 
지금까지 살펴본 것으로 로직을 만들어보면 다음과 같다.

01) 처음 1바이트를 읽는다.
02) 그 값이 [rex,66h,67h] 중 하나인가?
03) 그중 하나라면 해당되는 플래그를 설정한다.
04) 그 다음 1바이트를 읽는다.
05) 1바이트짜리 명령어목록에서 그 값을 찾는다
06) 1.3op 인지를 판단하고 오퍼랜드의 갯수와 성질을 체크한다.
07) 그 다음 바이트를 읽는다.(modR/m)
08) 그 값을 각각 mod, reg , r/m 값으로 쪼갠다
09) 만약 1.3op 라면 reg값으로 명령어를 결정하고 성질에 맞추어 출력한다.
10) (03)에서 설정한 플래그에 따라 reg의 레지스터를 결정한다
11) mod == 0 이고 r/m == 5 이면 32비트를 읽어서 변위값삼고
(rip+disp32) 명령어의 순서에 맞게 명령을 적는다. 끝
12) mod != 3 이고 r/m의 값이 4 이면 그 다음 바이트를 읽는다(sib바이트)
13) sib값을 scale, index, base별로 쪼갠다 
14) base == 5 and mod == 0 이면  베이스플래그를 셋한다.
15) rex의 값을 참고하여 index와 base의 레지스터를 결정한다.
16) index의 값이 4이면 베이스값만 남기고 나머지는 버린다.
명령어의 성질에 맞추어 출력한다. 끝
17) 베이스플래그가 셋되어 있으면 disp32 를 읽어와서
명령어의 성질에 맞추어 출력한다. 끝
18) 모드에 맞추어 변위가 있으면 읽는다.
19) 명령어의 성질에 맞추어 출력한다. 끝


핑백

  • 일상사 : amd64 또는 x64 에서의 기계어 분석 #2 (fpu #1) 2018-01-27 12:07:49 #

    ... 기계어분석 #1</a>] 에서 살펴본대로다. reg 0 는 위 표에 의하면 fadd single-real 이니까 d8 05 12 34 56 78 은 fadd real4 ptr [78563412] 로 된다. d8 84 1e 12 34 56 78 는 어떻게 될까? 84 = 1000 0100 = 10 000 100 =&gt; 모드 2, reg 0, r/m 4 =&gt; sib 필요1e = 00011110 = 00 011 110 =& ... more

덧글

댓글 입력 영역