吾爱破解安全大赛cm1分析
本帖最后由 L4Nce 于 2016-3-29 10:33 编辑按钮事件被乱序加花,使用windbg的pykd脚本进行乱序修复,
花指令去除结果并整理后如下代码:
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: fs:0053:00000000=0018fa94
00435ce3 50 push eax
00439393 81ec64010000 sub esp,164h
00439eb7 26a118904100 mov eax,dword ptr es: es:002b:00419018=d76cde0e
0043ccc6 33c4 xor eax,esp
00443c02 8984245c010000mov dword ptr ,eax ss:002b:0018f9dc=00100486
00443efd f3a118904100 rep mov eax,dword ptr ds:002b:00419018=d76cde0e;无效前缀
0043a888 33c4 xor eax,esp
0043d133 50 push eax
00440f88 8d842470010000lea eax,
0043eaf5 64a300000000 mov dword ptr fs:,eax fs:0053:00000000=0018fa94
00435173 8bf9 mov edi,ecx
0043cdc0 6a32 push 32h
00443008 8d442435 lea eax,
00438cee c644243400 mov byte ptr ,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 ds:002b:00412124={USER32!GetDlgItemTextA (74ae6bfe)}
0043847f 8d44243c lea eax,
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,
00436bc3 c644246800 mov byte ptr ,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,
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,
00437580 e87baafcff call 52challenge+0x2000 (00402000)
004417de 83ec08 sub esp,8
0043c4f2 c784248001000000000000 mov dword ptr ,0 ss:002b:0018f9ec=ffffffff
00438ff7 8d4c2418 lea ecx,
00440882 e8d909fcff call 52challenge+0x1260 (00401260)
00438f57 8bf0 mov esi,eax
00437b53 85f6 test esi,esi
0043ac5d 0f8b180f0000 jnp 52challenge+0x3bb7b (0043bb7b)
00435830 51 push ecx
004410b3 56 push esi
0043ed44 8d4c2418 lea ecx,
004390bf e87c84fcff call 52challenge+0x1540 (00401540)
0043b016 85c0 test eax,eax
0043a27d 8d4c2464 lea ecx,
0043f380 51 push ecx ;参数
0043d375 8d4c2434 lea ecx,
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 ds:002b:0041212c={USER32!MessageBoxA (74adfde6)};msg
00437e02 56 push esi
pykd去花还原脚本
# -*- coding: utf-8 -*-
#.load pykd.pyd
#!py d:\fuckcm.py
import pykd
def getmemonic(op):
returnop.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):
returnop.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(" ") == "6" and op.split(" ") == "8":
return True
else:
return False
def isret(op):
if op.split(" ") == "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(" ") == "e" and op.split(" ") == "9":
return True
else:
return False
def isjunkjcc(op):
op_n = getnextop()
if 'j' in op and 'j' in op_n:
addr1 =op.split("(").split(")")
addr2 =op.split("(").split(")")
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("(").split(")")
op = stepover()
#print addr
#print op.split(" ")
if op.split(" ") != 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数码步骤做了限制
if ( v2 != name + 1 && pass )
{
pass_len = strlen(pass);
if ( pass_len - 19 <= 0x31 )
{
在矩阵部分首先根据用户名累加算出hash,并根据最后一位作为flag:
name_hash = get_hash((int)name) & 1;
第一部分密码的奇数位作为标记,用来限定偶数位的正负,限定的方案用之前的flag决定
while ( 1 )
{
v8 = v6;
pass_p_0 = -1;
v10 = v6 + v7;
v11 = pass_18;
if ( (unsigned __int8)(pass_18 - '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;
pass_p_1 = -1;
if ( (unsigned __int8)(pass_18 - '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 - v15.m128_f32;
v15 = v16;
}
v17 = i;
if ( i >= const_3 )
break;
if ( v6 >= const_3_ )
break;
++v6;
之后进行矩阵运算,矩阵的相乘和相加。由于给出了key,只要讲前面的矩阵固定即可。
第二部分的逻辑,首先用用户名初始化8数码。
v34 = &pass_left;
do
v35 = *v34++;
while ( v35 );
v36 = v34 - &v70;
egiht_mov(name_);
LOBYTE(v25) = 0;
然后用剩余的密码去恢复到123456780的形式
密码的转换方式做了一个变形
首先有四个基址分别代表上下左右
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) )
然后输入偏移,若基址+偏移的位置为空闲位置,则根据基址代表的意义进行移位
最后返回校验结果
do
{
if ( byte_10013F10[(unsigned __int8)v39] < byte_10013F0F[(unsigned __int8)v39] )
break;
++v39;
}
while ( (unsigned __int8)v39 < 8u );
return v39 == 8;
最后的keygen如下
#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;//标记
string path;//记录路径
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<s)num++;
sum+=(num*fac);
}
return sum+1;
}
struct Node
{
int s;
int loc;//“0”的位置
int status;//康拖展开的hash值
string path;//路径
};
int move={{-1,0},{1,0},{0,-1},{0,1}};//u,d,l,r
char indexs="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+1;
cur.s=0;
cur.loc=8;
cur.status=aim;
cur.path="";
queue<Node>q;
q.push(cur);
path="";
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;
int ty=y+move;
if(tx<0||tx>2||ty<0||ty>2)continue;
next=cur;
next.loc=tx*3+ty;
next.s=next.s;
next.s=0;
next.status=cantor(next.s);
if(!vis)
{
vis=true;
next.path=indexs+next.path;
q.push(next);
path=next.path;
}
}
}
}
string game(char* table)
{
char ch;
Node cur;
bfs();
for(int i =0;i<9;i++)
{
cur.s = table;
}
cur.status=cantor(cur.s);
if(vis)
{
return path;
}
else cout<<"unsolvable"<<endl;
return 0;
}
int main()
{
char username;
int namelen;
int hash;
int flag;
int step;
int black=3;
char table={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;
flag = hash & 1;
for(int i=0;i<namelen;i++)
{
step = (int)username;
step = step%4;
switch(step)
{
case 0:
if(black > 2)//up
{
temp = table;
table = table;
table = temp;
black -=3;
}
break;
case 1:
if(black < 8)//right
{
temp = table;
table = table;
table = temp;
black +=1;
}
break;
case 2: //down
if(black < 6)
{
temp = table;
table = table;
table = temp;
black +=3;
}
break;
case 3: //left
if(black>0)
{
temp = table;
table = table;
table = 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)
{
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;
}
你真不是人,用windbg写还原脚本。。。 加油楼主! 先留名,再看{:301_1009:} 前排支持懒死老师 {:301_972:} 才看到.. 有好多疑问的.. 原来是加花了.. 我说呢.. 到一定的地方代码就变了{:301_971:} 谢谢,学习中……先加分再说! 谢谢楼主分享经验 哇噻,答案出来了,赶紧围观学习,不过好像看不太懂{:1_890:} 原来是加花了.. 我说呢{:1_937:}