汇编基础

汇编基础

64位编程

之前介绍的一直都是32位的程序,64位和32位有很多相似的地方,但仍有许多的不同:
比如64位MASM 11.0不支持INVOKE伪指令,而且不需要在程序前写出.model,.stack,.386的

寄存器

64位模式下,操作数的默认大小是32位,并且有8个通用的寄存器。但是给每条指令加上REX(寄存器扩展)前缀后,操作数也可达64位。通用寄存器的数量也增加到了16个:32位模式下的寄存器再加上8个有标号的寄存器,R8~R15

  • 64位模式下,单条指令不能同时访问寄存器的高字节,如AH,BH,CH以及新寄存器的低字节DIL
  • 64模式下,32位的EFLAGS被64位的RFLAGS取代。这两个寄存器共享低32位,而RFLAGS的高32位是不使用的
  • 32位和64位拥有相同的状态标志位

    加法减法

    SumArray_64.asm
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    ;数组求和
    ExitProcess PROTO
    .data
    intArray QWORD 1000000000000h, 20000000000000h
    QWORD 3000000000000h, 40000000000000h
    .code
    main PROC
    mov rdi, OFFSET intArray
    mov rcx, LENGTHOF intArray
    mov rax, 0
    L1:
    add rax, [rdi]
    add rdi, TYPE intArray
    loop L1
    mov ecx, 0 ;ExitProcess的返回值
    call ExitProcess
    main ENDP
    END
    需要注意的是操作数的大小,当操作数只使用部分寄存器的时候,其余位置不会被修改
    同时,使用OFFSET运算符将产生64位地址,必须用64位寄存器或者变量来保存

64位调用规范

  • 如果想要调用自己写的子程序或者Irvine64链接库的子程序,则程序员需要在自己的程序顶部用PROTO伪指令来指定所有在本程序外同时又会被调用的过程
    1
    2
    ExitProcess PROTO   ;Windows API
    WriteHex64 PROTO ;Irvine64链接库
  • 由于地址长为64位,因此CALL指令把RSP(堆栈指针)寄存器的值减去8
  • 长度不足64位的参数不进行零扩展,因此其高位的值是不确定的
  • 如果返回值的长度小于或者等于64位,那么它必须放在RAX寄存器中
  • 主调者要负责在堆栈中分配至少32字节的影子空间,以便被调用的子程序可以选择将寄存器保存在这个区域中
  • 调用子程序时,堆栈指针(RSP)必须对齐16字节边界。CALL指令将8字节的返回地址压入堆栈,因此,主调程序出了把堆栈指针减去32以便存放寄存参数之外,还要减去8
  • 大于64的返回值存放于运行时的堆栈,由RCX指出其位置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
ExitProcess PROTO
WriteInt64 PROTO
Crlf PROTO

.code
main PROC
sub rsp, 8 ;对准堆栈指针
sub rsp, 20h ;影子参数
mov rcx, 1
mov rdx, 2
mov r9, 3
mov r8, 4
call AddFour ;RAX中返回值
call WriteInt64
call Crlf
mov ecx, 0
call ExitProcess
main ENDP

AddFour PROC
mov rax, rcx
add rax, rdx
add rax, r8
add rax, 19
ret
AddFour ENDP
END

64位模式下的布尔指令

大多数情况下,64位模式中的64位指令与32位模式中的操作是一样的。比如,如果源操作数是常数,长度小于32位,而目的操作数使一个64位的寄存器或者内存操作数,那么目的操作数的所有位都会受到影响

1
2
3
4
5
6
7
8
9
10
11
.data
allones QWORD 0FFFFFFFFFFFFFFFFh
.code
mov rax, allones ;RAX=FFFFFFFFFFFFFFFF
and rax, 80h ;RAX=0000000000000080
mov rax, allones
and rax, 8080h ;RAX=0000000000008080
mov rax, allones
and rax, 808080h ;RAX=0000000000808080
mov rax, allones
and rax, 80808080h ;RAX=FFFFFFFF80808080

我们发现如果操作数是32位常数或寄存器,那么目的操作数,只有低32位会受到影响
这个同样适用于内存操作数,显然32位操作数是一种特殊的情况,需要和其他大小的操作数分开考虑

Author

Ctwo

Posted on

2019-08-12

Updated on

2020-10-25

Licensed under

Comments