9. 指令解析


上一页 返回目录 下一页

 

一、 assemble

  这里我想写一段汇编代码,作为源代码,然后手工译为机器编码

1、 assembly 源代码

这段是摘自我的 mouseOS 里面的一段代码 kstrcmp() 用来比较这个字符串是否相等:

;-------------------------------------------------------
; long kstrcmp(const char *s1, const char *s2);
; input:         rdi: s1     rsi: s2
; return value:    1: equal      0: not equal
;-------------------------------------------------------
__kstrcmp:

  xor rax, rax

kstrcmp_loop: 

  movzx r10, byte [rdi]
  movzx r11, byte [rsi]
  cmp r10, r11
  jnz kstrcmp_done
  test r10, r10
  jz kstrcmp_eql
  inc rdi
  inc rsi
  jmp kstrcmp_loop

kstrcmp_eql:

  inc rax

kstrcmp_done:
  ret


2、 encodes (机器编码)

下面是我完全用人工译出来的机器码:

0:        48 31 c0
3:        4c 0f b6 17
7:        4c 0f b6 1e
b:        4d 39 da
e:        75 10     * (jnz kstrcmp_done)
10:       4d 85 d2
13:       74 08    * (jz kstrcmp_eql)
15:       48 ff c7
18:       48 ff c6
1b:       eb e6     * (jmp kstrcmp_loop)
1d:       48 ff c0
20:       c3

下面再看一看 nasm 编译的情况:

00000000  4831C0            xor rax,rax
00000003  4C0FB617          movzx r10,byte [rdi]
00000007  4C0FB61E          movzx r11,byte [rsi]
0000000B  4D39DA            cmp r10,r11
0000000E  7513              jnz 0x23    * (jnz kstrcmp_done)
00000010  4D85D2            test r10,r10
00000013  740B              jz 0x20    * (jz kstrcmp_eql)
00000015  48FFC7            inc rdi
00000018  48FFC6            inc rsi
0000001B  E9E3FFFFFF        jmp dword 0x3    * (jmp kstrcmp_loop)
00000020  48FFC0            inc rax
00000023  C3                ret

 

对比 nasm 编译出来的机器码,不难发现 nasm 并不能编译出最优秀的代码:

(1)nasm 译出 0x23 个 bytes,比我手工译出来的要多 3 个 bytes。

(2)nasm 对于指令 jmp kstrcmp_loop 译为 5 bytes,而实际上只需要译为 2 bytes 就行了。

  这是因为 nasm 使用了 32 位的 immediate 值,而我使用了 8 位的 immediate 值。

(3)由于上面这条指令的译法与我的译法不同,从而导致了 3 条 jmp 指令都不同。

 

3、 指令 jnz kstrcmp_done 解析

  指令 jnz 的 Opcode 是 75,它的描述为 JNZ Jb,它的 operand 是基于 rip 的 8 位 offset 值。

  这个 offset 的计算方法是: target = rip + offset  所是: offset = target - rip

★ 由上所得:当前的 rip 为 0x10,而标号 kstrcmp_done 的地址 0x20

★ 所以: offset = target - rip = 0x20 - 0x10 = 0x10

  因此:它的 encode 为:75 10

 

 

二、 decode

 1、这是一段摘自 windows 7 的一段机器码:

fffff800`026ede60   59 65 48 8b 0c 25 20 00 00 00 80 79 20 01 77 72
fffff800`026ede70   0f 31 48 c1 e2 20 48 0b c2 48 2b 81 c0 67 00 00 
fffff800`026ede80   48 01 81 f8 67 00 00 48 01 81 c0 67 00 00 48 8b

2、看一看它到底是什么代码:

下面我用人工译码:

fffff800_026ede60:    59                           pop rcx
fffff800_026ede61:    65 48 8b 0c 25 20 00 00 00   mov rcx, qword ptr gs:[0x20]
fffff800_026ede6a:    80 79 20 01                  cmp byte ptr [rcx + 0x20], 0x01
fffff800_026ede6e:    77 72                        ja .+0x72    *(.表示当前的 rip 值)
fffff800_026ede70:    0f 31                        rdtsc
fffff800_026ede72:    48 c1 e2 20                  shl rdx, 0x20
fffff800_026ede76:    48 0b c2                     or rax, rdx
fffff800_026ede79:    48 2b 81 c0 67 00 00         sub rax, qword ptr [rcx + 0x67c0]
fffff800_026ede80:    48 01 81 f8 67 00 00         add qword ptr [rcx + 0x67f8], rax
fffff800_026ede87:    48 01 81 c0 67 00 00         add qword ptr [rcx + 0x67c0], rax
fffff800_026ede8e:    48 8b ... ...

这里只是纯作译码工作,这里看不出这段代码作用。

 

下面再看看 ndisasm 译的结果:

00000000  59                pop rcx
00000001  65488B0C25200000  mov rcx,[gs:0x20]
         -00
0000000A  80792001          cmp byte [rcx+0x20],0x1
0000000E  7772              ja 0x82    *(0x10+0x72=0x82)
00000010  0F31              rdtsc
00000012  48C1E220          shl rdx,0x20
00000016  480BC2            or rax,rdx
00000019  482B81C0670000    sub rax,[rcx+0x67c0]
00000020  480181F8670000    add [rcx+0x67f8],rax
00000027  480181C0670000    add [rcx+0x67c0],rax
0000002E  48                rex.w
0000002F  8B                db 0x8b

这里除了显示格式不同外, 结果是完全一样的。

 

 

3、来看一看这个 encode:65 48 8b 0c 25 20 00 00 00 应该怎么 decode

(1)65 它是一个 prefix,是 GS - segment override prefix

(2)48 它是一个 REX prefix,REX.W = 1 使用 64 位的 operands,而 REX.R = REX.X = REX.B = 0

(3)8b 它是 Opcode 码,表示:MOV Gv, Ev 即:frist operand 是 registers 而 second operann 是 register/memory,

  operand size 是 effective operand size,到目前为止,还不知道 operand 是什么

(4)0c 紧接着 Opcode 码,那么,它就是 ModRM 字节, ModRM = 00-001-100

  它提供的 register 是 rcx, ModRM.r/m 提供的寻址是 100 这是 [SIB] 字节,引导出 SIB,因此,下一字节必定是 SIB 字节。

(5)25 由第 (4)步得出,它是个 SIB 字节, SIB = 00-100-101 代表 scale = 00,index = 100, base = 101

  由于 index = 100(rsp),因此这里不存在 index 部分,只有 base 部分。

  base = 101(rbp),因此它将代表一个 disp32 值,那么,下面接下来的 4 个 bytes 就是 displacement 值

(6)20 00 00 00 这是由第(5)得知,这 4 个 bytes 是 displacement。

----------------------------------------------------------------------------------------

经过上面 6 个步聚的分析,得出最终的汇编指令是:mov rcx, qword ptr gs:[0x20]

 

 

上一页 返回目录 下一页


mik(deng zhi)写于 2008-11-26 15:40