pwn.college: Assembly Crash Course

你知道的,我的汇编全是靠它学的。

Assembly Crash Course

1. set-register

要求:rdi = 0x1337

1
2
3
4
.intel_syntax noprefix
.global _start
_start:
mov rdi, 0x1337

2. set-multiple-registers

In this level, you will work with multiple registers. Please set the following:

  • rax = 0x1337
  • r12 = 0xCAFED00D1337BEEF
  • rsp = 0x31337
1
2
3
4
5
6
.intel_syntax noprefix
.global _start
_start:
mov rax, 0x1337
mov r12, 0xCAFED00D1337BEEF
mov rsp, 0x31337

3. add-to-register

Do the following:

  • Add 0x331337 to rdi
1
2
3
4
.intel_syntax noprefix
.global _start
_start:
add rdi, 0x331337

4. linear-equation-registers

1
2
3
4
5
6
7
.intel_syntax noprefix
.global _start
_start:
imul rdi, rsi
add rdx, rdi
mov rax, rdx

5. integer-division

1
2
3
4
5
.intel_syntax noprefix
.global _start
_start:
mov rax, rdi
div rsi

6. modulo-operation

div操作之后,余数会被存于rdx(dx)中。

1
2
3
4
5
6
.intel_syntax noprefix
.global _start
_start:
mov rax, rdi
div rsi
mov rax, rdx

7. set-upper-byte

1
2
3
4
5
6
7
8
9
10
MSB                                    LSB
+----------------------------------------+
| rax |
+--------------------+-------------------+
| eax |
+---------+---------+
| ax |
+----+----+
| ah | al |
+----+----+

Using only one move instruction, please set the upper 8 bits of the ax register to 0x42.

1
2
3
4
.intel_syntax noprefix
.global _start
_start:
mov ah, 0x42

8. efficient-modulo

Using only the following instruction(s):

  • mov

Please compute the following:

  • rax = rdi % 256
  • rbx = rsi % 65536
1
2
3
4
5
6
7
.intel_syntax noprefix
.global _start
_start:
mov rcx, rdi
mov al, cl
mov rdx, rsi
mov bx, dx

9. byte-extraction

shl指令:

1
2
3
;rax = 10001010
shl al, 1
;al = 00010100

exp:

1
2
3
4
5
6
.intel_syntax noprefix
.global _start
_start:
shr rdi, 32
mov rbx, rdi
mov al, bl

10. bitwise-and

如果不使用以下说明:movxchg,请执行以下操作:
rax 设置为 (rdi AND rsi) 的值

1
2
3
4
5
6
.intel_syntax noprefix
.global _start
_start:
and rdi, rsi
and rax, 0
xor rax, rdi

11. check-even

1
2
3
4
5
6
.intel_syntax noprefix
.global _start
_start:
and rdi, 1
and rax, 1
xor rax, rdi

12. memory-read

请执行以下操作:将存储在 0x404000 的值放入 rax 中。确保 rax 中的值是存储在 0x404000 的原始值。

1
2
3
4
5
.intel_syntax noprefix
.global _start
_start:
mov rax, [0x404000]

13. memory-write

1
2
3
4
5
.intel_syntax noprefix
.global _start
_start:
mov [0x404000], rax

14. memory-increment

1
2
3
4
5
6
7
.intel_syntax noprefix
.global _start
_start:
mov rax, [0x404000]
mov rbx , rax
add rbx , 0x1337
mov [0x404000], rbx

15. byte-access

Here is the breakdown of the names of memory sizes:
以下是内存大小名称的细分:

  • Quad Word = 8 Bytes = 64 bits
    四字 = 8 字节 = 64 位
  • Double Word = 4 bytes = 32 bits
    双字 = 4 字节 = 32 位
  • Word = 2 bytes = 16 bits
    字 = 2 字节 = 16 位
  • Byte = 1 byte = 8 bits
    字节 = 1 字节 = 8 位
1
2
3
4
.intel_syntax noprefix
.global _start
_start:
mov al, [0x404000]

在 x86_64 中,您可以在取消引用地址时访问这些大小,就像使用更大或更小的 register 访问一样:

  • mov al, [address] <=> moves the least significant byte from address to rax
    mov al, [address] <=> 将最低有效字节从 address 移动到 rax
  • mov ax, [address] <=> moves the least significant word from address to rax
    mov ax, [address] <=> 将最低有效字从 address 移动到 rax
  • mov eax, [address] <=> moves the least significant double word from address to rax
    mov eax, [address] <=> 将最低有效双字从 address 移动到 rax
  • mov rax, [address] <=> moves the full quad word from address to rax
    mov rax, [address] <=> 将完整的四元字从 address 移动到 rax

16. memory-size-access

1
2
3
4
5
6
7
.intel_syntax noprefix
.global _start
_start:
mov al, [0x404000]
mov bx, [0x404000]
mov ecx, [0x404000]
mov rdx, [0x404000]

17. little-endian-write

值得注意的是,值的存储顺序与我们表示它们的顺序相反。example:

1
2
3
4
5
6
7
8
9
10
;[0x1330] = 0x00000000deadc0de
; 检查它在内存中的实际存储情况:
;[0x1330] = 0xde
;[0x1331] = 0xc0
;[0x1332] = 0xad
;[0x1333] = 0xde
;[0x1334] = 0x00
;[0x1335] = 0x00
;[0x1336] = 0x00
;[0x1337] = 0x00

这就是小端存储(Little Endian)。

exp

1
2
3
4
5
6
7
.intel_syntax noprefix
.global _start
_start:
mov rax, 0xdeadbeef00001337
mov [rdi], rax
mov rax, 0xc0ffee0000
mov [rsi], rax

18. memory-sum

内存是连续存储的,因此可以使用偏移量来获取指定字节。例如:

1
2
3
4
5
6
7
8
;[0x1337] = 0x00000000deadbeef
;[0x1337] = 0xef
;[0x1337 + 1] = 0xbe
;[0x1337 + 2] = 0xad
...
;[0x1337 + 7] = 0x00
; 假设我需要访问某个地址的第5个字节。那么可以:
mov al, [address+4]

题目要求:

  • Load two consecutive quad words from the address stored in rdi.
    从存储在 rdi 中的地址加载两个连续的四字。
  • Calculate the sum of the previous steps’ quad words.
    计算前面步骤的四边形词的总和。
  • Store the sum at the address in rsi.
    将总和存储在 rsi 中的地址。
1
2
3
4
5
6
7
.intel_syntax noprefix
.global _start
_start:
mov rax, [rdi]
mov rbx, [rdi+8]
add rax, rbx
mov [rsi], rax

19. stack-substraction

1
2
3
4
5
6
.intel_syntax noprefix
.global _start
_start:
pop rax
sub rax, rdi
push rax

20. swap-stack-values

1
2
3
4
5
6
7
.intel_syntax noprefix
.global _start
_start:
push rdi
push rsi
pop rdi
pop rsi

21. average-stack-values

除法还是很坑的,记得一定是rdx:rax / reg,商在rax中,余数在rdx中。因此,在调用div的时候一定要清空rdx寄存器(如果被除数没有占用到rdx的话)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
.intel_syntax noprefix
.global _start
_start:
mov rdx, [rsp]
mov rcx, [rsp+0x8]
mov rbx, [rsp+0x10]
mov rax, [rsp+0x18]
add rax, rbx
add rax, rcx
add rax, rdx
mov rbx, 4
mov rdx, 0
div rbx
push rax

22. absolute-jump

绝对跳转,指的是跳转到指定地址

1
2
3
4
5
.intel_syntax noprefix
.global _start
_start:
mov rax,0x403000
jmp rax

23. relative-jump

对于所有跳转,有三种类型:

  • Relative jumps: jump + or - the next instruction.
    相对跳转:jump + 或 - 下一条指令。
  • Absolute jumps: jump to a specific address.
    Absolute jumps:跳转到指定地址。
  • Indirect jumps: jump to the memory address specified in a register.
    Indirect jumps:跳转到 register 中指定的 memory 地址。

jmp (reg1 | addr | +/-offset)

要求:

  • Make the first instruction in your code a jmp.
    将代码中的第一条指令设为 jmp
  • Make that jmp a relative jump to 0x51 bytes from the current position.
    使该 jmp 相对跳转到 0x51 字节的当前位置。
  • At the code location where the relative jump will redirect control flow, set rax to 0x1.
    在相对跳转将重定向控制流的代码位置,将 rax 设置为 0x1。
1
2
3
4
5
6
7
8
9
.intel_syntax noprefix
.global _start
_start:
jmp set_rax
.rept 0x51
nop
.endr
set_rax:
mov rax,0x1

24. jump-trampoline

1
2
3
4
5
6
7
8
9
10
11
.intel_syntax noprefix
.global _start
_start:
jmp set_rax
.rept 0x51
nop
.endr
set_rax:
pop rdi
mov rbx, 0x403000
jmp rbx

25. conditional-jump

挖草,巨坑的一点!

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
.intel_syntax noprefix
.global _start
_start:
mov eax, [rdi]
cmp eax, 0x7f454c46
je flag_1
cmp eax, 0x00005A4D
je flag_2
jmp flag_3

flag_1:
mov eax, [rdi+4]
add eax, [rdi+8]
add eax, [rdi+12]
jmp done
flag_2:
mov eax, [rdi+4]
sub eax, [rdi+8]
sub eax, [rdi+12]
jmp done
flag_3:
mov eax, [rdi+4]
imul eax,[rdi+8]
imul eax, [rdi+12]
jmp done
done:

注意的是mov eax, [rdi],如果用mov rax, [rdi]会出问题,因为会读取rdi地址上的pword,那么永远走的都是else下的语句。服了。

26. indirect-jump

1
2
3
4
5
6
7
8
9
10
11
12
13
.intel_syntax noprefix
.global _start
_start:
mov rax, rdi
cmp rax, 3
jg default
imul rax,8
add rax, rsi
jmp [rax]
default:
mov rax, rsi
add rax, 0x20
jmp [rax]

这里其实也有坑,需要好好把控[]会解析地址。例如下面的exp:

1
2
3
4
5
6
7
8
9
10
11
.intel_syntax noprefix
.global _start
_start:
mov rax, rdi
cmp rax, 3
jg default
mov rax, [rsi + rdi * 8]
jmp rax
default:
mov rax, [rsi + 0x20]
jmp rax

27. average-loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
.intel_syntax noprefix
.global _start
_start:
mov rcx, 0
mov rax, 0
mov rbx, rdi
loop_start:
add rax, [rbx]
inc rcx
add rbx, 0x8
cmp rcx, rsi
jg loop_end
jmp loop_start
loop_end:
mov rbx, rsi
div rbx

依然是要注意[]的使用,要认知是取内存指向的值,还是取内存进行加减。

28. count-non-zero

感觉主要问题是,我没理解题目的意思。理解后就很简单了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.intel_syntax noprefix
.global _start
_start:
mov rax, 0
loop_start:
cmp rdi, 0
je loop_end
mov bl, [rdi]
cmp bl, 0
je loop_end
inc rdi
inc rax
jmp loop_start

loop_end:

29. string lower

这个还行,一遍过了。还记得系统调用的时候,第一个参数是用rdi存储,第二个参数是用rsi存储

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
28
29
.intel_syntax noprefix
.global _start
_start:
mov rbx, 0
cmp rdi, 0
jne loop_start
jmp done
loop_start:
mov cl, [rdi]
cmp cl, 0x00
je done
cmp cl, 0x5a
jle exe_1
inc rdi
jmp loop_start
exe_1:
mov rcx, rdi
mov rax, 0x403000
mov rdi, [rdi]
call rax
mov rdi, rcx
mov [rdi], rax
inc rbx
inc rdi
jmp loop_start
done:
mov rax, rbx
ret

30. most-common-byte

这道题卡了半天吧得,主要是对寄存器不熟悉,重复用了一些寄存器,然后导致出现问题,后面用普通寄存器r8~15解决的,普通寄存器也得用,不然变量不够存的。

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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
.intel_syntax noprefix
.global _start
_start:
push rbp
mov rbp, rsp
sub rsp, 0x200
mov rcx, 0

loop_count_bytes:
cmp rcx, rsi
jge loop_count_bytes_end

mov dl, [rdi + rcx]
movzx rax, dl
mov rbx, rbp
sub rbx, rax
inc byte ptr [rbx]
inc rcx
jmp loop_count_bytes

loop_count_bytes_end:
mov rcx, 0
mov rdx, 0
mov rax, 0

loop_find_max:
cmp rcx, 0xff
jg loop_find_max_end
mov rbx, rbp
sub rbx, rcx
mov r8b, [rbx]
movzx rbx, r8b
cmp rbx, rdx
jle skip_update
mov rdx, rbx
mov rax, rcx
inc rcx
jmp loop_find_max

skip_update:
inc rcx
jmp loop_find_max

loop_find_max_end:
mov rsp, rbp
pop rbp
ret


pwn.college: Assembly Crash Course
https://loboq1ng.github.io/2024/08/29/pwn-college-Assembly-Crash-Course/
作者
Lobo Q1ng
发布于
2024年8月29日
许可协议