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

目录
相关文章
|
2月前
|
缓存
crash命令 —— files
crash命令 —— files
|
5月前
|
存储 前端开发 rax
Assembly Crash Course(1)
Assembly Crash Course(1)
45 6
|
3月前
|
iOS开发 Perl
解决Xcode报错Stored properties cannot be marked unavailable with ‘@available‘
解决Xcode报错Stored properties cannot be marked unavailable with ‘@available‘
44 0
|
图形学
Unity3D 5.x 启动过程中 License Error 解决办法
Unity3D 5.x 启动过程中 License Error 解决办法
273 0
|
Web App开发 SQL JavaScript
【性能】7分钟带你了解【尤大】都在使用的 Chrome Runtime Performance Debug!
前段时间用Vue3开发了一个线上标准的项目,也都已经提测了,最近可以抽时间来整理一下项目中用到的技术栈,( 我用前端【最新】技术栈完成了一个生产标准的项目【Vue3 + TS + Vite + Pinia + Windicss + NavieUI】。 在开发之前特地重温了一下18年尤大的VUE CONF(杭州),过程中发现尤大使用到了这个Performance去分析JavaScript每一帧的计算,之前知道这个工具,但是平时基本不怎么用,借此机会学一下并做一些记录。
224 0
|
Windows
用WinDbg分析Debug Diagnostic Tool生成的Userdump文件
1、下载WinDbg(Debugging Tools for Windows):http://www.microsoft.com/whdc/devtools/debugging/default.mspx 2、安装WinDbg 3、运行WinDbg 4、配置Symbol文件路径: File>Symbol File Path,输入:SRV*c:\websymbols*http://msdl.
1065 0