算术

加法

.intel_syntax noprefix
.global _start
_start:
add rdi, 0x331337

乘法

.intel_syntax noprefix
.global _start
_start:
imul rdi, rsi
add rdi, rdx
mov rax, rdi

除法

intelx86架构的除法div,商存放在rax,余数存放在rdx,在运算之前需要将rdx清空

.intel_syntax noprefix
.global _start
_start:
mov rax, rdi
div rsi
mov rax, rdx

Sizes

将 ax 寄存器的高 8 位设置为 0x42。

.intel_syntax noprefix
.global _start
_start:
mov ah, 0x42

快速实现计算余数

如果我们有“x % y”,并且 y 是 2 的幂,例如 2^n,则结果将是 x 的低 n 位

计算以下内容:
rax = rdi % 256
rbx = rsi % 65536

.intel_syntax noprefix
.global _start
_start:
mov al, dil
mov bx, si

字长

将 rax 设置为 0x404000 处的字节
将 rbx 设置为 0x404000 处的字
设置 rcx设置为位于 0x404000 处的双字
将 rdx 设置为位于 0x404000 处的四字

.intel_syntax noprefix
.global _start
_start:
mov al, [0x404000]
mov bx, [0x404000]
mov ecx, [0x404000]
mov rdx, [0x404000]

从存储在 rdi 中的地址加载两个连续的四字
计算前面步骤四字的总和。
将总和存储在 rsi 中的地址

1个四字等于8个字节

.intel_syntax noprefix
.global _start
_start:
mov r8, [rdi]
mov r9, [rdi+8]
mov [rsi], r8
add [rsi], r9

Bitwise

移位

将 rax 设置为 rdi 的第 5 个最低有效字节。

例如:
rdi = | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 |
将 rax 设置为 B4 的值

rdi共64位(8字节),所以向右移4字节(32位),最低位就是原先的第五个字节

.intel_syntax noprefix
.global _start
_start:
shr rdi, 32
mov al,dil

不使用 MOVXCHG 指令的情况下执行 RAX = RDI AND RSI

.intel_syntax noprefix
.global _start
_start:
and rax, rdi
and rax, rsi

异或

仅使用and,xor,or实现以下逻辑:
if x 为偶数,则
y = 1
else
y = 0

其中:
x = rdi
y = rax

.intel_syntax noprefix
.global _start
_start:
and rdi, 1
xor rax, rax
xor rax, 1
xor rax, rdi

内存

读,写

.intel_syntax noprefix
.global _start
_start:
mov rax, [0x404000]
mov rdi, 0x1337
add [0x404000], rdi

Stack

获取堆栈顶部的值,从中减去 rdi,然后将其放回

.intel_syntax noprefix
.global _start
_start:
pop r8
sub r8, rdi
push r8

不使用 pop,请计算存储在堆栈上的 4 个连续四字的平均值。 将平均值推送到堆栈上。 提示: RSP+0x?? 四字 A RSP+0x?? 四字 B RSP+0x?? 四字 C RSP 四字 D

控制流

绝对跳转

.intel_syntax noprefix
.global _start
_start:
mov r8,0x403000
jmp r8

相对跳转

将代码中的第一条指令设为 jmp
将 jmp 设为从当前位置到 0x51 字节的相对跳转
在相对跳转将重定向控制流的代码位置,将 rax 设置为 0x1

.rept.endr 指令用来重复 nop 操作码

.intel_syntax noprefix
.global _start
_start:
jmp set_rax_to_one
.rept 0x51
nop
.endr
set_rax_to_one:
mov rax, 0x1

间接跳转

实现以下逻辑:
如果 rdi 为 0:
jmp 0x403061
否则,如果 rdi 为 1:
jmp 0x40310d
否则,如果 rdi 为 2:
jmp 0x4031a2
否则,如果 rdi 为 3:
jmp 0x40325a
否则:
jmp 0x403357

假设 rdi 不会为负数
使用不超过 1 个 cmp 指令
使用不超过 3 个跳转(任何变体)
我们将为您提供在 rdi 中“切换”的数字。
我们将为您提供 rsi 中的跳转表基址。

.intel_syntax noprefix
.global _start
_start:
cmp rdi, 4
jge default_case
lea rax, [rsi + rdi*8]
jmp [rax]
default_case:
mov rax,rsi
add rax,0x20
jmp [rax]

实现以下内容:
if [x] is 0x7f454c46:
y = [x+4] + [x+8] + [x+12]
else if [x] is 0x00005A4D:
y = [x+4] - [x+8] - [x+12]
else:
y = [x+4] * [x+8] * [x+12]

其中:
x = rdi,y = rax。

.intel_syntax noprefix
.global _start
_start:
mov eax, [rdi]
cmp eax, 0x7f454c46
je elf_case
cmp eax, 0x00005A4D
je mz_case
jmp default_case

elf_case:
mov eax, [rdi + 4]
add eax, [rdi + 8]
add eax, [rdi + 12]
jmp end
mz_case:
mov eax, [rdi + 4]
sub eax, [rdi + 8]
sub eax, [rdi + 12]
jmp end
default_case:
mov eax, [rdi + 4]
imul eax, [rdi + 8]
imul eax, [rdi + 12]
end:
nop

Exam

For循环

请计算 n 个连续四字的平均值,其中:
rdi = 第一个四字的内存地址
rsi = n(循环次数)
rax = 计算的平均值

.intel_syntax noprefix
.global _start
_start:
xor rax, rax
mov rcx, rsi

average_loop:
mov rdx, [rdi]
add rax, rdx
add rdi, 8
dec rcx
jnz average_loop

xor rdx, rdx
div rsi

While循环

计算连续内存区域中连续的非零字节数,其中:
rdi = 第一个字节的内存地址
rax = 连续非零字节数

此外,如果 rdi = 0,则设置 rax = 0

.intel_syntax noprefix
.global _start
_start:
xor rax, rax
xor rcx, rcx
cmp rdi, 0
jz done

count_non_zero:
mov bl, [rdi]
cmp bl, 0
jz done
inc rcx
inc rdi
jmp count_non_zero

rdi_is_zero:
mov rcx, 0
jmp done

done:
mov rax,rcx

函数

函数使用指令“call”和“ret”。

“call”指令将下一条指令的内存地址推送到堆栈上,然后跳转到存储在第一个参数中的值。

ret 从堆栈中弹出顶部值并跳转到该值。

请实现以下逻辑:

str_lower(src_addr):
i = 0
if src_addr != 0:
while [src_addr] != 0x00:
if [src_addr] <= 0x5a:
[src_addr] = foo([src_addr])
i += 1
src_addr += 1
return i

foo 在 0x403000 处提供。
foo 将单个参数作为值并返回一个值。

所有函数(foo 和 str_lower)都必须遵循 Linux amd64 调用约定(也称为 System V AMD64 ABI):
https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI

因此,您的函数 str_lower 应该在 rdi 中查找 src_addr,并将函数返回放在 rax 中。

需要注意的是,src_addr 是内存中的地址(字符串所在的位置),而 [src_addr] 指的是 src_addr 中存在的字节。

因此,函数 foo 接受一个字节作为其第一个参数并返回一个字节

.intel_syntax noprefix
.global _start
_start:
xor rcx,rcx
xor rdx,rdx
mov rdx, rdi
test rdi,rdi
jz done

loop:
mov bl,[rdx]
test bl,bl
jz done

cmp bl,0x5a
jg continue

mov rax, 0x403000
xor rdi, rdi
mov dil, bl
call rax
mov [rdx], al
inc rcx

continue:
inc rdx
jmp loop

done:
mov rax,rcx
ret