首页
社区
课程
招聘
[旧帖] [原创]从零开始学《加密与解密》第三版 之2.1.4 ASCII TraceMe 0.00雪花
发表于: 2015-9-8 23:04 2151

[旧帖] [原创]从零开始学《加密与解密》第三版 之2.1.4 ASCII TraceMe 0.00雪花

2015-9-8 23:04
2151
先载入TraceMe.exe,Ctrl+G,输入GetDlgItemTextA,确定来到这边:
76086900 > 8BFF             MOV EDI,EDI

F2,下断点,运行程序输入用户名 pediy,密码123456,确定,程序被断下:
76086900 > 8BFF             MOV EDI,EDI                              ; USER32.GetDlgItemTextA

这里断在了系统领空,我们先取消断点,然后按ALT+F9返回程序领空(程序停在004011B6):
004011AE   . 6A 51          PUSH 51                                  ; /Count = 51 (81.)
004011B0   . 50             PUSH EAX                                 ; |Buffer
004011B1   . 6A 6E          PUSH 6E                                  ; |ControlID = 6E (110.)
004011B3   . 56             PUSH ESI                                 ; |hWnd
004011B4   . FFD7           CALL EDI                                 ; \GetDlgItemTextA
004011B6   . 8D8C24 9C00000>LEA ECX,DWORD PTR SS:[ESP+9C]
004011BD   . 6A 65          PUSH 65                                  ; /Count = 65 (101.)
004011BF   . 51             PUSH ECX                                 ; |Buffer
004011C0   . 68 E8030000    PUSH 3E8                                 ; |ControlID = 3E8 (1000.)
004011C5   . 56             PUSH ESI                                 ; |hWnd
004011C6   . 8BD8           MOV EBX,EAX                              ; |
004011C8   . FFD7           CALL EDI                                 ; \GetDlgItemTextA

我们发现,正好上下一共调用了两次GetDlgItemTextA函数,我们猜测这里是读取文本框的用户名和密码。

在第一次调用GetDlgItemTextA的时候下一个断点,这里是004011AE处,Ctrl+F2重新载入程序,运行,输入用户名(pediy)和密码(123456),程序被断下:
004011AE   . 6A 51          PUSH 51                                  ; /Count = 51 (81.)
004011B0   . 50             PUSH EAX                                 ; |Buffer EAX=0x0019F758 是Buffer的地址
004011B1   . 6A 6E          PUSH 6E                                  ; |ControlID = 6E (110.)
004011B3   . 56             PUSH ESI                                 ; |hWnd
004011B4   . FFD7           CALL EDI                                 ; \GetDlgItemTextA

此时我们在内存窗口Ctrl+G,输入0x0019F758,可以看到内容是我们的用户名

004011B6   . 8D8C24 9C00000>LEA ECX,DWORD PTR SS:[ESP+9C]
004011BD   . 6A 65          PUSH 65                                  ; /Count = 65 (101.)

004011BF   . 51             PUSH ECX                                 ; |Buffer ECX=0x0019F7A8 是Buffer的地址
004011C0   . 68 E8030000    PUSH 3E8                                 ; |ControlID = 3E8 (1000.)
004011C5   . 56             PUSH ESI                                 ; |hWnd
004011C6   . 8BD8           MOV EBX,EAX                              ; |EAX是上一个函数的返回值,即用户名长度
004011C8   . FFD7           CALL EDI                                 ; \GetDlgItemTextA

这里,我们查看0x0019F7A8地址,内容正好是我们的密码,这里也就验证了我们的猜想,两个GetDlgItemTextA函数分别获取了用户名和密码

继续F8单步往下走
004011CA   . 8A4424 4C      MOV AL,BYTE PTR SS:[ESP+4C];获取用户名的第一个字节
004011CE   . 84C0           TEST AL,AL
004011D0   . 74 76          JE SHORT TraceMe.00401248 ;通过TEST命令,如果第一个字节是0,就跳转,即失败
004011D2   . 83FB 05        CMP EBX,5 ;在004011C6   处,EBX存放了用户名长度
004011D5   . 7C 71          JL SHORT TraceMe.00401248;用户名长度小于5则失败
004011D7   . 8D5424 4C      LEA EDX,DWORD PTR SS:[ESP+4C];EDX是用户名字符串的指针
004011DB   . 53             PUSH EBX
004011DC   . 8D8424 A000000>LEA EAX,DWORD PTR SS:[ESP+A0];EAX是密码字符串的指针
004011E3   . 52             PUSH EDX
004011E4   . 50             PUSH EAX
004011E5   . E8 56010000    CALL TraceMe.00401340;
004011EA   . 8B3D BC404000  MOV EDI,DWORD PTR DS:[<&USER32.GetDlgIte>;  USER32.GetDlgItem
004011F0   . 83C4 0C        ADD ESP,0C
004011F3   . 85C0           TEST EAX,EAX
004011F5   . 74 37          JE SHORT TraceMe.0040122E

004011E5 的CALL很重要,因为上面将用户名和密码分别入栈,然后调用这个CALL,所以很可能是判断密码是否正确的,我们先看最后一行(004011F5),这里对EAX进行判断,走下去发现,如果EAX=0,就跳转到失败的地方,EAX正好是这个CALL的返回值,所以这里基本可以判断,这个CALL返回0则密码错误,非0则密码正确,那么我们把004011F5这一行NOP就可以实现简单的爆破了。
现在我们在这个CALL F2下断点,Ctrl+F2重新载入,输入用户名(pediy)和密码(123456)确定,F7 进入这个CALL
00401340  /$ 55             PUSH EBP
00401341  |. 8B6C24 0C      MOV EBP,DWORD PTR SS:[ESP+C];EBP是用户名字符串指针
00401345  |. 56             PUSH ESI
00401346  |. 57             PUSH EDI
00401347  |. 8B7C24 18      MOV EDI,DWORD PTR SS:[ESP+18];EDI是用户名长度
0040134B  |. B9 03000000    MOV ECX,3;ECX=3,是个常量
00401350  |. 33F6           XOR ESI,ESI
00401352  |. 33C0           XOR EAX,EAX
00401354  |. 3BF9           CMP EDI,ECX;将用户名长度(EDI)和3比较
00401356  |. 7E 21          JLE SHORT TraceMe.00401379;用户名长度小于等于3则跳转,即失败
00401358  |. 53             PUSH EBX
00401359  |> 83F8 07        /CMP EAX,7
0040135C  |. 7E 02          |JLE SHORT TraceMe.00401360;这边把EAX和7对比,小于等于7就跳到下一行,否则用XOR EAX,EAX将EAX置0
0040135E  |. 33C0           |XOR EAX,EAX
00401360  |> 33D2           |XOR EDX,EDX
00401362  |. 33DB           |XOR EBX,EBX
00401364  |. 8A1429         |MOV DL,BYTE PTR DS:[ECX+EBP];DL=用户名[ECX],即取用户名的第ECX+1个字符
00401367  |. 8A98 30504000  |MOV BL,BYTE PTR DS:[EAX+405030];这里0x405030应该是个数组指针,根据上面EAX不能大于7,所以推测,数组长度为8,我们在内存窗口,Ctrl+G,输入0x405030,果然是一个数组,前八个字节分别为0x0C, 0x0A, 0x13, 0x09, 0x0C, 0x0B, 0x0A, 0x08
0040136D  |. 0FAFD3         |IMUL EDX,EBX
00401370  |. 03F2           |ADD ESI,EDX ;将上面的两个值相乘,结果放入ESI
00401372  |. 41             |INC ECX ;ECX递增
00401373  |. 40             |INC EAX;EAX递增
00401374  |. 3BCF           |CMP ECX,EDI;00401354 处在EDI存放了用户名长度,所以这边是将ECX和用户名长度相比
00401376  |.^7C E1          \JL SHORT TraceMe.00401359;ECX小于用户名长度则跳转,也就是重复上述步骤(do...while循环),这里我们发现,算法是从用户名的第4个字节开始,数组从第一个字节开始,两个字节相乘,结果加到ESI,如果数组超过第8个字节,则再从从第一个字节开始
00401378  |. 5B             POP EBX
00401379  |> 56             PUSH ESI                                 ; /<%ld>
0040137A  |. 68 78504000    PUSH TraceMe.00405078                    ; |Format = "%ld"
0040137F  |. 55             PUSH EBP                                 ; |s
00401380  |. FF15 9C404000  CALL DWORD PTR DS:[<&USER32.wsprintfA>]  ; \wsprintfA 调用wsprintf函数,将ESI从整型转换成字符串,*****这是关键,这个字符串就是密码*****
00401386  |. 8B4424 1C      MOV EAX,DWORD PTR SS:[ESP+1C]
0040138A  |. 83C4 0C        ADD ESP,0C
0040138D  |. 55             PUSH EBP                                 ; /String2
0040138E  |. 50             PUSH EAX                                 ; |String1
0040138F  |. FF15 04404000  CALL DWORD PTR DS:[<&KERNEL32.lstrcmpA>] ; \lstrcmpA 通过lstrcmpA将算出来的密码和用户输入的进行对比,判断密码是否正确

算法分析到此结束,下面给出C语言的注册机算法:
#include <stdio.h>
#include <stdlib.h>

int CalcKey(const char *name,char *key);

int main()
{
    char name[100];
    char key[100];
    while (1)
    {
        printf("Input the name:");
        scanf("%s", name);
        if (0==strcmp(name,"exit"))
        {
            break;
        }
        CalcKey(name, key);
        printf("The key is:%s\n", key);
    }
   
   
    return 0;
}

int CalcKey(const char *name,char *key)
{
    unsigned char keyArr[8] = { 0x0C, 0x0A, 0x13, 0x09, 0x0C, 0x0B, 0x0A, 0x08 };
    int nameLen = strlen(name);
   
    long sum = 0;
    int i = 0, j = 3;   
    if (nameLen <= 3)
    {
        return -1;
    }
    do
    {
        if (i>7)
        {
            i = 0;
        }
        sum += name[j] * keyArr[i];
        ++i;
        ++j;
    } while (j < nameLen);
    sprintf(key, "%ld", sum);
    return 0;
}


[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

上传的附件:
收藏
免费 0
支持
分享
最新回复 (7)
雪    币: 43
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
顶,因为我也不懂。。。
2015-9-8 23:17
0
雪    币: 35
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
顶,因为我也不懂。。。
2015-9-11 22:45
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
支持下  谢谢分享
2015-9-12 08:33
0
雪    币: 10
活跃值: (28)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
加油!为梦想加油!
2015-9-12 16:24
0
雪    币: 1
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
果然高深!!!
2015-9-15 17:42
0
雪    币: 933
活跃值: (12)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
不错书 学下下
2015-9-18 10:35
0
雪    币: 46
活跃值: (31)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
8
好书,有空要看看!
2015-9-19 08:29
0
游客
登录 | 注册 方可回帖
返回
//