我就是一条懒狗,天天白天睡觉。

游戏辅助

本来应该是签到题,但是实际上是脑洞题。

出题人本意可能是考察远程注入,但是题目描述太过于模糊,显得太过于脑洞了。

给出两个程序,一个是游戏辅助(假的,仅含注册码的判断的相关代码),一个是破解这个游戏辅助的补丁。

运行游戏辅助时要求输入注册码,随便输入了一个,不正确。

然后打开补丁,等破解完成后,再次随便输入一个注册码,正确。

补丁的窗口输出:flag{md5(dec(What_you_found))}

游戏辅助的代码很简单,明文比较注册码是否和1_am_n0t_f1ag相等。

游戏复制窗口中注册码输入1_am_n0t_f1ag,可以成功注册,没看懂上面的dec()是啥意思,我以为是decrypt,解密的意思,但是比较过程是明文比较的,也没有加密的过程啊。后来题目描述更新了,说是dec是十进制的意思。我无语了。。。

1_am_n0t_f1ag取md5提交,并不正确。

正确的What_you_found不在游戏辅助里面,而是在补丁里面。

看一下游戏补丁的部分代码:

1595946414254

WriteProcessMemory(v4, (char *)&loc_401227 + 1, &Buffer, 2u, 0);
// 向目标进程的内存0x401228处写入
// &Buffer开始的2个字节的数据,0x90 0x90,即两个nop

WriteProcessMemory(v4, (char *)&loc_401014 + 2, &v9, 5u, 0); 
// 向目标进程的内存0x401016处写入
// &v9开始的5个字节的数据,B8 A9 F1 00 00
// 即mov eax, 0x0000f1a9

WriteProcessMemory(v4, (char *)&loc_401019 + 2, &v7, 5u, 0);
// 向目标进程的内存0x40101b处写入
// &v7开始的5个字节的数据,BB 38 6E 0F 02
// 即mov ebx, 0x020f6e38

第2个和第3个WriteProcessMemory分别写入5个数据,拿第3个WriteProcessMemory举个例子:

v7 = 0x0F6E38BB;
v8 = 0x00000002;
v7是[bp-14],v8是[bp-10h]
依次写入BB 38 6E 0F 02

TIM图片20200728224437

上图来自本题一血的炜昊大佬:

为什么运行游戏补丁后,游戏辅助中随便输入一个注册码就能通过检测了呢?我们再来看一下游戏辅助中的0x401228附近。

1595946538781

上上图中,游戏补丁向游戏复制的内存的0x401228中写入了nop nop,正好件将jnz short loc_40125c覆盖,loc_40125C是注册失败的分支。覆盖后,无论输入的字符串和注册码的验证结果是否相同,都不会跳转至失败的分支,而是继续往下运行,即注册成功的分支。

flag是啥?是md5(20f6e38),20fe38是游戏补丁中向游戏辅助的内存0x40101b处写入的数据。别问我问啥偏偏是这个数据,我也不知道,要是知道的话,我当时就做出来了。

考察远程注入这个知识点倒是挺好的,但是你倒是说明白flag到底是啥呀,What you found,我found的数据可多了,我怎么知道是哪个。。。

simple

1595948326906

看起来似乎很简单,输入长度为16的字符串,进行32轮tea加密,比较密文。

虚假的解密脚本

v8, v9是明文->密文,v10每轮要减去0x61C8864661C88647或者加上0x9E3779B99E3779B9,注意需要手动维持溢出。v3和v4是中间变量,直接化简去掉即可。

轻松写出解密脚本:

def ull(n):
    return n & 0xffffffffffffffff

def get_v10(i):
    v10 = 0
    for i in range(i+1):
        #v10 = ull(v10 - 0x61C8864661C88647)
        v10 = ull(v10 + 0x9E3779B99E3779B9)
    return v10

def hex2bytes(h):
    import struct
    b = b''
    for i in range(7,-1,-1):
        b += struct.pack('b', int(h[2+i*2:2+i*2+2], 16))
    print(b)
    return b

def md5(b):
    import hashlib
    m = hashlib.md5()
    m.update(b)
    mm = m.hexdigest()
    print(mm)

def solve():
    v8 = 0xC3D3413C4B6381F9
    v9 = 0x8A6047975A08CCBA
    for i in range(31, -1, -1):
        v10 = get_v10(i)
        v9 = ull(v9 - ull(ull(v8+v10) ^ ull(16*v8) ^ ull(v8>>5)))
        v8 = ull(v8 - ull(ull(v9+v10) ^ ull(16*v9) ^ ull(v9>>5)))
        print(hex(v8), hex(v9), hex(v10))
    b = hex2bytes(hex(v8)) + hex2bytes(hex(v9))
    print(b)
    md5(b)

solve()

解出的输入应该为flag_is_not_here,按照题目要求取md5提交,不正确。啊哈,怎么回事??

查看交叉引用,发现这个函数(sub_402436)并没有被call,只是mov而已:

1595949291230

先去call的sub_402880,这个函数贼复杂,直接动调吧。后来才知道,这个函数类似于脱壳,将代码解码出来。

动调

本想动调看看哪里有问题,对于这个ELF64,ida7.0不知道为啥动调失败,好在最后发现ida6.8可以动调它。

经过多次动调总结:在4110f0处按f4,输入长度16的字符串

1595949631903

1595949711343

图1是按f4前,图2是按f4并输入长度为16的字符串后。

按3次f8,即可来到解压出的新代码处,是个堆(heap):

1595949809381

其中有些奇奇怪怪的汇编指令:

1595949868541

dec eax频繁出现,而且这个ELF64的程序,汇编指令中没有出现任何一个rax之类的。出大问题。

我的猜测是:

dec     eax
mov     eax, [eax+8]
的机器码同样可以反汇编成:
mov     rax, [rax+8]

猜测的对不对我不清楚,反正我拿capstone反汇编的结果验证了我的猜测。

capstone 机器码->汇编代码

ida反汇编的结果太影响理解了。

idc脚本提取出这个堆里的代码:

static main()
{
    auto i,fp;
    fp = fopen("d:\\dump","wb");
    auto start = 0x1fd4b20;
    auto size = 0x1fd4d00 - 0x1fd4b20;
    for(i=start;i<start+size;i++)
    {
        fputc(Byte(i),fp);
    }
    fp.close();
}

使用capstone反汇编:

from capstone import *

def dis(b):
    shellcode = b
    md = Cs(CS_ARCH_X86, CS_MODE_64)
    disasm = ''
    for i in md.disasm(shellcode, 0x00):
        #print("0x%x:\t%s\t%s" %(i.address, i.mnemonic, i.op_str))
        disasm += "\t%s\t%s\n" %(i.mnemonic, i.op_str)
    return disasm

#with open(r'd:\dump', 'rb')as f:
#    b = f.read()
b = b'UH\x89\xe5H\x83\xec`dH\x8b\x04%(\x00\x00\x00H\x89E\xf81\xc0H\x89u\xb8\xc6E\xeb\x84\xc6E\xec\xb2\xc6E\xed\xa8\xc6E\xee\xaf\xc6E\xef\xfd\xc6E\xf0\xb4\xc6E\xf1\xb3\xc6E\xf2\xad\xc6E\xf3\xa8\xc6E\xf4\xa9\xc6E\xf5\xfd\xc6E\xf6\xe7\xc6E\xf7\xfd\xc6E\xe1\x9c\xc6E\xe2\x87\xc6E\xe3\x89\xc6E\xe4\x86\xc6E\xe5\x9a\xc6E\xe6\x88\xc6E\xe7\x8d\xc6E\xe8\x90\xc6E\xe9\x91\xc6E\xea\x98\xc7E\xa8\x00\x00\x00\x00\x83}\xa8\x0c\x7f\x1d\x8bE\xa8H\x98\x0f\xb6D\x05\xeb\x83\xf0\xdd\x0f\xb6\xc0\x89\xc7\xe8\x85\xb4\xe6\xfe\x83E\xa8\x01\xeb\xddH\x8bE\xb8H\x89\xc7\xe8s\xb2\xe6\xfeH\x8bE\xb8H\x8b\x00H\x89E\xc0H\x8bE\xb8H\x8b@\x08H\x89E\xc8H\xc7E\xd0\x00\x00\x00\x00H\xb8\x8aJ\x04\xad\x8aJ\x04\xbdH\x89E\xd8H\xb83333333#H1E\xd8\xc7E\xac\x00\x00\x00\x00\x83}\xac\x1f\x7fXH\x8bE\xc8H\xc1\xe0\x04H\x89\xc2H\x8bE\xc8H\xc1\xe8\x05H1\xc2H\x8bE\xc8H\x01\xd0H3E\xd0H\x01E\xc0H\x8bE\xd8H\x01E\xd0H\x8bE\xc0H\xc1\xe0\x04H\x89\xc2H\x8bE\xc0H\xc1\xe8\x05H1\xc2H\x8bE\xc0H\x01\xd0H3E\xd0H\x01E\xc8\x83E\xac\x01\xeb\xa2H\xb8\xaa\x92\x06\x94Y\xb4K\xadH9E\xc0uFH\xb8\xc9\xc6G\xc4N\xfdeVH9E\xc8u6\xc7E\xb0\x00\x00\x00\x00\x83}\xb0\x04\x7f\x1d\x8bE\xb0H\x98\x0f\xb6D\x05\xe1\x83\xf0\xee\x0f\xb6\xc0\x89\xc7\xe8\x8f\xb3\xe6\xfe\x83E\xb0\x01\xeb\xdd\xbf\n\x00\x00\x00\xe8\x7f\xb3\xe6\xfe\xeb3\xc7E\xb4\x00\x00\x00\x00\x83}\xb4\x04\x7f\x1c\x8bE\xb4H\x98\x0f\xb6D\x05\xe6\xf7\xd0\x0f\xb6\xc0\x89\xc7\xe8Z\xb3\xe6\xfe\x83E\xb4\x01\xeb\xde\xbf\n\x00\x00\x00\xe8J\xb3\xe6\xfe\xbf\x00\x00\x00\x00\xe8\xd0\x97\xe6\xfe'
d = dis(b)
print(d)

输出的汇编代码为:

        push    rbp
        mov     rbp, rsp
        sub     rsp, 0x60
        mov     rax, qword ptr fs:[0x28]   
        mov     qword ptr [rbp - 8], rax   
        xor     eax, eax
        mov     qword ptr [rbp - 0x48], rsi
        mov     byte ptr [rbp - 0x15], 0x84
        mov     byte ptr [rbp - 0x14], 0xb2
        mov     byte ptr [rbp - 0x13], 0xa8
        mov     byte ptr [rbp - 0x12], 0xaf
        mov     byte ptr [rbp - 0x11], 0xfd
        mov     byte ptr [rbp - 0x10], 0xb4
        mov     byte ptr [rbp - 0xf], 0xb3
        mov     byte ptr [rbp - 0xe], 0xad
        mov     byte ptr [rbp - 0xd], 0xa8
        mov     byte ptr [rbp - 0xc], 0xa9
        mov     byte ptr [rbp - 0xb], 0xfd
        mov     byte ptr [rbp - 0xa], 0xe7
        mov     byte ptr [rbp - 9], 0xfd
        mov     byte ptr [rbp - 0x1f], 0x9c
        mov     byte ptr [rbp - 0x1e], 0x87
        mov     byte ptr [rbp - 0x1d], 0x89
        mov     byte ptr [rbp - 0x1c], 0x86
        mov     byte ptr [rbp - 0x1b], 0x9a
        mov     byte ptr [rbp - 0x1a], 0x88
        mov     byte ptr [rbp - 0x19], 0x8d
        mov     byte ptr [rbp - 0x18], 0x90
        mov     byte ptr [rbp - 0x17], 0x91
        mov     byte ptr [rbp - 0x16], 0x98
        mov     dword ptr [rbp - 0x58], 0
        cmp     dword ptr [rbp - 0x58], 0xc
        jg      0xa1
        mov     eax, dword ptr [rbp - 0x58]
        cdqe
        movzx   eax, byte ptr [rbp + rax - 0x15]
        xor     eax, 0xffffffdd
        movzx   eax, al
        mov     edi, eax
        call    0xfffffffffee6b520
        add     dword ptr [rbp - 0x58], 1
        jmp     0x7e
        mov     rax, qword ptr [rbp - 0x48]
        mov     rdi, rax
        call    0xfffffffffee6b320
        mov     rax, qword ptr [rbp - 0x48]
        mov     rax, qword ptr [rax]
        mov     qword ptr [rbp - 0x40], rax
        mov     rax, qword ptr [rbp - 0x48]
        mov     rax, qword ptr [rax + 8]
        mov     qword ptr [rbp - 0x38], rax
        mov     qword ptr [rbp - 0x30], 0
        movabs  rax, 0xbd044a8aad044a8a
        mov     qword ptr [rbp - 0x28], rax
        movabs  rax, 0x2333333333333333
        xor     qword ptr [rbp - 0x28], rax
        mov     dword ptr [rbp - 0x54], 0
        cmp     dword ptr [rbp - 0x54], 0x1f
        jg      0x14d
        mov     rax, qword ptr [rbp - 0x38]
        shl     rax, 4
        mov     rdx, rax
        mov     rax, qword ptr [rbp - 0x38]
        shr     rax, 5
        xor     rdx, rax
        mov     rax, qword ptr [rbp - 0x38]
        add     rax, rdx
        xor     rax, qword ptr [rbp - 0x30]
        add     qword ptr [rbp - 0x40], rax
        mov     rax, qword ptr [rbp - 0x28]
        add     qword ptr [rbp - 0x30], rax
        mov     rax, qword ptr [rbp - 0x40]
        shl     rax, 4
        mov     rdx, rax
        mov     rax, qword ptr [rbp - 0x40]
        shr     rax, 5
        xor     rdx, rax
        mov     rax, qword ptr [rbp - 0x40]
        add     rax, rdx
        xor     rax, qword ptr [rbp - 0x30]
        add     qword ptr [rbp - 0x38], rax
        add     dword ptr [rbp - 0x54], 1
        jmp     0xef
        movabs  rax, 0xad4bb459940692aa
        cmp     qword ptr [rbp - 0x40], rax
        jne     0x1a3
        movabs  rax, 0x5665fd4ec447c6c9
        cmp     qword ptr [rbp - 0x38], rax
        jne     0x1a3
        mov     dword ptr [rbp - 0x50], 0
        cmp     dword ptr [rbp - 0x50], 4
        jg      0x197
        mov     eax, dword ptr [rbp - 0x50]
        cdqe
        movzx   eax, byte ptr [rbp + rax - 0x1f]
        xor     eax, 0xffffffee
        movzx   eax, al
        mov     edi, eax
        call    0xfffffffffee6b520
        add     dword ptr [rbp - 0x50], 1
        jmp     0x174
        mov     edi, 0xa
        call    0xfffffffffee6b520
        jmp     0x1d6
        mov     dword ptr [rbp - 0x4c], 0
        cmp     dword ptr [rbp - 0x4c], 4
        jg      0x1cc
        mov     eax, dword ptr [rbp - 0x4c]
        cdqe
        movzx   eax, byte ptr [rbp + rax - 0x1a]
        not     eax
        movzx   eax, al
        mov     edi, eax
        call    0xfffffffffee6b520
        add     dword ptr [rbp - 0x4c], 1
        jmp     0x1aa
        mov     edi, 0xa
        call    0xfffffffffee6b520
        mov     edi, 0
        call    0xfffffffffee699b0

capstone反汇编的结果中,跳转分支没有明确地指示,call的函数也和ida中的表示不太一样,不过对照着看也不是不行。

汇编分析

分析结果大体如下:

        push    rbp
        mov     rbp, rsp
        sub     rsp, 0x60
        mov     rax, qword ptr fs:[0x28]
        mov     qword ptr [rbp - 8], rax
        xor     eax, eax
        mov     qword ptr [rbp - 0x48], rsi             
        mov     byte ptr [rbp - 0x15], 0x84             //Your input : 
        mov     byte ptr [rbp - 0x14], 0xb2             //func: s[i] ^ 0xdd
        mov     byte ptr [rbp - 0x13], 0xa8
        mov     byte ptr [rbp - 0x12], 0xaf
        mov     byte ptr [rbp - 0x11], 0xfd
        mov     byte ptr [rbp - 0x10], 0xb4
        mov     byte ptr [rbp - 0xf], 0xb3
        mov     byte ptr [rbp - 0xe], 0xad
        mov     byte ptr [rbp - 0xd], 0xa8
        mov     byte ptr [rbp - 0xc], 0xa9
        mov     byte ptr [rbp - 0xb], 0xfd
        mov     byte ptr [rbp - 0xa], 0xe7
        mov     byte ptr [rbp - 9], 0xfd
        mov     byte ptr [rbp - 0x1f], 0x9c             //right
        mov     byte ptr [rbp - 0x1e], 0x87             //func: s[i] ^ 0xee
        mov     byte ptr [rbp - 0x1d], 0x89
        mov     byte ptr [rbp - 0x1c], 0x86
        mov     byte ptr [rbp - 0x1b], 0x9a
        mov     byte ptr [rbp - 0x1a], 0x88             //wrong
        mov     byte ptr [rbp - 0x19], 0x8d             //func: ~s[i]
        mov     byte ptr [rbp - 0x18], 0x90
        mov     byte ptr [rbp - 0x17], 0x91
        mov     byte ptr [rbp - 0x16], 0x98
        
        
        
        mov     dword ptr [rbp - 0x58], 0      
        
        cmp     dword ptr [rbp - 0x58], 0xc
        jg      0xa1                                    //跳出本段代码
        mov     eax, dword ptr [rbp - 0x58]
        cdqe
        movzx   eax, byte ptr [rbp + rax - 0x15]        //输出"Your input : %16c"
        xor     eax, 0xffffffdd
        movzx   eax, al
        mov     edi, eax
        call    0xfffffffffee6b520                        //即ida中的sub_412040,输出一个字符
        add     dword ptr [rbp - 0x58], 1
        jmp     0x7e
        mov     rax, qword ptr [rbp - 0x48]
        mov     rdi, rax
        call    0xfffffffffee6b320                      //goto 本段代码开始处



        mov     rax, qword ptr [rbp - 0x48]             //输入字符串赋值给var_40, var_38
        mov     rax, qword ptr [rax]                    //即[rbp - 0x40] ~ [rbp - 0x31]
        mov     qword ptr [rbp - 0x40], rax
        mov     rax, qword ptr [rbp - 0x48]
        mov     rax, qword ptr [rax + 8]
        mov     qword ptr [rbp - 0x38], rax



        mov     qword ptr [rbp - 0x30], 0               //v28 = 0xbd044a8aad044a8a ^ 0x2333333333333333
        movabs  rax, 0xbd044a8aad044a8a
        mov     qword ptr [rbp - 0x28], rax
        movabs  rax, 0x2333333333333333
        xor     qword ptr [rbp - 0x28], rax



        mov     dword ptr [rbp - 0x54], 0

        cmp     dword ptr [rbp - 0x54], 0x1f            //循环32次
        jg      0x14d                                   //跳出本段代码
        mov     rax, qword ptr [rbp - 0x38]
        shl     rax, 4                                  //v9 << 4
        mov     rdx, rax
        mov     rax, qword ptr [rbp - 0x38]
        shr     rax, 5                                  //v9 >> 5
        xor     rdx, rax                                //(v9 << 4) ^ (v9 >>5)
        mov     rax, qword ptr [rbp - 0x38]
        add     rax, rdx                                //v9 + ((v9 << 4) ^ (v9 >>5))
        xor     rax, qword ptr [rbp - 0x30]             //(v9 + ((v9 << 4) ^ (v9 >>5))) ^ v30
        add     qword ptr [rbp - 0x40], rax             //v8 = v8 + ((v9 + ((v9 << 4) ^ (v9 >>5))) ^ v30)

        mov     rax, qword ptr [rbp - 0x28]
        add     qword ptr [rbp - 0x30], rax             //v30 = v30 + v28

        mov     rax, qword ptr [rbp - 0x40]
        shl     rax, 4                                  //v8 << 4
        mov     rdx, rax
        mov     rax, qword ptr [rbp - 0x40]
        shr     rax, 5                                  //v8 >> 5
        xor     rdx, rax                                //(v8 << 4) ^ (v8 >> 5)
        mov     rax, qword ptr [rbp - 0x40]
        add     rax, rdx                                //v8 + ((v8 << 4) ^ (v8 >> 5))
        xor     rax, qword ptr [rbp - 0x30]             //(v8 + ((v8 << 4) ^ (v8 >> 5))) ^ v30
        add     qword ptr [rbp - 0x38], rax             //v9 = v9 + ((v8 + ((v8 << 4) ^ (v8 >> 5))) ^ v30)
        
        add     dword ptr [rbp - 0x54], 1
        jmp     0xef                                    //跳回本段代码开始处
        
        
        
        movabs  rax, 0xad4bb459940692aa                 //judge
        cmp     qword ptr [rbp - 0x40], rax
        jne     0x1a3                                   //goto wrong
        movabs  rax, 0x5665fd4ec447c6c9
        cmp     qword ptr [rbp - 0x38], rax
        jne     0x1a3                                   //goto wrong



        mov     dword ptr [rbp - 0x50], 0               //输出right
        cmp     dword ptr [rbp - 0x50], 4
        jg      0x197
        mov     eax, dword ptr [rbp - 0x50]
        cdqe
        movzx   eax, byte ptr [rbp + rax - 0x1f]
        xor     eax, 0xffffffee
        movzx   eax, al
        mov     edi, eax
        call    0xfffffffffee6b520
        add     dword ptr [rbp - 0x50], 1
        jmp     0x174
        mov     edi, 0xa
        call    0xfffffffffee6b520
        jmp     0x1d6                                   //goto exit



        mov     dword ptr [rbp - 0x4c], 0               //输出wrong
        cmp     dword ptr [rbp - 0x4c], 4
        jg      0x1cc
        mov     eax, dword ptr [rbp - 0x4c]
        cdqe
        movzx   eax, byte ptr [rbp + rax - 0x1a]
        not     eax
        movzx   eax, al
        mov     edi, eax
        call    0xfffffffffee6b520
        add     dword ptr [rbp - 0x4c], 1
        jmp     0x1aa
        mov     edi, 0xa
        call    0xfffffffffee6b520



        mov     edi, 0                                  //exit
        call    0xfffffffffee699b0

Your input:,right,wrong是如何输出的呢?

以right为例:

        movzx   eax, byte ptr [rbp + rax - 0x1f]
        xor     eax, 0xffffffee
        movzx   eax, al

rax是索引,rbp - 0x1f开始的5个数据对应right。

取出数据后与0xee异或,比如第一个数据[rbp - 0x1f] = 0x9c,chr(0x9c ^ 0xee) = 'r'

后面的4个数据也是这样处理,可得字符串right。

加密流程

下面看一下整体代码的流程,配合多次动调分析:

1.首先是Your Input : rightwrong这些字符所对应的加密数据入栈(数据经过解密处理后才可以得到明文)

2.输出Your Input :

3.将input传值到[rbp - 0x40] ~ [rbp - 0x31],我将var_40记为v8,var_38记为v9,以便和ida对应。

4.v28 = 0xbd044a8aad044a8a ^ 0x2333333333333333

5.tea加密,好像是变种,我不清楚。管它是原版还是变种,直接逆它的逻辑就行。共32次循环,每轮循环:

v8 = v8 + ((v9 + ((v9 << 4) ^ (v9 >>5))) ^ v30)
v30 += 0x2333333333333333 ^ 0xbd044a8aad044a8a
v9 = v9 + ((v8 + ((v8 << 4) ^ (v8 >> 5))) ^ v30)

6.判断v8 == 0xad4bb459940692aa , v9 == 0x5665fd4ec447c6c9

7.输出right或是wrong

8.exit

真正的解密脚本

def ull(n):
    return n & 0xffffffffffffffff

def hex2bytes(h):
    import struct
    b = b''
    for i in range(7,-1,-1):        #这里不是for i in range(8),也把我小坑了一把
        b += struct.pack('b', int(h[2+i*2:2+i*2+2], 16))
    print(b)
    return b

def md5(b):
    import hashlib
    m = hashlib.md5()
    m.update(b)
    mm = m.hexdigest()
    print(mm)

def get_v30(i):
    v10 = 0
    for i in range(i):
        v10 = ull(v10 + ull(0x2333333333333333 ^ 0xbd044a8aad044a8a))  #尼玛+和^的运算优先级把我给坑死了,大哭
    return v10

def solve():
    v8 = 0xad4bb459940692aa
    v9 = 0x5665fd4ec447c6c9
    for i in range(31, -1, -1):
        v30 = get_v30(i+1)
        v9 = ull(v9 - ull(ull(ull(v8 + ull(ull(v8 << 4) ^ ull(v8 >> 5))) ^ v30)))
        v30 = get_v30(i)
        v8 = ull(v8 - ull(ull(v9 + ull(ull(v9 << 4) ^ ull(v9 >>5))) ^ v30))
        print(hex(v8), hex(v9), hex(v30))
    b = hex2bytes(hex(v8)) + hex2bytes(hex(v9))
    print(b)
    md5(b)

solve()

# 输入为:ae569d8654d7b54e
# 按照题目要求,flag为其md5:6efa713d07cd5bc15d70da8c6b120e5d

注意大小端,注意+^的运算优先级。注意v30的改变是在v8和v9中间。

Last modification:July 29th, 2020 at 12:01 am