REVERSE-Daily(4)-Elfcrackme2

Posted on 2016-09-14 02:08  LOMOoO  阅读(521)  评论(0编辑  收藏  举报

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

链接: 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\nPlease 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 '\n'
                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('\n','')
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)