吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 23395|回复: 103
收起左侧

[原创] 吾爱破解安全大赛cm1分析

  [复制链接]
L4Nce 发表于 2016-3-28 23:29
本帖最后由 L4Nce 于 2016-3-29 10:33 编辑

按钮事件被乱序加花,使用windbg的pykd脚本进行乱序修复,

winbg。png.png

花指令去除结果并整理后如下代码:
[Asm] 纯文本查看 复制代码
004406c0 55              push    ebp
0044217a 8bec            mov     ebp,esp
00440b83 83e4f8          and     esp,0FFFFFFF8h
004398ff 6aff            push    0FFFFFFFFh
0043b4b7 683b0f4100      push    offset 52challenge+0x10f3b (00410f3b)
00437a45 64a100000000    mov     eax,dword ptr fs:[00000000h] fs:0053:00000000=0018fa94
00435ce3 50              push    eax
00439393 81ec64010000    sub     esp,164h
00439eb7 26a118904100    mov     eax,dword ptr es:[52challenge+0x19018 (00419018)] es:002b:00419018=d76cde0e
0043ccc6 33c4            xor     eax,esp
00443c02 8984245c010000  mov     dword ptr [esp+15Ch],eax ss:002b:0018f9dc=00100486
00443efd f3a118904100    rep mov eax,dword ptr [52challenge+0x19018 (00419018)] ds:002b:00419018=d76cde0e;无效前缀
0043a888 33c4            xor     eax,esp
0043d133 50              push    eax
00440f88 8d842470010000  lea     eax,[esp+170h]
0043eaf5 64a300000000    mov     dword ptr fs:[00000000h],eax fs:0053:00000000=0018fa94
00435173 8bf9            mov     edi,ecx
0043cdc0 6a32            push    32h
00443008 8d442435        lea     eax,[esp+35h]
00438cee c644243400      mov     byte ptr [esp+34h],0       ss:002b:0018f8a4=e8
004388e5 6a00            push    0
00436409 50              push    eax
00438d69 e842fcfcff      call    52challenge+0x89b0 (004089b0)
00444e2c 8b3524214100    mov     esi,dword ptr [52challenge+0x12124 (00412124)] ds:002b:00412124={USER32!GetDlgItemTextA (74ae6bfe)}
0043847f 8d44243c        lea     eax,[esp+3Ch]
00439f24 83c40c          add     esp,0Ch
0043f78c 6a32            push    32h
0043b113 50              push    eax
0043f6c0 68ec030000      push    3ECh
0043a989 ffd6            call    esi {USER32!GetDlgItemTextA (74ae6bfe)}
00441e84 68ff000000      push    0FFh
0043bf6c 8d442469        lea     eax,[esp+69h]
00436bc3 c644246800      mov     byte ptr [esp+68h],0       ss:002b:0018f8d8=98
0043b35c 6a00            push    0
0043edb1 50              push    eax
00443fad e8fe49fcff      call    52challenge+0x89b0 (004089b0)
004400ad 83c40c          add     esp,0Ch
0043b98e 8d442464        lea     eax,[esp+64h]
004402c2 68ff000000      push    0FFh
00439a8b 50              push    eax
0043fedc 68ee030000      push    3EEh
004446da 57              push    edi
00441f3c ffd6            call    esi {USER32!GetDlgItemTextA (74ae6bfe)}
00441c02 8d4c2410        lea     ecx,[esp+10h]
00437580 e87baafcff      call    52challenge+0x2000 (00402000)
004417de 83ec08          sub     esp,8
0043c4f2 c784248001000000000000 mov dword ptr [esp+180h],0 ss:002b:0018f9ec=ffffffff
00438ff7 8d4c2418        lea     ecx,[esp+18h]
00440882 e8d909fcff      call    52challenge+0x1260 (00401260)
00438f57 8bf0            mov     esi,eax
00437b53 85f6            test    esi,esi
0043ac5d 0f8b180f0000    jnp     52challenge+0x3bb7b (0043bb7b)          [br=1]
00435830 51              push    ecx
004410b3 56              push    esi
0043ed44 8d4c2418        lea     ecx,[esp+18h]
004390bf e87c84fcff      call    52challenge+0x1540 (00401540)
0043b016 85c0            test    eax,eax
0043a27d 8d4c2464        lea     ecx,[esp+64h]
0043f380 51              push    ecx                        ;参数
0043d375 8d4c2434        lea     ecx,[esp+34h]
0043b61c 51              push    ecx                        ;参数
0043d613 ffd0            call    eax {10001450}        ;调用检测算法
0044058c 6a40            push    40h
004387d8 68b8744100      push    offset 52challenge+0x174b8 (004174b8)
0043f876 3c01            cmp     al,1
00443349 0F85 7C090000   jnz     CrackMe.00443CCB                       ;此处可以爆破,代码修正
00443ccb 68e8744100      push    offset 52challenge+0x174e8 (004174e8)
004395ef 57              push    edi
0043900c ff152c214100    call    dword ptr [52challenge+0x1212c (0041212c)] ds:002b:0041212c={USER32!MessageBoxA (74adfde6)};msg
00437e02 56              push    esi



pykd去花还原脚本
[Python] 纯文本查看 复制代码
 
# -*- coding: utf-8 -*-
#.load pykd.pyd
 #!py d:\fuckcm.py
import pykd
def getmemonic(op):
        return  op.split(" ")[-1]
        
def ispushreg(op):
        if 'push' in op:
                if 'eax' in op:
                        return True
                elif 'ebx' in op:
                        return True
                elif 'ecx' in op:
                        return True
                elif 'edx' in op:
                        return True
                elif 'esi' in op:
                        return True
                elif 'edi' in op:
                        return True
                elif 'ebp' in op:
                        return True
                elif 'esp' in op:
                        return True
                else:
                        return False
        else:
                return False
def ispopreg(op):
        if 'pop' in op:
                if 'eax' in op:
                        return True
                elif 'ebx' in op:
                        return True
                elif 'ecx' in op:
                        return True
                elif 'edx' in op:
                        return True
                elif 'esi' in op:
                        return True
                elif 'edi' in op:
                        return True
                elif 'ebp' in op:
                        return True
                elif 'esp' in op:
                        return True
                else:
                        return False
        else:
                return False
        return False        
def getppreg(op):
        return  op.split(" ")[-1]
def stepin():
        pykd.trace()        #单步
        dis = pykd.disasm(pykd.reg("eip"))
        op = dis.instruction()
        return op
        
def stepover():
        pykd.step()        #单步
        dis = pykd.disasm(pykd.reg("eip"))
        op = dis.instruction()
        return op
def ispushimm(op):
        if op.split(" ")[1][0] == "6" and op.split(" ")[1][1] == "8":
                return True
        else:
                return False
def isret(op):
        if op.split(" ")[1] == "c3":
                return True
        else:
                return False
def getnextop():
        dis = pykd.disasm(pykd.reg("eip"))
        #print pykd.reg("eip")+dis.length()
        dis_1 = pykd.disasm(pykd.reg("eip")+dis.length())
        op = dis_1.instruction()
        #print op
        return op
def islongjmp(op):
        if op.split(" ")[1][0] == "e" and op.split(" ")[1][1] == "9":
                return True
        else:
                return False
def isjunkjcc(op):
        op_n = getnextop()
        if 'j' in op and 'j' in op_n:
                addr1 =  op.split("(")[1].split(")")[0]
                addr2 =  op.split("(")[1].split(")")[0]
                if addr1 == addr2:
                        return True
                else:
                        return False
        else:
                return False                
def dealjunk():
        x=0;
        #------------------------------------------------
        #花指令有三种
        #pusad 修改寄存器 popad
        #push reg 修改reg pop reload
        #修改reg 真实指令处理reg
        #乱序连接 分解为jcc 和 push imm ret 和 jmp
        #------------------------------------------------
        dis = pykd.disasm(pykd.reg("eip"))
        op = dis.instruction()
        
        
        if getmemonic(op) == "pushad":        #花指令pushad类
                while True:
                        op = stepover()
                        if getmemonic(op) == "popad":
                                op = stepover()
                                break
                return op                
        elif ispushreg(op):
                reglist = []
                while ispushreg(op):                #找到所有push并把reg加入list
                        reglist.insert(0,getppreg(op))
                        op = stepover()
                while True:
                        flag = False
                        size =len(reglist)
                        if ispopreg(op):
                                for i in reglist:
                                        if i == getppreg(op):
                                                x=x+1;
                                        else:
                                                break
                                        op = stepover()
                                if        x==size or x==size-1:
                                        break
                        op = stepover()
                        x=0
        return op
def main():
        step = 0
        pykd.setBp(0x004011c5)#按钮事件
        pykd.go()
        pykd.trace()
        pykd.trace()        #代码开始
        opcode = []
#--------------------------------------------------
        while True:                #一个指令块
                opcode.append("")
                junkflag = False
                step = step+1
                dis = pykd.disasm(pykd.reg("eip"))
                op = dis.instruction()
                #print"-------------------------"
                #print op 
                if step > 500:
                        break
                while True:        #指令解析
                        #print "code"
                        dis = pykd.disasm(pykd.reg("eip"))
                        op = dis.instruction()
                        if (ispushreg(op) and getmemonic(getnextop())!="pushad") or getmemonic(op) == "pushad":
                                if junkflag == False:
                                        #print "junk"
                                        op = dealjunk()
                                        junkflag = True

                                #dis = pykd.disasm(pykd.reg("eip"))
                                #print dis.instruction()
                        if ispushimm(op)  and isret(getnextop()):                                
                                stepover()
                                stepover()

                                break
                        if islongjmp(op):
                                #print "jmp"
                                stepover()
                                break
                        if isjunkjcc(op):
                                #print "jcc"
                                addr = op.split("(")[1].split(")")[0]
                                op = stepover()
                                #print addr
                                #print op.split(" ")[0] 
                                if op.split(" ")[0] != addr:
                                        stepover()
                                        break
                                break
                        opcode[-1] = op
                        stepover()
                if opcode[-1]!="":
                        print opcode[-1]
if __name__ == '__main__':
        main()


首先算法分成两部分:
1.一部分为3*3矩阵运算
2.8数码问题

所以key也分成两部,并对8数码步骤做了限制
[C] 纯文本查看 复制代码
if ( v2 != name + 1 && pass )
{
  pass_len = strlen(pass);
  if ( pass_len - 19 <= 0x31 )
  {


在矩阵部分首先根据用户名累加算出hash,并根据最后一位作为flag:
[ColdFusion] 纯文本查看 复制代码
name_hash = get_hash((int)name) & 1;

第一部分密码的奇数位作为标记,用来限定偶数位的正负,限定的方案用之前的flag决定
[C] 纯文本查看 复制代码
while ( 1 )
 {
   v8 = v6;
   pass_p_0 = -1;
   v10 = v6 + v7;
   v11 = pass_18[2 * v10];
   if ( (unsigned __int8)(pass_18[2 * v10] - '0') > 9u )// 是不是数字
   {
     if ( (unsigned __int8)(v11 - 'A') > 5u )
     {
       if ( (unsigned __int8)(v11 - 'a') <= 5u )
         pass_p_0 = v11 - 'W';
     }
     else
     {
       pass_p_0 = v11 - '7';
     }
   }
   else
   {
     pass_p_0 = v11 - '0';
   }
   v12 = pass_p_0 % 2;
   v13 = pass_18[2 * v10 + 1];
   pass_p_1 = -1;
   if ( (unsigned __int8)(pass_18[2 * v10 + 1] - '0') > 9u )
   {
     if ( (unsigned __int8)(v13 - 'A') > 5u )
     {
       if ( (unsigned __int8)(v13 - 'a') <= 5u )
         pass_p_1 = v13 - 'W';
     }
     else
     {
       pass_p_1 = v13 - '7';
     }
   }
   else
   {
     pass_p_1 = v13 - '0';
   }
   v15 = _mm_cvtepi32_ps(_mm_cvtsi32_si128(pass_p_1));
   if ( v12 != name_hash )
   {
     v16 = 0;
     v16.m128_f32[0] = 0.0 - v15.m128_f32[0];
     v15 = v16;
   }
   v17 = i;
   if ( i >= const_3 )
     break;
   if ( v6 >= const_3_ )
     break;
   ++v6;



之后进行矩阵运算,矩阵的相乘和相加。由于给出了key,只要讲前面的矩阵固定即可。


第二部分的逻辑,首先用用户名初始化8数码。


[C] 纯文本查看 复制代码
v34 = &pass_left;
     do
       v35 = *v34++;
     while ( v35 );
     v36 = v34 - &v70;
     egiht_mov(name_);
     LOBYTE(v25) = 0;

然后用剩余的密码去恢复到123456780的形式
密码的转换方式做了一个变形
首先有四个基址分别代表上下左右


[C] 纯文本查看 复制代码
if ( *(_BYTE *)(v4 + 0x10013F10) )
{
  if ( v4 <= 2 || *(_BYTE *)(v4 + 0x10013F0D) )
  {
    if ( v5 >= 2 || *(_BYTE *)(v4 + 0x10013F11) )
    {
      if ( v3 >= 2 || *(_BYTE *)(v4 + 0x10013F13) )
      {
        if ( v5 > 0 && !*(_BYTE *)(v4 + 0x10013F0F) )


然后输入偏移,若基址+偏移的位置为空闲位置,则根据基址代表的意义进行移位

最后返回校验结果


[C] 纯文本查看 复制代码
do
{
  if ( byte_10013F10[(unsigned __int8)v39] < byte_10013F0F[(unsigned __int8)v39] )
    break;
  ++v39;
}
while ( (unsigned __int8)v39 < 8u );
return v39 == 8;





最后的keygen如下
[C] 纯文本查看 复制代码
#include<stdio.h>
#include<string.h>
#include<iostream>
#include<queue>
#include<string>
using namespace std;
const int MAXN=1000000;//最多是9!/2
int fac[]={1,1,2,6,24,120,720,5040,40320,362880};//康拖展开判重
//         0!1!2!3! 4! 5!  6!  7!   8!    9!
bool vis[MAXN];//标记
string path[MAXN];//记录路径
int cantor(int s[])//康拖展开求该序列的hash值
{
    int sum=0;
    for(int i=0;i<9;i++)
    {
        int num=0;
        for(int j=i+1;j<9;j++)
          if(s[j]<s[i])num++;
        sum+=(num*fac[9-i-1]);
    }
    return sum+1;
}
struct Node
{
    int s[9];
    int loc;//“0”的位置
    int status;//康拖展开的hash值
    string path;//路径
};
int move[4][2]={{-1,0},{1,0},{0,-1},{0,1}};//u,d,l,r
char indexs[5]="udlr";//和上面的要相反,因为是反向搜索
int aim=46234;//123456780对应的康拖展开的hash值
void bfs()
{
    memset(vis,false,sizeof(vis));
    Node cur,next;
    for(int i=0;i<8;i++)cur.s[i]=i+1;
    cur.s[8]=0;
    cur.loc=8;
    cur.status=aim;
    cur.path="";
    queue<Node>q;
    q.push(cur);
    path[aim]="";
    while(!q.empty())
    {
        cur=q.front();
        q.pop();
        int x=cur.loc/3;
        int y=cur.loc%3;
        for(int i=0;i<4;i++)
        {
            int tx=x+move[i][0];
            int ty=y+move[i][1];
            if(tx<0||tx>2||ty<0||ty>2)continue;
            next=cur;
            next.loc=tx*3+ty;
            next.s[cur.loc]=next.s[next.loc];
            next.s[next.loc]=0;
            next.status=cantor(next.s);
            if(!vis[next.status])
            {
                vis[next.status]=true;
                next.path=indexs[i]+next.path;
                q.push(next);
                path[next.status]=next.path;
            }
        }
    }
 
}
string game(char* table)
{
    char ch;
    Node cur;
    bfs();
    for(int i =0;i<9;i++)
    {
        cur.s[i] = table[i];
    }
        cur.status=cantor(cur.s);
        if(vis[cur.status])
        {
            return path[cur.status];
        }
        else cout<<"unsolvable"<<endl;
 
    return 0;
}
int main()
{
    char username[100];
    int namelen;
    int hash;
    int flag;
    int step;
    int black=3;
    char table[9]={7,1,4,0,3,8,2,5,6};
    char temp;
    string mo;
     
     
    scanf("%s",username);
    namelen = strlen(username);
    for(int i=0;i<namelen;i++)
        hash += (int)username[i];
    flag = hash & 1;
 
    for(int i=0;i<namelen;i++)
    {
        step = (int)username[i];
        step = step%4;
        switch(step)
        {
            case 0:
                if(black > 2)//up
                {
                    temp = table[black];
                    table[black] = table[black-3];
                    table[black-3] = temp;
                    black -=3;
                }
                break;
            case 1:
                if(black < 8)//right
                {
                    temp = table[black];
                    table[black] = table[black+1];
                    table[black+1] = temp;
                    black +=1;
                }
                break;
            case 2:     //down
                if(black < 6)
                {
                    temp = table[black];
                    table[black] = table[black+3];
                    table[black+3] = temp;
                    black +=3;
                }
                break;
            case 3:     //left
                if(black>0)
                {
                    temp = table[black];
                    table[black] = table[black-1];
                    table[black-1] = temp;
                    black -=1;  
                }
                break;
        }
    }
     mo = game(table);
    //mo ="ulldrrdluurddllurdrulul";
    //cout<<endl;
    if(flag)
        printf("323031303330213032");
    else
        printf("222021202320312022");
     
    for(int i=0;i<mo.length();i++)
        switch(mo[i])
        {
            case 'l':
                printf("%d",black+1);
                black = black+1;
                break;
            case 'r':
                printf("%d",black-1);
                black = black-1;
                break;
            case 'u':
                printf("%d",black+3);
                black = black+3;
                break;
            case 'd':
                printf("%d",black-3);
                black = black-3;
                break;
        }
    printf("\n");
    return 0;
}

keygen.zip (180.81 KB, 下载次数: 55)




点评

感谢老师 我也会去试一试的!  发表于 2016-3-28 23:49

免费评分

参与人数 43热心值 +43 收起 理由
GG0 + 1 我很赞同!
Mr.Mlwareson_V + 1 用心讨论,共获提升!
未知心 + 1 我很赞同!
白色巨猫 + 1 热心回复!
siuhoapdou + 1 用心讨论,共获提升!
说给自己听 + 1 用心讨论,共获提升!
Lnairan + 1 谢谢@Thanks!
jianboom7 + 1 感谢您的宝贵建议,我们会努力争取做得更好.
云顶天籁 + 1 谢谢@Thanks!
hack_koko + 1 无法抑制我的心情
st1717 + 1 用心讨论,共获提升!
yets11 + 1 我很赞同!
WildWolf + 1 6
蚯蚓翔龙 + 1 谢谢@Thanks!都看不懂
125733578 + 1 虽然看着费劲,但我能加分
currwin + 1 你真不是人,用windbg写还原脚本。。。
ripples + 1 谢谢@Thanks!
Lucas丶 + 1 谢谢@Thanks!
czr27 + 1 nb
myouter + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
codelive + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
在下谢谢你们 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
我是哥布林 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩.
Terrorblade + 1 我很赞同!
yypE + 1 禁止发布黑客相关内容!
yAYa + 1 学习,感谢师傅~
yeyulang + 1 用心讨论,共获提升!
965852235 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
yzh2004 + 1 用心讨论,共获提升!
286733081 + 1 围观大牛!
Nutural + 1 热心回复!
LOVE_TT + 1 能再详细点吗?
冰封的记忆~ + 1 用心讨论,共获提升!
renfeng + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
lingyulingyu + 1 谢谢@Thanks!
小范 + 1 厉害!!!!
Sound + 1 此为违规行为,请遵守论坛相关规则!
天蝎浪花 + 1 谢谢@膜拜大神!
lies2014 + 1 谢谢@Thanks!
wzxkk123 + 1 总算可以解开迷惑了0.0
Godfather.Cr + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩.
梦游枪手 + 1 用心讨论,共获提升!
丶懒喵喵 + 1 热心回复!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

Hmily 发表于 2016-3-29 10:07
你真不是人,用windbg写还原脚本。。。
丶懒喵喵 发表于 2016-3-28 23:35
梦游枪手 发表于 2016-3-28 23:35
知足zz 发表于 2016-3-28 23:39 来自手机
前排支持懒死老师
wzxkk123 发表于 2016-3-28 23:46
才看到.. 有好多疑问的..
wzxkk123 发表于 2016-3-28 23:48
原来是加花了.. 我说呢.. 到一定的地方代码就变了
lies2014 发表于 2016-3-28 23:58
谢谢,学习中……先加分再说!
丶小明 发表于 2016-3-29 00:07 来自手机
谢谢楼主分享经验
天蝎浪花 发表于 2016-3-29 00:10
哇噻,答案出来了,赶紧围观学习,不过好像看不太懂
chenjingyes 发表于 2016-3-29 00:24
原来是加花了.. 我说呢
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-17 11:03

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表