Assembly Crash Course-pwn.college(持续更新 level26)
本文最后更新于 112 天前,其中的信息可能已经有所发展或是发生改变。
少看多做
少说多听
由于题目可能更新,目前2024年5月22日有效

level1

Set a register(设置一个寄存器)

题目描述中提到要让rdi的值为0x1337,直接输入mov rdi, 0x1337

Screenshot 2023 09 06 185735.png-

回显需要输入bin二进制文件

使用python的pwntools进行构造,context.arch必须设置,否则asm不会解析汇编为当前架构

运行代码

from pwn import *
context.arch='amd64'
p=process('/challenge/run')
p.recvline()
p.send(asm('mov rdi,0x1337'))
print(p.readallS())
Screenshot 2023 09 06 190314.png-1

level2

Set multiple registers(设置多个寄存器)

level3.png-2

使用三行代码分别赋值给rax,r12,rsp

from pwn import *
context.arch='amd64'
p=process('/challenge/run')
p.recvline()
p.send(asm('''
mov rax, 0x1337
mov r12, 0xCAFED00D1337BEEF
mov rsp, 0x31337'''))
print(p.recvallS())
Screenshot 2024 01 18 091559.png-3

level3

Addition (加法)

Screenshot 2023 09 06 205434.png-4

题目描述要求add 0x331337 to rdi就是add rdi,0x331337

from pwn import *
 
context.arch = 'amd64'
p = process('/challenge/run')
 
p.recvline()
p.send(asm('''
    add rdi, 0x331337'''))
print(p.recvallS())
Screenshot 2023 09 06 205708.png-5

level4

Multiplication (乘法)

assembly level3.png-6

前面这些是教如何编译汇编,把二进制程序输入到程序中,我们不用这么麻烦,直接使用python的pwntools库,来执行代码

assembly level3 2.png-7

题目让我们实现mx+b,就是m乘x加b,结果存在rax里

使用imul进行乘法运算,imul和mul区别一个有符号,一个无符号,imul rdi, rsi等同于rdi = rdi * rsi

由于题目给了值,所以只需要运算即可

from pwn import *

context.arch='amd64'
p=process('/challenge/run')

p.recvline()
p.send(asm('''
    xor rax,rax
    imul rdi,rsi
    mov rax,rdi
    add rax,rdx'''))
print(p.recvallS())
assembly level3 3.png-8

level5

Division (除法)

assembly level4 1.png-9

div rsi代表 rax = rax / rsi,如果rax和rsi都为64bit,则余数rdx为0,如果rax为128bit,则存在rdx余数,本题distance为64bit

题目要求speed = distance / time,速度=路程/时间,其中路程和时间题里已经给了

在使用未初始化的rax或rdx时,记得xor rax, rax初始化寄存器,否则数据会错。

from pwn import *
context.arch='amd64'

p=process('/challenge/run')

p.recvline()
p.send(asm('''
    xor rax,rax
    mov rax,rdi
    div rsi
    '''))
print(p.recvallS())
assembly level4 2.png-10

level6

Modulus (模数)

assembly level5 1.png-11

模是除法中的余,10 / 3 = 3 …… 1,就是10 % 3 = 1,1是模数,在大多数编程语言中%为模运算

题目要求我们求模rax = rax / <reg> ...... rdx

由于x86汇编没有求模指令,使用div除法求余数

from pwn import *

context.arch='amd64'
p=process('/challenge/run')
p.recvline()
p.send(asm('''
    xor rax, rax
    mov rax, rdi
    div rsi
    xor rax, rax
    mov rax, rdx
    '''))
print(p.recvallS())

在x86架构汇编语言中,rax寄存器通常是函数的返回值,所以把rdx放到rax里

assembly level5 2.png-12

level7

Register sizes (寄存器大小)

assembly level6 1.png-13

题目要求仅使用mov指令实现模运算,模除256,相当于只保留64位寄存器的低8位,模除65536,相当于只保留寄存器的16位

assembly level6 2.png-14
Screenshot 2024 01 18 102210.png-15

常用寄存器

x64调用约定64位寄存器32位寄存器16位寄存器8位寄存器(低)8位寄存器(高)
函数返回值raxeaxaxalal
rbxebxbxblbl
第四个参数rcxecbcxclcl
第三个参数rdxedxdxdldl
第一个参数rdiedididil
第二个参数rsiesisisil
下一条指令的地址ripeipip
栈底指针rbpebpbpbpl
栈顶指针rspespspspl
第五个参数r8r8dr8wr8b
第六个参数r9r9dr9wr9b
以此类推
from pwn import *
context.arch='amd64'

p=process('/challenge/run')
p.recvline()
p.send(asm('''
    mov ah, 0x42
    '''))
print(p.recvallS())

level8

Register sizes for modulus(模数的寄存器大小)

Screenshot 2024 01 18 105202.png-16

题目要我们求模,但是限制我们只能使用mov指令,不过只要除数是2的幂数,例如2^n,模数就是低n位

from pwn import *
context.arch='amd64'

p=process('/challenge/run')
p.recvline()
p.send(asm('''
    mov al, dil
    mov bx, si
    '''.strip()))
print(p.recvallS())
Screenshot 2024 01 18 110044.png-17

level9

Bitwise shift (位移)

assembly level7 1.png-18

shl rax, 1意思是rax左移1位

例如rax = 10010001 01011001 11001100 10100101 11001010 00111100 10101010 01011010

结果rax = 00100010 10110011 10011001 01001011 10010100 01111001 01010100 10110100

最高位遗弃,最低位置零。shl rax, rbx意思是rax左移rbx的值,rbx为8则左移8位

题目只能用shl(左移)shr(右移)mov

assembly level7 1.png-18

题目要求让rax的值为B4,就要让rdi先左移3字节(24位),把B4移到最左边,再让rdi右移7字节(56位),把B4移到最右边,这样rdi的值就为B4了,直接赋值给rax就行

1byte(字节) = 8bits(比特/位)4位二进制 = 1位十六进制

from pwn import *
context.arch='amd64'

p=process('/challenge/run')
p.recvline()
p.send(asm('''
    shl rdi,24
    shr rdi,56
    mov rax,rdi
    '''.strip()))
print(p.recvallS())
assembly level7 2.png-19

level10

Bitwise and (位与)

与运算 全1为1,有0出0
或运算 有1出1,全0出0
异或运算 相异为1,相同为0

assembly level8 2.png-20

例:rax = 10101010
rbx = 00110011
and rax, rbx
; rax = 00100010

assembly level8 3.png-21

题目限制不能用mov,可以用or来赋值,先把rax清零,再把rax和任意值或运算,就能把任意值赋值给rax

from pwn import *
context.arch='amd64'

p=process('/challenge/run')
p.recvline()
p.send(asm('''
    and rdi, rsi
    xor rax, rax
    or rax, rdi
    '''))
print(p.recvallS())
assembly level8 4.png-22

level11

Bitwise logic (位逻辑运算)

assembly level9 1.png-23

只能使用and, or, xor来判断和赋值

我们可以对x和1进行与运算,如果x为奇数则结果为1,如果x为偶数则结果为0,再把结果和1进行异或,最终结果为奇数则为0,为偶数则为1

from pwn import *
context.arch='amd64'

p=process('/challenge/run')
p.recvline()
p.send(asm('''
   xor rax, rax
   and rdi, 1
   xor rdi, 1
   or rax, rdi
   '''))
p.interactive()
assembly level9 2.png-24

level12

Memory reads(内存读取)

Screenshot 2024 01 19 013356.png-25

0x404000是值,[0x404000]是0x404000地址里的值

题目要求我们把0x404000地址里的值赋值给rax

from pwn import *
context.arch='amd64'

p=process('/challenge/run')
p.recvline()
p.send(asm('''
    xor rax, rax
    mov rax, [0x404000]
    '''))
print(p.recvallS())
Screenshot 2024 01 19 013501.png-26

level13

Memory writes(内存写入)

Screenshot 2024 01 19 013822.png-27

题目要求把rax里的值放到0x404000中,也就是[0x404000],很简单,构造代码

from pwn import *
context.arch='amd64'

p=process('/challenge/run')
p.recvline()
p.send(asm('''
    xor rdi, rdi
    mov rdi, 0x404000
    mov [rdi], rax
    '''))
print(p.recvallS())
Screenshot 2024 01 19 014050.png-28

level14

Memory reads and writes (内存读取和写入)

assembly level10 1.png-29

题目要求我们把0x404000地址里的值赋值给rax,再把0x404000地址里的值增加0x1337

不能直接mov [0x404000], 0x1337,地址里的值不能用立即数赋值,可以通过寄存器赋值,例如mov [0x404000], rbx

更多请自行搜索 汇编的寻址模式和过程调用约定

from pwn import *
context.arch='amd64'

p=process('/challenge/run')
p.recvline()
p.send(asm('''
    mov rax, [0x404000]
    mov rbx, [0x404000]
    add rbx, 0x1337
    mov [0x404000], rbx
    '''))
print(p.recvallS())
assembly level10 2.png-30

level15

Read one size data(读取特定大小的数据)

Screenshot 2024 01 21 174851 1024x303.png-31

四字 = 8字节 = 64bits
双字 = 4字节 = 32bits
字 = 2字节 = 16bits
1字节 = 8bits

rax = 四字
eax = 双字
ax = 字
al = 字节

Screenshot 2024 01 21 175556.png-32

题目要求rax的值为0x404000地址里的字节值,直接mov al, [0x404000],更改操作数的大小就行

from pwn import *
context.arch='amd64'

p=process('/challenge/run')
p.recvline()
p.send(asm('''
    xor rax, rax
    mov al, [0x404000]
    '''))
print(p.recvallS())
Screenshot 2024 01 21 175948.png-33

level16

Read multiple data sizes(读取多种数据大小)

Screenshot 2024 01 21 180723.png-34

就像上一题说的那样,依次按大小赋值

from pwn import *
context.arch='amd64'

p=process('/challenge/run')
p.send(asm('''
   mov al,[0x404000]
   mov bx,[0x404000]
   mov ecx,[0x404000]
   mov rdx,[0x404000]'''))
print(p.recvallS())
Screenshot 2024 01 21 181150.png-35

level17

Dynamic address memory writes(动态地址内存写入)

Screenshot 2024 01 22 230115 1024x370.png-36

数据在内存中是以小端序(little endian)存储的

不过这题没啥用

image 6 1024x287.png-37

直接赋值就行了

from pwn import *
context.arch='amd64'

p=process('/challenge/run')
p.recvline()
p.send(asm('''
    mov rax,0xdeadbeef00001337
    mov [rdi],rax
    mov rax,0xc0ffee0000
    mov [rsi],rax'''))
print(p.recvallS())
image 7.png-38

level18

Consecutive memory reads(连续内存读取)从这里开始vscode换成深色模式了

Screenshot 2024 02 22 133950.png-39

题目要求我们从rdi读取两个相邻的四字数据并相加,存到rsi的值里

from pwn import *
context.arch='amd64'

p=process('/challenge/run')
p.recvline()
p.send(asm('''
    mov rax,[rdi]
    add rax,[rdi+8]
    mov [rsi],eax'''))
print(p.recvallS())

先读取rdi,在读取rdi+8,正好是两个相邻的四字

image 8.png-40

level19

The stack(栈)

Screenshot 2024 02 22 135708 1024x313.png-41

寄存器是有限的,当要存储的数据量超过了cpu自带的寄存器的数量,就要使用栈来存储数据。
任何程序都是由函数组成的,栈则是函数运行时临时使用的一块内存。

rsp是栈顶指针寄存器,总是指向新入栈的数据地址,rbp是栈(底/基)指针寄存器,一般保持不变,由于栈则是从高位地址向地位地址分配的,所以新入栈的数据地址更低。

在x86_64汇编中push rdi是先把rsp值-8,再把rdi的值存入栈中,pop rdi意思是把当前栈顶指针指向的值存入rdi,再把rsp值+8。

Screenshot 2024 02 22 144233 1024x146.png-42

题目要求我们取出栈顶的值,减去rdi的值,然后再放回去

from pwn import *

context.arch='amd64'
p=process('/challenge/run')
p.recvline()
p.send(asm('''
    pop rax
    sub rax, rdi
    push rax'''))
print(p.recvallS())
Screenshot 2024 02 22 144601.png-43

level20

Swap register values with the stack(与栈交换寄存器值)

Screenshot 2024 02 22 150443 1024x154.png-44
Screenshot 2024 02 22 150636 1024x351.png-45

这块就让我们使用先进后出(LIFO)的属性来调换两个寄存器的值,很简单,先push rdi rsi再pop rdi rsi

from pwn import *

context.arch='amd64'
p=process('/challenge/run')
p.recvline()
p.send(asm('''
    push rdi
    push rsi
    pop rdi
    pop rsi'''))
print(p.recvallS())
Screenshot 2024 02 22 150832.png-46

level21

Memory reads and writes with the stack(通过栈读取写入内存)

Screenshot 2024 02 22 151324 1024x466.png-47

题里要求我们不使用pop指令使用rsp读取栈内4个四字的值,并求他们的平均数。

from pwn import *

context.arch='amd64'
p=process('/challenge/run')
p.recvline()
p.send(asm('''
   mov rax, [rsp]
   add rax, [rsp+8]
   add rax, [rsp+16]
   add rax, [rsp+24]
   mov rdi, 4
   div rdi
   push rax'''))
print(p.recvallS())

使用rsp读取最后入栈的4个值,求平均数

Screenshot 2024 02 22 152542.png-48

level22

Absolute jump(绝对跳转)

Screenshot 2024 02 22 165213 1024x233.png-49

x86_64用法 jmp <register>,jmp后不能直接用立即数,必须用寄存器

from pwn import *

context.arch='amd64'
p=process('/challenge/run')
p.recvline()
p.send(asm('''
    mov rsi, 0x403000
    jmp rsi'''))
print(p.recvallS())
Screenshot 2024 02 22 165640.png-50

level23

Relative jump(相对跳转)

Screenshot 2024 02 22 175247 1024x197.png-51

题目要求相对跳转到当前地址+0x51字节的位置,我们设置一个label标签定位地址,然后再前面填充0x51字节的nop,最后跳转到标签,就成功相对当前地址跳转0x51字节。

from pwn import *

context.arch='amd64'
p=process('/challenge/run')
p.send(asm(f'''
    jmp address
    .rept 0x51
    nop
    .endr
    address:
    mov rax, 0x1
    '''))
print(p.readallS())
Screenshot 2024 02 22 180028.png-52

level24

Control flow(控制流)

Screenshot 2024 02 22 181449 1024x355.png-53

和上一关差不多,不过是两种跳转合一起了

from pwn import *

context.arch = 'amd64'
p = process('/challenge/run')
p.send(asm('''
jmp start
.rept 0x51
nop
.endr
start:
pop rdi
mov rsi, 0x403000
jmp rsi
'''))
print(p.recvallS())
Screenshot 2024 02 22 181559.png-54

level25

Conditional branches(条件分支)

Screenshot 2024 02 22 183004 1024x471.png-55

cmp比较,相等ZF为1否则ZF为0,je是ZF为1时跳转,jne则相反。

from pwn import *

context.update(arch="amd64")
p = process("/challenge/run")
p.write(asm("""
mov eax, [rdi]
mov ebx, [rdi + 4]
mov ecx, [rdi + 8]
mov edx, [rdi + 12]

cmp eax, 0x7f454c46
je con1

cmp eax, 0x00005A4D
je con2

imul ebx, ecx
imul ebx, edx
jmp done

con1:
add ebx, ecx
add ebx, edx
jmp done

con2:
sub ebx, ecx
sub ebx, edx
done:
mov eax, ebx"""))
print(p.readallS())

level26

Jump tables(跳转表)

Screenshot 2024 05 22 101225.jpg-56

使用跳转表实现switch-case-defaut功能

Screenshot 2024 05 22 101350.png-57

先判断default情况,如果rdi>=4,则跳转到最后一种情况,剩下的情况直接根据rdi偏移跳转

from pwn import *

context.arch = 'amd64'
p = process('/challenge/run')
p.send(asm('''
cmp rdi, 4
jae default
jmp [rsi + rdi * 8]
jmp end
default:
jmp [rsi + 4 * 8]
end:
nop
'''))
print(p.recvallS())
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
贴吧
颜文字
Emoji
小恐龙
花!
贴吧
上一篇
下一篇