zoukankan      html  css  js  c++  java
  • REVERSE-Daily(4)-Elfcrackme2

    非常坑爹的一道题目,看似非常简单,实则有套路

    链接: http://pan.baidu.com/s/1i4XLCd3 密码:9zho

    为了练手 我会写出三种解法,包括 结合ascii码值范围的爆破,动态调试解法,静态调试解法

    (感谢我xx学长的支持和某位网友的writeup)

    1.首先拿到该程序,丢进ida会发现这是个elf程序,程序主体看起来也很简单,在ubuntu下也能正常跑起来

    int __cdecl main()
    {
      int v0; // eax@1
      char *v1; // edx@2
      int v2; // ecx@3
      int v4; // [sp+1Ch] [bp-10h]@4
    
      printf("%s
    Please input your flag:", off_8049900);
      __isoc99_scanf("%s", &byte_8049908);
      v0 = off_8049900 - (char *)main;
      if ( off_8049900 > (char *)main )
      {
        v1 = (char *)main;
        do
        {
          v2 = (unsigned __int8)*v1++;
          v0 = v2 ^ (v0 >> 27) ^ 32 * v0;
        }
        while ( v1 != off_8049900 );
      }
      v4 = v0;
      if ( __PAIR__((unsigned __int8)(byte_8049909 ^ BYTE1(v4)), (unsigned __int8)(byte_8049908 ^ v0)) != word_80498F0
        || ((unsigned __int8)byte_804990A ^ BYTE2(v0)) != byte_80498F2
        || (BYTE3(v0) ^ (unsigned __int8)byte_804990B) != byte_80498F3
        || ((unsigned __int8)v0 ^ (unsigned __int8)byte_804990C) != byte_80498F4
        || (BYTE1(v4) ^ (unsigned __int8)byte_804990D) != byte_80498F5
        || (BYTE2(v0) ^ (unsigned __int8)byte_804990E) != byte_80498F6
        || (BYTE3(v0) ^ (unsigned __int8)byte_804990F) != byte_80498F7
        || ((unsigned __int8)v0 ^ (unsigned __int8)byte_8049910) != byte_80498F8
        || (BYTE1(v4) ^ (unsigned __int8)byte_8049911) != byte_80498F9
        || (BYTE2(v0) ^ (unsigned __int8)byte_8049912) != byte_80498FA
        || (BYTE3(v0) ^ (unsigned __int8)byte_8049913) != byte_80498FB
        || ((unsigned __int8)dword_8049914 ^ (unsigned __int8)v0) != byte_80498FC
        || (*(_WORD *)((char *)&dword_8049914 + 1) ^ *(_WORD *)((char *)&v4 + 1)) != word_80498FD
        || (BYTE3(dword_8049914) ^ BYTE3(v0)) != byte_80498FF )
      {
        puts("You are wrong");
      }
      else
      {
        puts("You are right");
      }
      return 0;
    }

    2.结合汇编语言,对程序可以做一个大致的分析,第二个if的判断是分别将输入的字符串的每一位和v4/v0(32位)的第一,二,三,四个字节做异或并将结果和0x80498f0起始处的字符串做比较,只有16位全相等时才会输出正确

    3.瞄了一眼,v4和v0的值发现和输入字符串无关,应该是个固定值,而v4=v0对应的汇编语言是mov edx,eax,所以正常的想法是用gdb在0x08048416处下断点

    .text:080483EB                 mov     eax, ebx
    .text:080483ED                 sub     eax, offset main
    .text:080483F2                 cmp     ebx, offset main
    .text:080483F8                 jbe     short loc_8048416
    .text:080483FA                 mov     edx, offset main
    .text:080483FF                 nop
    .text:08048400
    .text:08048400 loc_8048400:                            ; CODE XREF: main+64j
    .text:08048400                 mov     ecx, eax
    .text:08048402                 sar     ecx, 1Bh
    .text:08048405                 shl     eax, 5
    .text:08048408                 xor     eax, ecx
    .text:0804840A                 movzx   ecx, byte ptr [edx]
    .text:0804840D                 add     edx, 1
    .text:08048410                 xor     eax, ecx
    .text:08048412                 cmp     edx, ebx
    .text:08048414                 jnz     short loc_8048400
    .text:08048416
    .text:08048416 loc_8048416:                            ; CODE XREF: main+48j
    .text:08048416                 mov     edx, eax
    .text:08048418                 xor     dl, ds:byte_8049908
    .text:0804841E                 cmp     dl, byte ptr word_80498F0
    .text:08048424                 mov     [esp+1Ch], eax
    .text:08048428                 jnz     loc_8048566
    .text:0804842E                 movzx   edx, byte ptr [esp+1Dh]
    .text:08048433                 mov     ecx, edx
    .text:08048435                 xor     cl, ds:byte_8049909
    .text:0804843B                 cmp     cl, byte ptr word_80498F0+1
    .text:08048441                 jnz     loc_8048566
    .text:08048447                 movzx   ecx, byte ptr [esp+1Eh]
    .text:0804844C                 mov     ebx, ecx
    .text:0804844E                 xor     bl, ds:byte_804990A
    .text:08048454                 cmp     bl, byte_80498F2
    .text:0804845A                 jnz     loc_8048566

    4.如果你的思路和我一样,那么恭喜你入套了,不过还是先这样走完

    5.用gdb下断点后可以得到eax的值,如图为:0xd90c5525

    6.于是我尝试写了这样一个python脚本:

    a=[0x18,0x5b,0xbf,0x38,0x34,0x5a,0x99,0x4d,0x2e,0x73,0xbb,0x4e,0x23,0x76,0x9f,0x3]
    b=[0x25,0x55,0x0c,0xd9]
    c=''
    for i in range(16):
        d=i%4
        c+=(chr(a[i]^b[d]))
    print c

    7.当然你得到的只会是乱码。。。

    8.好吧,现在是早上01:43:32我就不废话了,我还要睡觉呢!错误的原因在于第一个if语句是对text段的读操作,并对读到的值进行一系列的运算操作,从main函数一直读到0x0804872A,当然也会读到你下断点的地方,而据网上资料所讲b *0x 操作是将相应处内存改为0xcc,这样一来你得到的eax和edx的值就会不对,那该怎么办呢

    解法一:动态调试的方法

    这期间我犯了很多很多的错误

    错误一:我分别用了 b *0x08048418 if $eip==0x08048146//r 和b *0x080483fa//r//b *08048418 if $edx==$dbx这两种方法

               错误的原因在于理解错了breakpoint指令的意思  我以为b if 是时时监控 条件成立就下断点  实际上是运行到断点处才判断。。

               如果是犯同样错误的人自己理解吧,这样是不行的

    错误二:watch $eip==0x08048418      也是错的 具体原因我还没有查

    直接写出最终解法吧:内存访问监控  涉及到的gdb指令是awatch(awatch是读写断点,内存被读或着写时都会断。而rwatch是读时断,watch是写时断。)

    我这里用到的是awatch

    指令序列为awatch *0x08048148//r//info b//d 1//s//s//...//b *0x08048148//c   

    还有其他很多方法:b *0x80483fa//r//d 1//awatch *0x8049908//c

    最简单的方法:awatch *0x80498f0

    这样一来就能得到正确的eax值,再结合最开始写的脚本就可以得到flag

    方法二:爆破

    这样需要结合flag的格式进行一定的手动猜测,一个简单python脚本最没技术含量的吧:

    a=[0x18,0x5b,0xbf,0x38,0x34,0x5a,0x99,0x4d,0x2e,0x73,0xbb,0x4e,0x23,0x76,0x9f,0x3]
    b='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTTUVWXYZ0123456789{}_()='
    for i in range(4):
        for j in range(0x00,0xff):
            for k in range(4):
                d=chr(j^a[i+4*k])
                if d not in b:
                    break
                if k==3:
                    print '
    '
                    print i,hex(j)
                    for l in range(4):
                        print chr(j^a[i+4*l]),

    方法三:静态调试

    写脚本直接访问文件对应地址的数据进行计算,这里计算得到的都是eax寄存器的值

    分别用c和python写了两个脚本,两种方法

    先看c的,直接访问源文件,这里有一个问题,文本要以rb方式打开,r不行,因为

    以“r”方式读取,系统只读出ascii中可显示字符;如果不可显示(控制字符),系统会把它滤掉。但是“rb”的方式则不会。

    #include<stdlib.h>
    #include<stdio.h>
    
    void main()
    {
        FILE *fp;
        fp=fopen("C:\Users\tLOMO\Desktop\Elfcrackme2","rb");
        int i=0x37a; //(main)0x80483b0~0x804872a地址之间的二进制文件内容的长度0x37A
        if(fp!=NULL)
        {
            int m,n;
            int a;
            for(n=0;n<0x3b0;n++)//main从0x3b0开始
            {
                fgetc(fp);
            }
            for(m=0;m<0x37a;m++)
            {
                a=fgetc(fp);
                i=a^(i>>27)^(32*i);
            }
            printf("value of eax is:%x",i);
        }
        else
        {
            printf("the file is blank");
        }
        fclose(fp);
        system("pause");
    }

    再来看python的,这里也遇到了一个比较严重的问题,不注意的话可能不会发现,

    因为源程序是c语言,int默认为有符号32位,那么c>>27,如果是负数的话,高位会补f而不是0,而python是个弱类型语言,64位python,int为64位,所以会出问题,所以这里要做一个转换:

    a='''55 89 E5 57 56 53 83 E4  F0 83 EC 20 A1 00 99 04
     08 C7 04 24 F0 86 04 08  89 44 24 04 E8 8F FF FF
     FF C7 44 24 04 08 99 04  08 C7 04 24 0B 87 04 08
     E8 BB FF FF FF 8B 1D 00  99 04 08 89 D8 2D B0 83
     04 08 81 FB B0 83 04 08  76 1C BA B0 83 04 08 90
     89 C1 C1 F9 1B C1 E0 05  31 C8 0F B6 0A 83 C2 01
     31 C8 39 DA 75 EA 89 C2  32 15 08 99 04 08 3A 15
     F0 98 04 08 89 44 24 1C  0F 85 38 01 00 00 0F B6
     54 24 1D 89 D1 32 0D 09  99 04 08 3A 0D F1 98 04
     08 0F 85 1F 01 00 00 0F  B6 4C 24 1E 89 CB 32 1D
     0A 99 04 08 3A 1D F2 98  04 08 0F 85 06 01 00 00
     0F B6 7C 24 1F 0F B6 1D  0B 99 04 08 31 FB 3A 1D
     F3 98 04 08 0F 85 EC 00  00 00 0F B6 1D 0C 99 04
     08 31 C3 3A 1D F4 98 04  08 0F 85 D7 00 00 00 0F
     B6 1D 0D 99 04 08 31 D3  3A 1D F5 98 04 08 0F 85
     C2 00 00 00 0F B6 1D 0E  99 04 08 31 CB 3A 1D F6
     98 04 08 0F 85 AD 00 00  00 0F B6 1D 0F 99 04 08
     31 FB 3A 1D F7 98 04 08  0F 85 98 00 00 00 0F B6
     1D 10 99 04 08 31 C3 3A  1D F8 98 04 08 0F 85 83
     00 00 00 0F B6 1D 11 99  04 08 31 D3 3A 1D F9 98
     04 08 75 72 0F B6 1D 12  99 04 08 31 CB 3A 1D FA
     98 04 08 75 61 0F B6 1D  13 99 04 08 31 FB 3A 1D
     FB 98 04 08 75 50 33 05  14 99 04 08 3A 05 FC 98
     04 08 75 42 32 15 15 99  04 08 3A 15 FD 98 04 08
     75 34 32 0D 16 99 04 08  3A 0D FE 98 04 08 75 26
     89 FB 32 1D 17 99 04 08  3A 1D FF 98 04 08 75 16
     C7 04 24 0E 87 04 08 E8  14 FE FF FF 8D 65 F4 31
     C0 5B 5E 5F 5D C3 C7 04  24 1C 87 04 08 E8 FE FD
     FF FF EB E8 31 ED 5E 89  E1 83 E4 F0 50 54 52 68
     60 86 04 08 68 70 86 04  08 51 56 68 B0 83 04 08
     E8 FB FD FF FF F4 90 90  90 90 90 90 90 90 90 90
     B8 07 99 04 08 2D 04 99  04 08 83 F8 06 77 02 F3
     C3 B8 00 00 00 00 85 C0  74 F5 55 89 E5 83 EC 18
     C7 04 24 04 99 04 08 FF  D0 C9 C3 90 8D 74 26 00
     B8 04 99 04 08 2D 04 99  04 08 C1 F8 02 89 C2 C1
     EA 1F 01 D0 D1 F8 75 02  F3 C3 BA 00 00 00 00 85
     D2 74 F5 55 89 E5 83 EC  18 89 44 24 04 C7 04 24
     04 99 04 08 FF D2 C9 C3  90 8D B4 26 00 00 00 00
     80 3D 04 99 04 08 00 75  13 55 89 E5 83 EC 08 E8
     7C FF FF FF C6 05 04 99  04 08 01 C9 F3 C3 66 90
     A1 D0 97 04 08 85 C0 74  1E B8 00 00 00 00 85 C0
     74 15 55 89 E5 83 EC 18  C7 04 24 D0 97 04 08 FF
     D0 C9 E9 79 FF FF FF E9  74 FF FF FF 90 90 90 90
     55 89 E5 5D C3 8D 74 26  00 8D BC 27 00 00 00 00
     55 89 E5 57 56 53 E8 4F  00 00 00 81 C3 4D 12 00
     00 83 EC 1C E8 9B FC FF  FF 8D BB 04 FF FF FF 8D
     83 00 FF FF FF 29 C7 C1  FF 02 85 FF 74 24 31 F6
     8B 45 10 89 44 24 08 8B  45 0C 89 44 24 04 8B 45
     08 89 04 24 FF 94 B3 00  FF FF FF 83 C6 01 39 FE
     72 DE 83 C4 1C 5B 5E 5F  5D C3 8B 1C 24 C3 90 90
     55 89 E5 53 83 EC 04 E8  00 00 00 00 5B 81 C3 EC
     11 00 00 59 5B C9 C3 00  03 00 00 00 01 00 02 00
     25 73 0A 50 6C 65 61 73  65 20 69 6E 70 75 74 20
     79 6F 75 72 20 66 6C 61  67 3A 00 25 73 00 59 6F
     75 20 61 72 65 20 72 69  67 68 74 00 59 6F 75 20
     61 72 65 20 77 72 6F 6E  67 00'''.replace('  ',' ').replace('
    ','')
    b=a.split(' ')
    print hex(len(b))
    c=0x37a
    for i in b:
        e=c>>27
        d=c&0x80000000
        if(d==0x80000000):
            e|=0xffffffe0
        c=int(i,16)^e^32*c
        c&= 0xffffffff
    print hex(c)
  • 相关阅读:
    旋转编码器控制线扫相机
    函数被const修饰,const修饰的究竟是谁?
    静态函数不能引用非静态成员变量的原因,this指针
    Ubuntu18.04 截图工具flameshot
    基于STM32的uCGUI移植和优化
    基于bootsplash的嵌入式linux启动画面定制
    uC/OS-II源码分析(一)
    uC/OS-II源码分析(二)
    uC/OS-II源码分析(三)
    uC/OS-II源码分析(四)
  • 原文地址:https://www.cnblogs.com/lomooo/p/5870433.html
Copyright © 2011-2022 走看看