欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  网络运营

西南某比赛的三个简单逆向题分析

程序员文章站 2022-03-19 11:29:07
忙碌了一周,虽然很辛苦,但屏幕上不时跳出的KEY还是会让我们信心倍增。这次参加西南某大学的信息安全大赛,收获挺多,也深刻了解到自己和大牛的差距,这里分享三个简单的逆向题。 比赛还有一个多小时...
忙碌了一周,虽然很辛苦,但屏幕上不时跳出的KEY还是会让我们信心倍增。这次参加西南某大学的信息安全大赛,收获挺多,也深刻了解到自己和大牛的差距,这里分享三个简单的逆向题。

比赛还有一个多小时就结束了,大家都没做题了,希望这时候发破文出来对比赛没有影响。

第一题:

这是一个非常简单的CrackMe,找注册码很容易,写注册机也只需简要分析。

首先载入OD,在GetDlgItemTextA函数处下断,运行后来到这里。

代码:

00401241  |.  6A 0A         PUSH 0A                                  ; /Count = A (10.)

00401243  |.  52            PUSH EDX                                 ; |Buffer => CrackMe.0040300C

00401244  |.  68 F2030000   PUSH 3F2                                 ; |ControlID = 3F2 (1010.)

00401249  |.  FFB5 FCFEFFFF PUSH DWORD PTR SS:[EBP-104]              ; |hWnd

0040124F  |.  E8 1A010000   CALL <JMP.&user32.GetDlgItemTextA>       ; \GetDlgItemTextA

00401254  |.  8D05 0C304000 LEA EAX,DWORD PTR DS:[40300C]

0040125A  |.  50            PUSH EAX                                 ; /Arg1 => 0040300C ASCII "Speday"

0040125B  |.  E8 2DFEFFFF   CALL CrackMe.0040108D                    ; \CrackMe.0040108D

00401260  |.  8D15 0C304000 LEA EDX,DWORD PTR DS:[40300C]

00401266  |.  6A 0A         PUSH 0A                                  ; /Count = A (10.)

00401268  |.  52            PUSH EDX                                 ; |Buffer => CrackMe.0040300C

00401269  |.  68 F3030000   PUSH 3F3                                 ; |ControlID = 3F3 (1011.)

0040126E  |.  FFB5 FCFEFFFF PUSH DWORD PTR SS:[EBP-104]              ; |hWnd

00401274  |.  E8 F5000000   CALL <JMP.&user32.GetDlgItemTextA>       ; \GetDlgItemTextA

00401279  |.  68 20304000   PUSH CrackMe.00403020                    ; /String2 = ""

0040127E  |.  68 0C304000   PUSH CrackMe.0040300C                    ; |String1 = "Speday"

00401283  |.  E8 3A010000   CALL <JMP.&kernel32.lstrcmpA>            ; \lstrcmpA

00401288  |.  85C0          TEST EAX,EAX

0040128A  |.  75 1B         JNZ SHORT CrackMe.004012A7

0040128C  |.  6A 40         PUSH 40                                  ; /Style = MB_OK|MB_ICONASTERISK|MB_APPLMODAL

0040128E  |.  FF35 A1204000 PUSH DWORD PTR DS:[4020A1]               ; |Title = "成功"

00401294  |.  FF35 A5204000 PUSH DWORD PTR DS:[4020A5]               ; |Text = "注册成功!"

0040129A  |.  FFB5 FCFEFFFF PUSH DWORD PTR SS:[EBP-104]              ; |hOwner

004012A0  |.  E8 E7000000   CALL <JMP.&user32.MessageBoxA>           ; \MessageBoxA

很明显,0040125B这个CALL是关键CALL,单步进去看一下。

代码:

0040108D  /$  55            PUSH EBP

0040108E  |.  8BEC          MOV EBP,ESP

00401090  |.  83C4 F4       ADD ESP,-0C

00401093  |.  8B45 08       MOV EAX,DWORD PTR SS:[EBP+8]             ;  计算name字段的长度

00401096  |.  50            PUSH EAX                                 ; /String

00401097  |.  E8 2C030000   CALL <JMP.&kernel32.lstrlenA>            ; \lstrlenA

0040109C  |.  48            DEC EAX                                  ;  长度减1,后面有用

0040109D  |.  8945 FC       MOV DWORD PTR SS:[EBP-4],EAX

004010A0  |.  8B75 08       MOV ESI,DWORD PTR SS:[EBP+8]

004010A3  |.  8D3D 20304000 LEA EDI,DWORD PTR DS:[403020]

004010A9  |.  33DB          XOR EBX,EBX

004010AB  |.  895D F8       MOV DWORD PTR SS:[EBP-8],EBX

004010AE  |.  EB 4B         JMP SHORT CrackMe.004010FB

004010B0  |>  33C0          /XOR EAX,EAX

004010B2  |.  8A0433        |MOV AL,BYTE PTR DS:[EBX+ESI]            ;  取name的第一个字符

004010B5  |.  C1F8 04       |SAR EAX,4                               ;  右移4位

004010B8  |.  8845 F7       |MOV BYTE PTR SS:[EBP-9],AL              ;  存到变量中

004010BB  |.  33D2          |XOR EDX,EDX

004010BD  |.  8B45 F8       |MOV EAX,DWORD PTR SS:[EBP-8]

004010C0  |.  B9 02000000   |MOV ECX,2

004010C5  |.  F7F1          |DIV ECX                                 ;  eax=eax/2

004010C7  |.  33C0          |XOR EAX,EAX                             ;  edx=eax%ecx

004010C9  |.  33C9          |XOR ECX,ECX

004010CB  |.  83FA 01       |CMP EDX,1                               ;  判断edx(余数)是否等于1

004010CE  |.  74 0F         |JE SHORT CrackMe.004010DF

004010D0  |.  8A4433 01     |MOV AL,BYTE PTR DS:[EBX+ESI+1]          ;  取后一个字符

004010D4  |.  C1E0 1C       |SHL EAX,1C                              ;  左移28位

004010D7  |.  C1E8 1C       |SHR EAX,1C                              ;  右移28位

004010DA  |.  83C0 41       |ADD EAX,41                              ;  加上大写字母A的ascii码

004010DD  |.  EB 0D         |JMP SHORT CrackMe.004010EC

004010DF  |>  8A4433 FF     |MOV AL,BYTE PTR DS:[EBX+ESI-1]          ;  取前一个字符

004010E3  |.  C1E0 1C       |SHL EAX,1C                              ;  左移28位

004010E6  |.  C1E8 1C       |SHR EAX,1C                              ;  右移28位

004010E9  |.  83C0 61       |ADD EAX,61                              ;  加上小写字母a的ascii码

004010EC  |>  8A4D F7       |MOV CL,BYTE PTR SS:[EBP-9]              ;  取出上面变量中存的值

004010EF  |.  03C1          |ADD EAX,ECX                             ;  加在eax上

004010F1  |.  88043B        |MOV BYTE PTR DS:[EBX+EDI],AL            ;  存起来

004010F4  |.  8B5D F8       |MOV EBX,DWORD PTR SS:[EBP-8]

004010F7  |.  43            |INC EBX

004010F8  |.  895D F8       |MOV DWORD PTR SS:[EBP-8],EBX

004010FB  |>  8B45 FC        MOV EAX,DWORD PTR SS:[EBP-4]

004010FE  |.  3BD8          |CMP EBX,EAX                             ;  判断是不是倒数第二个字符

00401100  |.^ 7C AE         \JL SHORT CrackMe.004010B0

00401102  |.  8A0433        MOV AL,BYTE PTR DS:[EBX+ESI]             ;  取最后一位字符

00401105  |.  C1E0 1C       SHL EAX,1C                               ;  左移28位

00401108  |.  C1F8 1C       SAR EAX,1C                               ;  右移28位

0040110B  |.  83C0 31       ADD EAX,31                               ;  加上数字1的ascii码

0040110E  |.  88043B        MOV BYTE PTR DS:[EBX+EDI],AL             ;  存起来

00401111  |.  33C0          XOR EAX,EAX

00401113  |.  43            INC EBX

00401114  |.  88043B        MOV BYTE PTR DS:[EBX+EDI],AL             ;  末尾置0

00401117  |.  C9            LEAVE

00401118  \.  C2 0400       RETN 4

结合上面的分析,自己用C语言写了个注册机,供大家参考。

代码:

#include<stdio.h>

#include<string.h>

#include<windows.h>

 

main()

{

  char name[100];

  char key[100];

  unsigned int i,j,a,b,c,d;

  int m;

  printf("Please input a name:\t");

  scanf("%s",name);

  j=lstrlen(name);

  for(i=0;i<j-1;i++)

  {

    m=name[i];

    a=m>>4;

    c=a;

    d=i%2;

    if(d==1)

    {

      a=name[i-1];

      a=a<<0x1c;

      a=a>>0x1c;

      a=a+0x61;

      a=a+c;

      key[i]=a;

    }

    else

    {

      a=name[i+1];

      a=a<<0x1c;

      a=a>>0x1c;

      a=a+0x41;

      a=a+c;

      key[i]=a;

    }

  }

  m=name[j-1];

  m=m<<0x1c;

  m=m>>0x1c;

  m=m+0x31;

  key[i]=m;

  key[i+1]=0;

  printf("The key is :\t%s\n",key);

  getchar();

  getchar();

}

第二个题:

平时几乎没遇到过.net的程序,这次居然遇到了,所以拿出来与大家分享下。

第一次看到.net程序,感觉无从下手。连用什么样的编译器都不知道,于是翻出《加密与解密》看了会,然后到看雪官网上下载了一个反编译器:Reflector。

将程序拉进去,一头雾水,都是抱着试试看的心态逐个找,后来惊讶的发现.net程序居然可以完全反编译成这样。

代码:

Private Sub button1_Click(ByVal sender As Object, ByVal e As EventArgs)

    Dim array As Char() = Me.textBox1.Text.Replace("e", "").Replace("E", "e").Replace("3", "E").ToCharArray

    Array.Reverse(array)

    Dim s As New String(array)

    Dim chArray2 As Char() = Convert.ToBase64String(Encoding.GetEncoding("UTF-8").GetBytes(s)).ToCharArray

    Array.Reverse(chArray2)

    Dim str4 As New String(chArray2)

    If (str4 = "==wYjsCZlN2TtBVasVWP0hUa181YIFTYxETRudUR") Then

        MessageBox.Show(ChrW(39564) & ChrW(35777) & ChrW(36890) & ChrW(36807) & ChrW(65281) & "KEY" & ChrW(23601) & ChrW(26159) & ChrW(20320) & ChrW(36755) & ChrW(20837) & ChrW(30340) & ChrW(23494) & ChrW(30721), ChrW(25104) & ChrW(21151), MessageBoxButtons.OK, MessageBoxIcon.Asterisk)

    Else

        MessageBox.Show(ChrW(39564) & ChrW(35777) & ChrW(19981) & ChrW(36890) & ChrW(36807) & ChrW(65281) & ChrW(35831) & ChrW(37325) & ChrW(26032) & ChrW(36755) & ChrW(20837), ChrW(22833) & ChrW(36133), MessageBoxButtons.OK, MessageBoxIcon.Hand)

        Me.textBox1.Text = ""

    End If

End Sub

虽然反编译出来了,但是没有学过.net程序,还是有点麻烦,那就一个个看吧。

首先从最后一个比较开始,因为这里决定成功与否。

看到这句时,直觉告诉我是将字符串交换顺序Array.Reverse(chArray2),于是我把"==wYjsCZlN2TtBVasVWP0hUa181YIFTYxETRudUR"换了下顺序,得到

RUduRTExYTFIY181aUh0PWVsaVBtT2NlZCsjYw==,继续入往上看,发现是base64加密的,于是拿到百度上去解密,得到EGnE11a1Hc_5iHt=eliPmOced+#c,

继续往上看,又是Array.Reverse(array),交换顺序吧(交换顺序很麻烦,特别是数字0和字母O,小写l和数字1,一不小心就。。)

再往 上看,replace,说明是换字母,换好后如下:c#+dEcOmPilE=tHi5_cH1a113nG3,输入进去OK了,呵呵。

 

第三个题:

这是一个服务器验证类型的题目,刚拿到时感觉无从下手,因为程序运行后就是一句话:server start!由于很久没写过通信的程序,觉得要写个客户端和它通信简直太麻烦了,

后来上厕所时突然想到DOS下的telnet命令,于是回来就把它拿下了。嘿嘿。。

首先,用recv   API函数下断,然后往后跟,来到这里

代码:

00401030  /> \55            PUSH EBP

00401031  |.  8BEC          MOV EBP,ESP

00401033  |.  83EC 50       SUB ESP,50

00401036  |.  53            PUSH EBX

00401037  |.  56            PUSH ESI

00401038  |.  57            PUSH EDI

00401039  |.  8D7D B0       LEA EDI,DWORD PTR SS:[EBP-50]

0040103C  |.  B9 14000000   MOV ECX,14

00401041  |.  B8 CCCCCCCC   MOV EAX,CCCCCCCC

00401046  |.  F3:AB         REP STOS DWORD PTR ES:[EDI]

00401048  |.  8B45 08       MOV EAX,DWORD PTR SS:[EBP+8]

0040104B  |.  50            PUSH EAX

0040104C  |.  E8 EF090000   CALL Server.00401A40                     ;  计算name长度

00401051  |.  83C4 04       ADD ESP,4

00401054  |.  8945 F8       MOV DWORD PTR SS:[EBP-8],EAX

00401057  |.  8B4D 0C       MOV ECX,DWORD PTR SS:[EBP+C]

0040105A  |.  51            PUSH ECX

0040105B  |.  E8 E0090000   CALL Server.00401A40                     ;  计算serial的长度

00401060  |.  83C4 04       ADD ESP,4

00401063  |.  8B55 F8       MOV EDX,DWORD PTR SS:[EBP-8]

00401066  |.  D1E2          SHL EDX,1                                ;  name长度乘以2

00401068  |.  3BC2          CMP EAX,EDX                              ;  如果serial长度不等于name长度乘以2,则直接退出

0040106A  |.  73 04         JNB SHORT Server.00401070

0040106C  |.  32C0          XOR AL,AL

0040106E  |.  EB 6D         JMP SHORT Server.004010DD

00401070  |>  C745 FC 00000>MOV DWORD PTR SS:[EBP-4],0

00401077  |.  EB 09         JMP SHORT Server.00401082

00401079  |>  8B45 FC       /MOV EAX,DWORD PTR SS:[EBP-4]

0040107C  |.  83C0 01       |ADD EAX,1

0040107F  |.  8945 FC       |MOV DWORD PTR SS:[EBP-4],EAX

00401082  |>  8B4D FC        MOV ECX,DWORD PTR SS:[EBP-4]

00401085  |.  3B4D F8       |CMP ECX,DWORD PTR SS:[EBP-8]            ;  判断是否已经算完

00401088  |.  73 51         |JNB SHORT Server.004010DB

0040108A  |.  8B55 08       |MOV EDX,DWORD PTR SS:[EBP+8]

0040108D  |.  0355 FC       |ADD EDX,DWORD PTR SS:[EBP-4]

00401090  |.  0FBE02        |MOVSX EAX,BYTE PTR DS:[EDX]             ;  取name的第一个字符

00401093  |.  35 FF000000   |XOR EAX,0FF                             ;  将第一个字符取反

00401098  |.  2B45 FC       |SUB EAX,DWORD PTR SS:[EBP-4]

0040109B  |.  8945 F4       |MOV DWORD PTR SS:[EBP-C],EAX            ;  将eax中的值转换为小写字符

0040109E  |.  6A 10         |PUSH 10                                 ; /Arg3 = 00000010

004010A0  |.  8D4D F0       |LEA ECX,DWORD PTR SS:[EBP-10]           ; |

004010A3  |.  51            |PUSH ECX                                ; |Arg2

004010A4  |.  8B55 F4       |MOV EDX,DWORD PTR SS:[EBP-C]            ; |

004010A7  |.  52            |PUSH EDX                                ; |Arg1

004010A8  |.  E8 D3830000   |CALL Server.00409480                    ; \Server.00409480

004010AD  |.  83C4 0C       |ADD ESP,0C

004010B0  |.  0FBE45 F0     |MOVSX EAX,BYTE PTR SS:[EBP-10]          ;  取得到的第一个字符

004010B4  |.  8B4D FC       |MOV ECX,DWORD PTR SS:[EBP-4]

004010B7  |.  8B55 0C       |MOV EDX,DWORD PTR SS:[EBP+C]

004010BA  |.  0FBE0C4A      |MOVSX ECX,BYTE PTR DS:[EDX+ECX*2]       ;  取输入的第一个字符

004010BE  |.  3BC1          |CMP EAX,ECX                             ;  对比

004010C0  |.  75 13         |JNZ SHORT Server.004010D5

004010C2  |.  0FBE55 F1     |MOVSX EDX,BYTE PTR SS:[EBP-F]           ;  取上面计算 得到的第二个字符

004010C6  |.  8B45 FC       |MOV EAX,DWORD PTR SS:[EBP-4]

004010C9  |.  8B4D 0C       |MOV ECX,DWORD PTR SS:[EBP+C]

004010CC  |.  0FBE4441 01   |MOVSX EAX,BYTE PTR DS:[ECX+EAX*2+1]     ;  取输入的第二个字符

004010D1  |.  3BD0          |CMP EDX,EAX                             ;  对比

004010D3  |.  74 04         |JE SHORT Server.004010D9

004010D5  |>  32C0          |XOR AL,AL

004010D7  |.  EB 04         |JMP SHORT Server.004010DD

004010D9  |>^ EB 9E         \JMP SHORT Server.00401079

004010DB  |>  B0 01         MOV AL,1

004010DD  |>  5F            POP EDI

004010DE  |.  5E            POP ESI

004010DF  |.  5B            POP EBX

004010E0  |.  83C4 50       ADD ESP,50

004010E3  |.  3BEC          CMP EBP,ESP

004010E5  |.  E8 D6090000   CALL Server.00401AC0

004010EA  |.  8BE5          MOV ESP,EBP

004010EC  |.  5D            POP EBP

004010ED  \.  C3            RETN

这个算法不难,这里给出我写的一个注册机,供初学者参考。

代码:

#include<stdio.h>

#include<string.h>

#include<windows.h>

main()

{

  char name[100];

  printf("Please input a name :\t");

  scanf("%s",name);

  int key[100];

  int i;

  for(i=0;name[i];i++)

  {

    key[i]=name[i]^0x0FF;

    key[i]=key[i]-i;

  }

  key[i]=0;

  for(i=0;key[i];i++)

    printf("%x",key[i]);

  getchar();

  getchar();

}

最后效果如图:

西南某比赛的三个简单逆向题分析

 

这三个题算比较简单的,供初学者学习吧,后面有时间我再把稍难的题目整理下,写个文档出来,当为这次比赛留个纪念。 

附件: http://up.2cto.com/2012/1119/20121119113130873.rar