Assembly Crash Course(2)

简介: Assembly Crash Course(2)

9. 程序跳转指令: level 22 ~ level 24

# jmp 跳转绝对地址需要把地址放入寄存器接上jmp命令。
mov r12,0x403000
jmp r12
---------------- CODE ----------------
0x400071:       mov     r12, 0x403000
0x400078:       jmp     r12
--------------------------------------

间接跳转到所在地址后面0x51地址,并在该地址将 rax 赋值为 0x1。

汇编代码文件后缀:.S .asm; 后期编译会进行预处理,.s文件属于中间文件,不进行预处理。

.global _start    #  头文件-----
_start:           # -----------
.intel_syntax noprefix  # -----
_start:
        jmp next
        .rept 0x51
                nop
        .endr
next:
        mov rax,1
as -o asm.o asm.S && objcopy -O binary --only-section=.text ./asm.o ./asm.bin && cat ./asm.bin | /challenge/run
.global _start
_start:
.intel_syntax noprefix
_start:
        jmp next
        .rept 0x51
                nop
        .endr
next:
        mov rdi,[rsp]       #  注意 mov rdi,rsp 传递的是rsp地址而不是该地址保存的值
        mov rax,0x403000     # 因为 rsp作为指针,保存一个地址,地址保存数据
        jmp rax

10. 分支循环语句: level 25~level 28

Using the above knowledge, implement the following:
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]
where:
x = rdi, y = rax.
mov eax,[rdi]      # 如果想使用 rax,rbx,rcx。可以试试xor清零
cmp eax,0x7f454c46
je one
nop
mov eax,[rdi]
cmp eax,0x00005A4D
je two
nop                 #  nop貌似不必要
mov eax,[rdi+4]
imul eax,[rdi+8]
imul eax,[rdi+12]
jmp done                # 使用je/jg等跳转进入分支语句之后,
                      #  注意执行完分支语句会按顺序向下执行,注意不要混淆执行流!!!
one:                  #  在结尾放一个 done分支,每个if分支执行完之后跳转到后面可以避免
  mov eax,[rdi+4]
  add eax,[rdi+8]
  add eax,[rdi+12]
  jmp done
two:
mov eax,[rdi+4]
  sub eax,[rdi+8]
  sub eax,[rdi+12]
done:
and eax,eax
mov ebx,[rdi+4]
mov ecx,[rdi+8]
mov edx,[rdi+12]
mov eax,[rdi]
cmp eax,0x7f454c46
je con1
nop
mov eax,[rdi]
cmp eax,0x00005A4D
je con2
nop
imul ebx,ecx
imul ebx,edx
jmp done
nop
con1:
add ebx,ecx
add ebx,edx
jmp done
nop
con2:
sub ebx,ecx
sub ebx,edx
done:
mov eax,ebx

level 26:基地址跳转,指令优化 (⭐)

In the above example, the jump table could look like:
  [0x1337] = address of do_thing_0
  [0x1337+0x8] = address of do_thing_1
  [0x1337+0x10] = address of do_thing_2
  [0x1337+0x18] = address of do_default_thing
Using the jump table, we can greatly reduce the amount of cmps we use.
Now all we need to check is if `number` is greater than 2.
If it is, always do:
  jmp [0x1337+0x18]
Otherwise:
  jmp [jump_table_address + number * 8]
Using the above knowledge, implement the following logic:
  if rdi is 0:
    jmp 0x403019
  else if rdi is 1:
    jmp 0x4030c9
  else if rdi is 2:
    jmp 0x4031d2
  else if rdi is 3:
    jmp 0x403287
  else:
    jmp 0x403378
Please do the above with the following constraints:
  Assume rdi will NOT be negative
  Use no more than 1 cmp instruction
  Use no more than 3 jumps (of any variant)
  We will provide you with the number to 'switch' on in rdi.
  We will provide you with a jump table base address in rsi.
Here is an example table:
  [0x40405b] = 0x403019 (addrs will change)
  [0x404063] = 0x4030c9
  [0x40406b] = 0x4031d2    #  ==
  [0x404073] = 0x403287
  [0x40407b] = 0x403378    #  ==
#               rsi 作为基地址0x40405b,[rsi]= 0x403019
mov rax,rdi
and rax,0xfffffffffffffffc  #  最低两位全0;作用,清空最低两位数据
                            
                            #  若 rdi == 0~3,得到0,执行跳转
je nomal                    #  je指令,隐式比较 rax 与 0,若 rdi>=4,不执行跳转
nop
jmp [rsi+32]        # >=4:  0x40407b-0x40405b == 0x20
nop
nomal:        # 0:    0x40405b-0x40405b == 0x0 == 0x0*8
jmp [rsi+rdi*8]     # 1:    0x404073-0x40405b == 0x8 == 0x1*8
nop                 # 2:    0x40405b-0x40405b == 0x10 == 0x2*8
                    # 3:    0x404073-0x40405b == 0x18 == 0x3*8
As an example, a for-loop can be used to compute the sum of the numbers 1 to n:
  sum = 0
  i = 1
  while i <= n:
    sum += i
    i += 1
Please compute the average of n consecutive quad words, where:
  rdi = memory address of the 1st quad word
  rsi = n (amount to loop for)
  rax = average computed
We will now set the following in preparation for your code:
  [0x4042f0:0x404498] = {n qwords]}
  rdi = 0x4042f0
  rsi = 53
xor rax,rax
mov rbx,0
one:
  cmp rbx,rsi
  jle loop
  nop
jmp done
nop
loop:
  add rax,[rdi+rbx*0x8]
  add rbx,0x1
  jmp one
done:
  div rsi
As an example, say we had a location in memory with adjacent numbers and we wanted
to get the average of all the numbers until we find one bigger or equal to 0xff:
  average = 0
  i = 0
  while x[i] < 0xff:
    average += x[i]
    i += 1
  average /= i
Using the above knowledge, please perform the following:
  Count the consecutive non-zero bytes in a contiguous region of memory, where:
    rdi = memory address of the 1st byte
    rax = number of consecutive non-zero bytes
Additionally, if rdi = 0, then set rax = 0 (we will check)!
An example test-case, let:
  rdi = 0x1000
  [0x1000] = 0x41
  [0x1001] = 0x42
  [0x1002] = 0x43
  [0x1003] = 0x00
then: rax = 3 should be set
We will now run multiple tests on your code, here is an example run:
  (data) [0x404000] = {10 random bytes},
  rdi = 0x404000
xor rax,rax     # 校验 rdi 是否为0【可能不必要】
cmp rdi,0x0             
je done
nop
mov rbx,0x0
loop:
  mov rcx,[rdi+rbx]
  cmp rcx,0x0
  je done
  nop
  add rax,0x1
  add rbx,0x1
  jmp loop
  nop
done:
  and rax,rax

11. 程序栈调用:level 29~level 30

level 29(⭐)

Functions use the instructions "call" and "ret".
The "call" instruction pushes the memory address of the next instruction onto
the stack and then jumps to the value stored in the first argument.
Let's use the following instructions as an example:
  0x1021 mov rax, 0x400000
  0x1028 call rax
  0x102a mov [rsi], rax
1. call pushes 0x102a, the address of the next instruction, onto the stack.
2. call jumps to 0x400000, the value stored in rax.
The "ret" instruction is the opposite of "call".
ret pops the top value off of the stack and jumps to it.
Let's use the following instructions and stack as an example:
                              Stack ADDR  VALUE
  0x103f mov rax, rdx         RSP + 0x8   0xdeadbeef
  0x1042 ret                  RSP + 0x0   0x0000102a
Here, ret will jump to 0x102a
Please implement the following logic:
  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 is provided at 0x403000.
foo takes a single argument as a value and returns a value.
All functions (foo and str_lower) must follow the Linux amd64 calling convention (also known as System V AMD64 ABI):
  https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI
Therefore, your function str_lower should look for src_addr in rdi and place the function return in rax.
An important note is that src_addr is an address in memory (where the string is located) and [src_addr] refers to the byte that exists at src_addr.
Therefore, the function foo accepts a byte as its first argument and returns a byte.
We will now run multiple tests on your code, here is an example run:
  (data) [0x404000] = {10 random bytes},
  rdi = 0x404000
mov rax,0
mov rsi,rdi
cmp rsi,0
je done
nop
loop:
mov bl,[rsi]
cmp bl,0
je done
nop
cmp bl,90
ja next
nop
mov dil,bl    ;将地址发给rdi,因为foo函数的参数从rdi引用
mov rdx,rax   ;调用函数前保存rax的值
mov rcx,0x403000
call rcx
mov [rsi],al
mov rax,rdx   ;使用之后再赋回rax的值,当然也可以直接使用其他寄存器,在函数最后返回时赋给rax
add rax,1
next:
add rsi,1
jmp loop
nop
done:
ret

level 30(⭐)

push 0
mov rbp,rsp
mov rax,-1
sub rsi,1
sub rsp,rsi
loop1:
        add rax,1
        cmp rax,rsi
        jg next
        nop
        mov rcx,0
        mov cl,[rdi+rax]
        mov r11,rbp
        sub r11,rcx
        mov dl,[r11]
        add dl,1
        mov [r11],dl
        jmp loop1
        nop
next:
mov rax,0
mov rbx,rax
mov rcx,rax
mov ax,-1
loop2:
        add ax,1
        cmp ax,0xff
        jg return
        nop
        mov r11,rbp
        sub r11,rax
        mov dl,[r11]
        cmp dl,bl
        jle loop2
        nop
        mov bl,dl
        mov cl,al
        jmp loop2
        nop
return:
mov rax,rcx
mov rsp,rbp
pop rbx
ret

参考wp

pwncollege通关笔记:3.Assembly Refresher(从0开始学习pwn) - FreeBuf网络安全行业门户

Assembly Crash Course | Write-ups

目录
相关文章
|
机器学习/深度学习 人工智能 自然语言处理
视觉 注意力机制——通道注意力、空间注意力、自注意力
本文介绍注意力机制的概念和基本原理,并站在计算机视觉CV角度,进一步介绍通道注意力、空间注意力、混合注意力、自注意力等。
14171 58
|
1月前
经典题:求一个数是否为质数
介绍质数判断与自幂数概念:质数为仅被1和自身整除的大于1的自然数;自幂数是n位数等于各位数字n次幂之和,如水仙花数(三位)、四叶玫瑰数(四位)等,体现数字的独特数学之美。(239字)
69 1
|
云安全 监控 安全
什么是游戏盾,能为游戏安全提供哪些帮助
游戏盾是一种专为游戏行业量身定制的网络安全防护解决方案。随着网络游戏的普及和玩家数量的激增,游戏服务器面临的网络威胁也日益增多,包括但不限于分布式拒绝服务攻击(DDoS攻击)、挑战碰撞(CC)攻击等。为了应对这些挑战,游戏盾应运而生,旨在为游戏运营商提供全方位、高效能的网络安全解决方案。
|
监控 数据可视化 数据挖掘
惊叹!燕云十六声运营团队靠它提升工作效率!
在游戏行业竞争激烈的2025蛇年新春,燕云十六声团队通过选择合适的可视化协作软件实现了高效工作。板栗看板以其高度可视化的任务管理、灵活的任务分配和强大的文件管理功能脱颖而出。Trello、Asana、Monday.com、Zeplin和Slack等工具也各具特色,提供了丰富的插件、权限管理和实时沟通等功能,助力团队应对巨大工作量和协作挑战,提升工作效率,打造更优质的游戏产品。
637 12
|
缓存 算法 大数据
大数据查询优化算法
【10月更文挑战第26天】
621 1
|
设计模式 编解码 程序员
探索 C++ 20 (co_await、co_yield 和 co_return)协程基本框架的使用
探索 C++ 20 (co_await、co_yield 和 co_return)协程基本框架的使用
2173 2
探索 C++ 20 (co_await、co_yield 和 co_return)协程基本框架的使用
|
Unix
深入理解 CMake 的 `cmake --build` 命令
深入理解 CMake 的 `cmake --build` 命令
2681 1
|
SQL 关系型数据库 MySQL
centos编译安装mariadb
一般我不太愿意用mysql,那个玩意,有的时候不太友好。 我还是比较喜欢mariadb。
655 0
|
消息中间件 NoSQL 算法
第一次凡尔赛,字节跳动3面+腾讯6面一次过,谈谈我的大厂面经
简单来说,就如标题一样,我今天也想要凡尔赛一次,原来大厂的面试也没有想象中的那么难,字节跳动3面+腾讯6面,就这么一次性过了,下面就细细聊聊我的大厂面经吧,希望能够给金三银四要面试的朋友提供一些经验。
|
Kubernetes 安全 Cloud Native
云原生|kubernetes|安全漏扫神器trivy的部署和使用
云原生|kubernetes|安全漏扫神器trivy的部署和使用
1067 0