V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hxndg
V2EX  ›  信息安全

小游戏: crack trycrackme

  •  
  •   hxndg · 2022-09-03 21:10:24 +08:00 · 1316 次点击
    这是一个创建于 819 天前的主题,其中的信息可能已经有所发展或是发生改变。

    trycrackme 的下载地址为 https://crackmes.one/crackme/61c8deff33c5d413767ca0ea ,直接从汇编就能看出来到底在做什么

         Dump of assembler code for function main:
            # 标准开局,保存堆栈
            0x00005555555551af <+0>:        push   %rbp
            0x00005555555551b0 <+1>:        mov    %rsp,%rbp
            0x00005555555551b3 <+4>:        sub    $0xe0,%rsp
            #存储 argc & argv ,不过这里没用到
            0x00005555555551ba <+11>:       mov    %edi,-0xd4(%rbp)
            0x00005555555551c0 <+17>:       mov    %rsi,-0xe0(%rbp)
            0x00005555555551c7 <+24>:       mov    %fs:0x28,%rax
            0x00005555555551d0 <+33>:       mov    %rax,-0x8(%rbp)
            0x00005555555551d4 <+37>:       xor    %eax,%eax
            # 注意这个常量,是我们比较的关键
            0x00005555555551d6 <+39>:       movabs $0x3534323773734034,%rax
            # 存储到了-0xbb(%rbp)的位置
            0x00005555555551e0 <+49>:       mov    %rax,-0xbb(%rbp)
            # 给上面的常量补充了两个字节的数字,拼接到最后面,拼接完以后看一下具体的内容
            # (gdb) x/16xb 140737488347509
            # 0x7fffffffe175: 0x34    0x40    0x73    0x73    0x37    0x32    0x34    0x35
            # 0x7fffffffe17d: 0x33    0x36    0x00    0x00    0x00    0x00    0x00    0x00
            0x00005555555551e7 <+56>:       movw   $0x3633,-0xb3(%rbp)
            0x00005555555551f0 <+65>:       movb   $0x0,-0xb1(%rbp)
            0x00005555555551f7 <+72>:       lea    -0xbb(%rbp),%rax
            0x00005555555551fe <+79>:       mov    %rax,%rdi
            0x0000555555555201 <+82>:       callq  0x555555555050 <strlen@plt>
            # 存储我们刚才拼接出来的字符串长度,明确地看出来是 10
            0x0000555555555206 <+87>:       mov    %eax,-0xc0(%rbp)
            0x000055555555520c <+93>:       mov    $0x0,%eax
            # 调用 banner 打印一些 flag ,没有啥用,不用管
            0x0000555555555211 <+98>:       callq  0x555555555199 <banner>
            # 准备调用 printf 提示用户输入数据,格式化字符串为 Put the key:
            0x0000555555555216 <+103>:      lea    0xef9(%rip),%rax        # 0x555555556116
            0x000055555555521d <+110>:      mov    %rax,%rdi
            0x0000555555555220 <+113>:      mov    $0x0,%eax
            0x0000555555555225 <+118>:      callq  0x555555555070 <printf@plt>
            # 记住这个-0xb0(%rbp)的地址,这个是存储 scanf 输入进来的地址
            0x000055555555522a <+123>:      lea    -0xb0(%rbp),%rax
            0x0000555555555231 <+130>:      mov    %rax,%rsi
            0x0000555555555234 <+133>:      lea    0xee9(%rip),%rax        # 0x555555556124
            0x000055555555523b <+140>:      mov    %rax,%rdi
            0x000055555555523e <+143>:      mov    $0x0,%eax
            0x0000555555555243 <+148>:      callq  0x555555555080 <__isoc99_scanf@plt>
            # 初始化两个变量,分别存储上面-0xbb(%rbp)字符串处理过的字符个数
            # 和要算出来作为正确的 code 所处理过的字符个数
            0x0000555555555248 <+153>:      movl   $0x0,-0xc8(%rbp)
            0x0000555555555252 <+163>:      movl   $0x0,-0xc4(%rbp)
       |--- 0x000055555555525c <+173>:      jmp    0x5555555552ab <main+252>
       |    # -0xc8(%rbp)是刚才那个常量字符串处理过的 byte 数,所以 cltq 下面那句就很明显了
    |-----> 0x000055555555525e <+175>:      mov    -0xc8(%rbp),%eax
    |  |    0x0000555555555264 <+181>:      cltq
    |  |    # 这里的意思是把刚才-0xbb(%rbp)字符串的字符,hex 形式丢到 eax
    |  |    0x0000555555555266 <+183>:      movzbl -0xbb(%rbp,%rax,1),%eax
    |  |    # 先做有符号数拓展,再做无符号数拓展,不过都小于 0x80 ,所以无所谓了
    |  |    0x000055555555526e <+191>:      movsbl %al,%eax
    |  |    0x0000555555555271 <+194>:      movzbl %al,%eax
    |  |    0x0000555555555274 <+197>:      mov    -0xc4(%rbp),%edx
    |  |    0x000055555555527a <+203>:      movslq %edx,%rdx
    |  |    # rcx 存储了计算结果的首地址, -0x70(%rbp)是我们最后比较的参照物的地址
    |  |    0x000055555555527d <+206>:      lea    -0x70(%rbp),%rcx
    |  |    # 加上已经结算过的结果,实际上就是挪动指针,存储下面 sprintf 的结果
    |  |    0x0000555555555281 <+210>:      add    %rdx,%rcx
    |  |    0x0000555555555284 <+213>:      mov    %eax,%edx
    |  |    # 这个字符是 %02x
    |  |    0x0000555555555286 <+215>:      lea    0xe9a(%rip),%rax        # 0x555555556127
    |  |    0x000055555555528d <+222>:      mov    %rax,%rsi
    |  |    0x0000555555555290 <+225>:      mov    %rcx,%rdi
    |  |    0x0000555555555293 <+228>:      mov    $0x0,%eax
    |  |    # 这里调用 sprintf 的含义就非常清楚了,从 hex 编码转换为字符串
    |  |    # 原先是 hex 0x34 ,那么转换为字符串"34"
    |  |    0x0000555555555298 <+233>:      callq  0x555555555090 <sprintf@plt>
    |  |    # hex 字符串处理过一 byte 后挪一
    |  |    # 而 sprintf 处理的结果是 2byte (两个 char 字符)
    |  |    0x000055555555529d <+238>:      addl   $0x1,-0xc8(%rbp)
    |  |    0x00005555555552a4 <+245>:      addl   $0x2,-0xc4(%rbp)
    |  |    #开始处理,先找到第一个字符,看看和上面的字符串长度 10 的大小,判断有没有处理完
    |  ---> 0x00005555555552ab <+252>:      mov    -0xc8(%rbp),%eax
    |       0x00005555555552b1 <+258>:      cmp    -0xc0(%rbp),%eax
    |------ 0x00005555555552b7 <+264>:      jl     0x55555555525e <main+175>
            0x00005555555552b9 <+266>:      lea    -0x70(%rbp),%rax
            0x00005555555552bd <+270>:      mov    %rax,%rdi
            0x00005555555552c0 <+273>:      callq  0x555555555050 <strlen@plt>
            0x00005555555552c5 <+278>:      mov    %rax,%rdx
            # 算出来的正确的 code
            0x00005555555552c8 <+281>:      lea    -0x70(%rbp),%rcx
            # 输入的 code
            0x00005555555552cc <+285>:      lea    -0xb0(%rbp),%rax
            0x00005555555552d3 <+292>:      mov    %rcx,%rsi
            0x00005555555552d6 <+295>:      mov    %rax,%rdi
            # 比较
            0x00005555555552d9 <+298>:      callq  0x555555555030 <strncmp@plt>
            0x00005555555552de <+303>:      test   %eax,%eax
       |----0x00005555555552e0 <+305>:      je     0x5555555552fd <main+334>
       |    0x00005555555552e2 <+307>:      lea    0xe43(%rip),%rax        # 0x55555555612c
       |    0x00005555555552e9 <+314>:      mov    %rax,%rdi
       |    0x00005555555552ec <+317>:      mov    $0x0,%eax
       |    0x00005555555552f1 <+322>:      callq  0x555555555070 <printf@plt>
       |    0x00005555555552f6 <+327>:      mov    $0xffffffff,%eax
       |    0x00005555555552fb <+332>:      jmp    0x555555555316 <main+359>
       |    # 这里就是正确的结果,所以我们只需要输入一个字符串和上面常量字符串从 hex 到字符串转换的结果即可
       |--->0x00005555555552fd <+334>:      lea    0xe3b(%rip),%rax        # 0x55555555613f
            0x0000555555555304 <+341>:      mov    %rax,%rdi
            0x0000555555555307 <+344>:      mov    $0x0,%eax
            0x000055555555530c <+349>:      callq  0x555555555070 <printf@plt>
            0x0000555555555311 <+354>:      mov    $0x0,%eax
            0x0000555555555316 <+359>:      mov    -0x8(%rbp),%rdx
            0x000055555555531a <+363>:      sub    %fs:0x28,%rdx
            0x0000555555555323 <+372>:      je     0x55555555532a <main+379>
            0x0000555555555325 <+374>:      callq  0x555555555060 <__stack_chk_fail@plt>
            0x000055555555532a <+379>:      leaveq
            0x000055555555532b <+380>:      retq
         End of assembler dump.        
    









































































































    简单说一下过程

    • 先从常量拼接出来要产生的字符串,0x3534323773734034 拼接 0x3633
    • 对每一 byte 调用 sprintf ,使用格式:%02x ,从 hex 转换为两 byte 的字符串
    • 转换完的结果和我们输入的结果做对比,一致则成功
    3 条回复
    qfdk
        1
    qfdk  
       2022-09-04 23:10:32 +08:00 via iPhone
    然而我直接实战了 哈哈哈
    hxndg
        2
    hxndg  
    OP
       2022-09-05 11:22:46 +08:00
    @qfdk 直接实战肯定更好,小游戏嘛,休闲娱乐
    qfdk
        3
    qfdk  
       2022-09-05 14:01:50 +08:00
    @hxndg #2 想起来 以前看的加密与解密 那时候还看不懂
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2695 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 07:27 · PVG 15:27 · LAX 23:27 · JFK 02:27
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.