前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >腾讯极客挑战赛丨从“碰撞”到“爆破”,42次尝试终破纪录

腾讯极客挑战赛丨从“碰撞”到“爆破”,42次尝试终破纪录

原创
作者头像
腾讯极客挑战赛
修改2021-06-16 14:24:44
8910
修改2021-06-16 14:24:44
举报
文章被收录于专栏:极客挑战赛极客挑战赛

各位爱挑战爱学习的coder们,大家千呼万唤的解题思路来啦!(原赛题传送门:腾讯极客挑战赛丨全世界最最最小的程序,等你来battle!

x86-64赛道的首破纪录奖励公布后,“阿鹏”同学仅用4天即打破纪录,最终以291字节的成绩,摘得首破纪录奖以及赛道冠军。下面由他带来x86-64赛道的解题思路分享,也欢迎小伙伴们在文末留言,分享自己的解题报告链接。


原理

可以从两个方面来解这道题:

  1. md5碰撞 直接碰撞输出定值,显然不可行 利用hashclash工具碰撞每一个bit
  2. 计算本程序的md5值

md5碰撞

碰撞的方法参考这几篇文章:

能否构造一个含有自己哈希或MD5等的文件(https://www.zhihu.com/question/411191287)、 collisions(https://github.com/corkami/collisions)

碰撞工具:hashclash(https://github.com/cr-marcstevens/hashclash)

目前并没有办法给定一个md5值,生成对应的消息,所以我们不能像编写一个helloworld一样直接输出一个定植,然后在结尾附加字节使得文件的md5值和输出一致。

王小云院士的差分攻击算法可以构造出两个不同的消息具有一样的md5值,并且有hashclash这样的开源工具可以利用。但通过这种方法每128字节才可以碰撞1个bit。我们需要编写程序读取每个128字节中的那一个bit,连起来转换成md5输出,这样数据加代码会使得程序会非常巨大。一开始我对碰撞比较好奇,所以仍然完成了这种方法

计算程序md5值

题目中给的示例即为这种方法。为了结果尽可能小,我们需要从优化算法,定制elf头两方面进行

定制elf头

既然要让程序足够小,就不能用gcc编译了。我们编写汇编代码,直接用nasm编译。nasm的好处在于可以直接生成二进制文件,方便我们定制elf文件头。定制过程中需要大量翻阅elf文件格式的定义,可以在linux教程(https://linux.die.net/man/5/elf)中找到。

elf头很简单,分为三个部分,elf header, program header和section header。其中section header是可选的,可以不要。我们只需要关注运行时的segment即可,于是只需要一个elf header和一个program header。

接下来分析elf header 和program header中的每一个字段,对于没用的字段可以像里面填充代码,以尽可能的节省空间

elf header

elf头包含了elf程序整体的一些信息。经过大量的测试,不可随意修改的字段包括

  • file_identification: magic nuber,固定为'\x7fELF'
  • ei_class: 64位程序
  • ei_data: LSB字节序
  • e_type: 可执行文件
  • e_machine: x86_64
  • e_entry: 入口点
  • e_phoff: program header的文件偏移
  • e_ehsize: elf头大小,固定为0x40
  • e_phentsize: program header的大小,固定为0x38
  • e_phnum: program header数量,只有1个
  • e_shnum: section header数量,没有

剩余字段都是可以填任意值的。由于没有section,e_shentsize(section header大小)这个字段也可以填为0。

program header

程序头描述了segment的属性。我们只需要1个segment即可。不可随意修改的字段包括

  • p_type: LOAD,这个segment需要load进内存。
  • p_flags: 7 这个segment需要可读可写可执行。这个字段由32位整形存储。经过测试只需要最低三个bit为1即可,其余bit可以随意(更进一步测试,对于内存权限而言,只要可执行必定可读,所以只需要执行和写对应的bit为1即可,如果需要非常极限的利用可以考虑这一点),因此p_flags的第一个字节的低3bit需要为1.
  • p_offset: 该segment与文件中的偏移。整个文件我们都要load进内存计算,因此偏移为0
  • p_vaddr: load到的虚拟地址位置,填入程序的基地址。这里只需要合法,低12bit需要为0,基地址不能特别大。在我本地测试下来任何小于0x7ffff0000000,低12bit为0的地址都可以作为基地址
  • p_filesz: load的大小,填写文件大小(实际测试下来这个字段填写filesize-0x1000范围内都可以,但是不是很容易利用上,所以没考虑在内)
  • p_memsz的高4byte: 这个字段描述内存大小,测试下来只有低4字节可以随意填写,第5字节在题目的环境上只能填0-f,而我本地的环境小于0x7ffffff00000左右都不影响运行。为了适用性更广我们只考虑使用它的低4字节

剩余部分都是可以填任意内容的了

接下来我们分析一下elf header末尾的字段和program header开头的字段,考虑重叠两部分。由于p_type必须要为dd 1,只有e_phnum这里开始可以重叠。刚好e_shnum填写7并不影响,于是program header的偏移地址就为0x38

总体算下来,我们一共有10+4+12+3+8+4字节可以填写任意字节。如果填代码的话,需要用jmp将他们连起来。分析一下10 和4这两个block,他们中间间隔了4个字节。我们可以吧这四个字节当作一条汇编指令的data部分(例如mov eax, 0的字节码为b8 00 00 00 00),这样省去了使用jmp连接两个block,又节省了一个字节。

同理,4和12这两个block中间间隔了dq entrydq 38h一共8个字节。如果entry_addr只占4字节,高4字节为0,那么可以把b8和这四字节连起来变成mov eax, entry00 00对应的汇编代码为add [rax], al38 00cmp [rax], al,此时rax指向entry_addr,是个合法指针,这两句add 和cmp可以顺利运行。add [rax], al看起来会修改rax指向的内容(入口点),但实际测试起来竟然没有!这也就使得我们可以使用这个trick

这里是一份重叠后的代码,大部分可随意填写的字段都被填入了nop(base addr并没有填入代码,不然还可以多至少4字节)最后用系统调用退出

代码语言:javascript
复制
BITS 64
                org     0x10000
base:
                db      0x7f, "ELF"     ; e_ident
                db      2               ; ei_class          ELFCLASS64
                db      1               ; ei_data           ELFDAA2LSB
start0:         ; any 10 bytes
                db      090h, 090h, 090h, 090h,             ; nop
                db      090h, 090h, 090h, 090h,             ; nop
                db      090h
                db      0b8h            ; mov eax, 0x3e0002
                dw      2               ; e_type            ET_EXEC
                dw      3Eh             ; e_machine         EM_X86_64
start1:         ; any 4 bytes
                nop
                nop
                nop
                db      0b8h            ; mov eax, entry
                dq      start0          ; e_entry
                dq      38h             ; e_phoff
start2:         ; any 12 bytes
                db      090h, 090h, 090h, 090h, 090h,       ; nop 
                db      090h, 090h, 090h, 090h, 090h,       ; nop
                jmp     start3                      
                dw      40h             ; e_ehsize
                dw      38h             ; e_phentsize
                dw      1               ; e_phnum
                dw      0               ; e_shentsize
                db      0x7
start3:         ; any 3 bytes
                nop
                jmp     start4
                dq      0               ; p_offset
                dq      $$              ; p_vaddr
start4:         ; any 8 bytes    
                nop
                nop
                nop
                nop
                nop
                nop
                jmp     start5 
                dq      filesize        ; p_filesz
start5:         ; any 4 bytes
                nop     
                nop     
                jmp     start 
                db      0,0,0,0
start:
                mov     rax, 60
                mov     rdi, 42
                syscall
filesize        equ $-$$

你可以使用这条命令汇编运行这段代码:

代码语言:javascript
复制
nasm ./test_elf.asm && chmod +x ./test_elf && ./test_elf || echo $?

也可以使用ida进行调试,将会看到运行到db 0而不会报错:

这样我们一共有10+4+12+3+8+4个字节可以任意填写,base addr中有32-12=20bit可以拿来爆破(为了使指针正常赋值,我们的基地址需要小于uint32的范围)

减去jmp和重叠的mov指令,可以被填写为代码的字节数为9+3+10+6+2字节

在向elf头填代码的时候也比较玄学,由于可以填写的空间是固定的,代码的顺序也是有要求的,所以不一定所有的地方都能填上代码,剩余的部分可以拿来爆破

优化md5算法

接下来我们考虑优化md5算法。

首先我在GitHub上找到了一份比较好的md5源码md5.c(https://gist.github.com/creationix/4710780),它没有内联任何代码,没有展开循环,代码本身比较小,我们基于它编写我们的代码。我们用gcc编译后,照着写一遍汇编,代码的体积很轻松的来到五百多字节。接下来我们需要用汇编完整重写一遍。

md5循环优化

md5使用了md结构,类似分组密码,讲消息padding到512bit,padding时先补一个1bit,然后补若干个0bit到448,最后64bit填写原消息的bit数。

接下来对每一个512bit的block计算4个int值,加到iv上(iv初始值固定)。总体为2层循环。

优化的思路为把iv放到最后一个block,这样前面block的内容是确定的,他们算出来的结果也是确定的,把这个结果作为iv,计算最后一个block的hash,这样可以省去一层循环。

这样的弊端在于程序的大小存在瓶颈。如果程序大小使得iv值刚好穿越最后一个block时,即一部分iv在上一个block,我们是没办法利用的,此时需要补充到刚好极限的大小。程序大小需要在[k*64+0x10:k*64+0x37]这个范围(最小值为iv刚好为多出去的0x10个字节,最后一个block都是iv,最大值为刚好加上0x80的padding字节,8字节的size字节后正好为512bit),也就是如果我们的程序长度为336这样的5*64+16的大小时,下一步就要减去0x10+8+1=25字节才是有意义的,这也就是为什么会卡在400,336这些瓶颈。

rbox

md5有一个rbox(rotate box),简单观察一下发现有16字节。但并不能找到一条从i到r[i]的简洁的公式,所以还是要打表,只是从64字节的表变成16字节的表,具体为r[i/16][i%4]

kbox

kbox打表太占空间了。我找了一些md5资料,kbox生成的算法为:

代码语言:javascript
复制
k[i] = (uint32_t)(floor(fabs(sin(i+1)*0x100000000)));

我们可以先生成k表,计算时打表,也可以计算md5时计算k表的值。我这里选择了后一种方法,感觉要比打表小一点。

计算浮点数我们使用x87 fpu指令,80x87的教程参考80x87 Instruction Set (x87 - Pentium)(https://www2.math.uni-wuppertal.de/~fpf/Uebungen/GdR-SS02/opcode_f.html)

fsin计算sin值,乘以0x100000000的操作可以用push 0x4f800000和fmul dword[rsp],fabs指令计算绝对值。最后floor的地方,这份资料里需要用fldcw设置fpu的控制字,然后再fistp保证为floor。后面参考了另一份资料FISTTP — Store Integer with

Truncation(https://www.felixcloutier.com/x86/fisttp),fisttp在将浮点数转为整形时就是floor转换,从而不必再设置控制字

计算k表:

代码语言:javascript
复制
 inc     ii            ; eax
                push    iiq            ; rax
                fild    dword [rsp]
                fsin
                push    0x4f800000
                fmul    dword [rsp]
                fabs
                fisttp   qword [rsp]
                pop     ffq            ; rsi

输出结果

输出需要将一个字节的低4bit和高4bit分离然后tohex。我们使用div bl(bl值为0x10),它将ax寄存器除以0x10,余数(低4bit)放到ah, 商(高4bit)放到al,根据范围加上不同的值转ascii。操作完al后eax右移,ah(低4bit)就去到了al,此时根据指针奇偶性,还需跳转到内层循环的开头操作一遍低4bit。然后用loop指令完成外层的0x10次循环

最后用系统调用write(1, buf, 32)输出结果,exit(0)退出。(我一开始以为结尾的回车是必要的,所以还构造了一个0x0a字节,输出33字节)

编写汇编代码

接下来对照c源代码编写我们的汇编代码。编写时需要代码尽量小,原则上时利用尽量短的代码,用到的一些trick:

  • 取数据/存数据考虑使用lodsb/lodsw/lodsd,stosb/stosw/stosd这类短的指令
  • mov可以考虑换成xchg,当xchg的一个寄存器为eax时xchg的长度为1字节
  • 一般情况下指针寄存器用64为寄存器更短(形如mov eax,  [rax]),直接使用的寄存器用32位寄存器更短(形如mov eax, ebx)。由于我们的内存空间在uint32范围内,所以用64位寄存器或32位寄存器效果来说一样
  • 分配寄存器的时候,频率最高的寄存器用eax,也就是下标寄存器用eax。我最开始用ecx存放下标,调整一下使用eax后发现少了1个字节
  • md5的switch内尽可能复用代码。md5算法分为4个switch,根据i的值进行不同操作。先写了一份基础代码后,发现一些指令在不同分支内都用到了,那就可以提取出来放在开头执行。对于两个不同分支的也可以服用,只要最终结果等价就行。(这里的优化是比较难的,需要考虑每条指令的关系,尽可能的复用)
  • 尽可能利用已有的东西。刚load完的程序,所有寄存器出了rip和rsp都设置为0,rsp指向的内容是固定的,argc, argv,envp等,堆栈平衡时可以把这些数据取出来利用:

比如我的代码用到了argc的这个1,省去了后面一次1的赋值;最终的md5字符串被写到了argv[0]里,一句pop rdi就获取了一个指针;argv[1]是空指针,刚好可以在最后退出的时候给rdi,省去了xor edi, edi这句清零(题目需要返回值为0)

为了方便调试,我另外制作了一份debug版本的汇编源文件,没有重叠任何elf头。可以直接使用gdb进行调试,入口点位于0x10078。

debug版本的汇编代码如下:

我们还需要额外编写一个程序计算输出的前n-1个block的结果,讲这个结果放到iv里。

代码语言:javascript
复制
BITS 64
                org     0x10000
base:
                db      0x7f, "ELF"     ; e_ident
                db      2               ; ei_class          ELFCLASS64
                db      1               ; ei_data           ELFDAA2LSB
                db      1               ; ei_version        EV_CURRENT
                db      0               ; ei_osabi          ELFOSABI_NONE
                db      0               ; ei_abiversion     0
                dd      0
                db      0,0,0

                dw      2               ; e_type            ET_EXEC
                dw      3Eh             ; e_machine         EM_X86_64
                dd      1               ; e_version         EV_CURRENT
                dq      start           ; e_entry
                dq      40h             ; e_phoff
                dq      0
                dd      0               ; e_flags
                dw      40h             ; e_ehsize
                dw      38h             ; e_phentsize
                dw      1               ; e_phnum
                dw      0               ; e_shentsize
                dw      0               ; e_shnum
                dw      0               ; e_shstrndx
                ; program header
                dd      1               ; p_type            PT_LOAD
                dd      7               ; p_flags           PF_X | PF_W | PF_R
                dq      0               ; p_offset
mul_val:        dq      0x10000         ; p_vaddr
                dq      0x10000         ; p_paddr
                dq      filesize        ; p_filesz
                dq      0xffffffff      ; p_memsz           upto 0xffffffff
                dq      1000h           ; p_align

padding_size_off equ padding_size - padding_byte
data_off         equ data - padding_byte
r_off            equ r - padding_byte
start:
%define v0 edx
%define v1 ebx
%define v2 ebp
%define v3 edi

%define v0q rdx
%define v1q rbx
%define v2q rbp
%define v3q rdi

%define idxs r13

%define ii eax
%define iiq rax
%define iib al

%define gg ecx
%define ggq rcx
%define ff esi
%define ffq rsi
                mov     esi, padding_byte - 0x10
                push    rsi
                lodsd
                xchg    eax, v0
                lodsd
                xchg    eax, v1
                lodsd
                xchg    eax, v2
                lodsd
                xchg    eax, v3
                mov     byte [rsi], 0x80
                mov     word [rsi + padding_size_off], filesize * 8
                lea     idxs, [rsi + r_off]
loop1_start: 
switch1_0:   
                mov     ff, v2
                cmp     iib, 0xF                         
                ja      short switch1_10
                xor     ff, v3
                and     ff, v1
                mov     gg, ii
                jmp     short switch1_end4
switch1_10:         
                xor     ff, v1          
                cmp     iib, 1Fh
                ja      short switch1_20
                and     ff, v3
                lea     gg, [iiq + iiq*4+1]
                jmp     short switch1_end3
switch1_20:                            
                lea     gg, [iiq + iiq*2+5]
switch1_end4:
                xor     ff, v3
                cmp     iib, 2Fh
                jbe      short switch1_end2
switch1_30:                            
                imul    gg, ii, 7
                mov     ff, v3
                not     ff
                or      ff, v1
switch1_end3:
                xor     ff, v2
switch1_end2:                            
                and     gg, 0Fh
                add     v0, dword [idxs + ggq * 4 + data-r] ; w[g]
                add     v0, ff ; f
                inc     ii
                push    iiq
                fild    dword [rsp]
                fsin
                push    0x4f800000
                fmul    dword [rsp]
                fabs
                fisttp   qword [rsp]
                pop     ffq
                add     v0, ff
                dec     ii
                mov     ff, ii
                shr     ff, 4
                and     iib, 3
                add     iiq, idxs
                xchg    ii, v3 ; switch eax, ecx
                mov     cl, byte [v3q + ffq * 4] ; cl is v3b
                rol     v0, cl
                add     v0, v1
                xchg    ii, v0
                xchg    ii, v1
                xchg    ii, v2
                xchg    ii, v3 ; switch back
                pop     iiq
                cmp     iib, 40h
                jnz     short loop1_start
loop1_end:      
                pop     rsi
                add     [rsi], v0
                add     [rsi+4], v1
                add     [rsi+8], v2
                add     [rsi+12], v3
                ; rax = 0x40
                ; rdi < 0x10
                ; rsi = buffer
                ; ecx, edx, ebx, ebp filled
                pop     rdx     ; rdx = 1
                mov     cl, 0x10
                mov     bl, cl
                pop     rdi     ; argv[0]
                push    rdi     ; save
loop_start:
                lodsb
                div     bl
loc:
                add     al, 30h
                cmp     al, 3ah
                jl      else
                add     al, 39
else:
                stosb
                shr     eax, 8
                test    edi, edx
                jnz      loc
                loop    loop_start
                pop     rsi     

                mov     edi, edx    ; edi = 1
                xchg    eax, edx    ; eax = 1
                mov     dl, 32
                syscall
                
                mov     al, 60
                pop     rdi
                syscall
r:
                db 7, 0Ch, 11h, 16h
                db 5, 9, 0Eh, 14h
                db 4, 0Bh, 10h, 17h
                db 6, 0Ah, 0Fh, 15h
iv:             dd 0x6369ccc1, 0x495eb568, 0xd15445da, 0xaea958a3    
padding_byte:
filesize        equ $-$$
align_addr      equ $ + (64 - filesize % 64)
data            equ align_addr - 64
padding_size    equ align_addr-8

爆破一个iv为0

经过很长时间的优化,程序的大小来到了297字节,此时已经找不到什么优化的办法了。看到和最终的结果272之差6个字节了,可以开始考虑爆破了。我们可以假设最后一个iv的值为0,这样直接用初始化的0字节,省去4字节。

最终输出的时候我是用指针的奇偶性来判断内层循环的。如果假设hash值的每个字节的低4bit都不全为0,那可以用shr eax, 8的结果(不为0则跳转,完成输出的内层循环,第二次shr后eax为0,不跳转),这里的概率为(15/16)**16,为百分之35左右

此时头中有3个任意字节没办法放代码,加上baseaddr的20bit,一共48bit拿来爆破,超过了需要的32bit的空间,理论上时可行的。编写好最终的结果后,编写爆破程序开始爆破。在我的笔记本版的i9上花了不到1个小时爆破出了8个结果(其中第8个才中了百分之35的概率,脸黑。。。)

最终的代码如下:

代码语言:javascript
复制

%define v0 edx
%define v1 ebx
%define v2 ebp
%define v3 edi

%define v0q rdx
%define v1q rbx
%define v2q rbp
%define v3q rdi

%define idxs r13

%define ii eax
%define iiq rax
%define iib al

%define gg ecx
%define ggq rcx
%define ff esi
%define ffq rsi

padding_size_off equ padding_size - padding_byte
data_off         equ data - padding_byte
r_off            equ r - padding_byte

; 9 4 12 3 8 5
; 5+4+1         10
; 3+1           4
; 1+3+6+2       12
; 4+2+2         8
; 1+2+2         5

BITS 64
               org     0xa0a60000
base:
               db      0x7f, "ELF"     ; e_ident
               db      2               ; ei_class         ELFCLASS64
               db      1               ; ei_data           ELFDAA2LSB
start0:         ; 10 bytes
               mov     esi, padding_byte - 0xC     ; 5
               push    rsi                         ; 1
               lodsd                               ; 1
               xchg    eax, v0                     ; 1
               lodsd                               ; 1
               db      0bbh      ; mov ebx,imm32   ; 1
               dw      2               ; e_type           ET_EXEC
               dw      3Eh             ; e_machine         EM_X86_64
start1:         ; 4 bytes
               xchg    eax, v1                     ; 1
               lodsd                               ; 1
               xchg    eax, v2                     ; 1
               db      0b8h            ; mov eax   ; 1
               ; jmp     start3
               dq      start0          ; e_entry
               dq      38h             ; e_phoff
start2:         ; 12 bytes
               mov     byte [rsi], 0x80            ; 3
               mov     word [rsi + padding_size_off], filesize * 8 ; 6
               xchg    eax, ecx                    ; 1
               
               jmp     start4                      ; 2
               dw      40h             ; e_ehsize
               dw      38h             ; e_phentsize
               dw      1               ; e_phnum
               dw      0               ; e_shentsize
               db      0x7
start3:         ; 3 bytes
               db      0xf6, 0xdc, 0                  ; any 3 bytes
               dq      0               ; p_offset
               dq     $$              ; p_vaddr

start4:         ; 8 bytes      
               lea     idxs, [rsi+r_off]           ; 4
loop1_start:
               mov     ff, v2                      ; 2
               jmp     start5                      ; 2
               dq      filesize        ; p_filesz
               ; 5 bytes
               

start5:         
               cmp     iib, 0xF                    ; 2
               jmp     start                       ; 2
               db      0,0,0,0

start:
; loop1_start:
switch1_0:   
               ; mov     ff, v2
               ; cmp     iib, 0xF                         
               ja      short switch1_10
               xor     ff, v3
               and     ff, v1
               mov     gg, ii
               jmp     short switch1_end4

switch1_10:         
               xor     ff, v1          
               cmp     iib, 1Fh
               ja      short switch1_20
               and     ff, v3
               lea     gg, [iiq + iiq*4+1]
               jmp     short switch1_end3

switch1_20:
               lea     gg, [iiq + iiq*2+5]
switch1_end4:
               xor     ff, v3
               ; jmp     short switch1_end2
               cmp     iib, 2Fh
               jbe      short switch1_end2


switch1_30:                            
               imul    gg, ii, 7
               mov     ff, v3
               not     ff
               or      ff, v1
switch1_end3:
               xor     ff, v2
               
               
switch1_end2:                            
               and     gg, 0Fh
               add     v0, dword [idxs + ggq * 4 + data-r] ; w[g]
               add     v0, ff ; f

               inc     ii
               push    iiq
               fild    dword [rsp]
               fsin
               push    0x4f800000
               fmul    dword [rsp]
               fabs
               fisttp   qword [rsp]
               pop     ffq
               add     v0, ff
               dec     ii

               
               mov     ff, ii
               shr     ff, 4
               and     iib, 3
               add     iiq, idxs

               xchg    ii, v3 ; switch eax, ecx

               mov     cl, byte [v3q + ffq * 4] ; cl is v3b
               rol     v0, cl
               add     v0, v1

               xchg    ii, v0
               xchg    ii, v1
               xchg    ii, v2
              
               xchg    ii, v3 ; switch back
               pop     iiq
               cmp     iib, 40h
               jnz     short loop1_start

loop1_end:      
               pop     rsi
               add     [rsi], v0
               add     [rsi+4], v1
               add     [rsi+8], v2
               mov     [rsi+12], v3
               
               ; rax = 0x40
               ; rdi < 0x10
               ; rsi = buffer
               ; ecx, edx, ebx, ebp filled

               pop     rdx ; rdx = 1
               
               ; mov   rcx, 0x10
               ; push   0x10
               ; pop     rcx
               mov     cl, 0x10
               ; mov   bl, 0x10
               mov     bl, cl
               pop     rdi         ; argv[0]
               push    rdi

loop_start:
               lodsb
               div     bl
loc:
               add     al, 30h
               cmp     al, 3ah
               jl      else
               add     al, 39
else:
               stosb
               shr     eax, 8
               ; test   edi, edx
               jnz      loc
               loop    loop_start
               pop     rsi

               mov     edi, edx    ; edi = 1
               xchg    eax, edx    ; eax = 1
               mov     dl, 32
               syscall
               
               mov     al, 60
               pop     rdi
               syscall
r:
               db 7, 0Ch, 11h, 16h
               db 5, 9, 0Eh, 14h
               db 4, 0Bh, 10h, 17h
               db 6, 0Ah, 0Fh, 15h

iv:             dd 0xc718942d, 0x27848216, 0x26f99fc3,
               
padding_byte:
filesize        equ $-$$
align_addr      equ $ + (64 - filesize % 64)
data            equ align_addr - 64
padding_size    equ align_addr-8

最终的输出:

代码语言:javascript
复制
$ nasm ./zero_res.asm && chmod +x ./zero_res && ./zero_res && echo # 编译运行,最后在echo一个回车
96935fafb893d3325b244bfe2ca98908
$ ./md5 ./zero_res # 这个程序用来计算目标文件n-1个block后的结果,并输出完整程序的md5值
0xc718942d, 0x27848216, 0x26f99fc3, 0x00000000
96935fafb893d3325b244bfe2ca98908


再分享一篇 x86-64赛道选手TODD在极客群内分享的解题思路(点击阅读),期待更多思路碰撞的火花

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 原理
  • md5碰撞
  • 计算程序md5值
    • 定制elf头
      • program header
      • 优化md5算法
        • md5循环优化
          • rbox
            • kbox
              • 编写汇编代码
                • 爆破一个iv为0
                • 最终的输出:
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档