1 前期准备
本题为安卓逆向,需要反编译工具可以使用jeb这种集成环境,或dex2jar 和 jd-gui配合使用 。 这里使用dex2jar 和 jd-gui
下载文件,解压,将apk再次解压,得到如图所示的几个文件
随后将classes.dex文件拖入d2j-dex2jar.bat,多出来一个classes-dex2jar.jar文件,然后用jd-gui-1.4.0.jar打开
2 函数分析
crackme类的结构如下
流程还是很清楚的,直接从上往下走就行
2.1 初始部分
前面定义了a,b,c,d四个数组,参数如下
int[] a = { 16, 6, 7, 10, 9, 16, 10, 8, 8, 9, 6, 6 };
int[] b = { 5, 10, 8, 15, 16, 15, 8, 16, 8, 16, 9, 17, 8, 17, 10, 8, 9, 18, 5, 15, 10, 9, 8, 9, 15, 18, 7, 8, 16, 6 };
int[] c = { 6, 7, 18, 9, 5, 16, 9, 15, 18, 8, 9, 5 };
int[] d = { 7, 7, 9, 12, 8, 7, 13, 5, 14, 5, 9, 17, 5, 7, 12, 8, 8, 6, 19, 6, 8, 16, 10, 6, 12, 9, 6, 7, 12, 5, 9, 8, 7, 8, 15, 9, 16, 8, 8, 19, 12, 6, 8, 7, 5, 15, 6, 16, 15, 7, 9, 12, 10, 7, 15, 6, 5, 14, 14, 9 };
中间一堆没有赋值的变量+空数组l 有一个字符串数组
String[] m = { "23to01", "01to03", "03to05", "05to07", "07to09", "09to11", "11to13", "13to15", "15to17", "17to19", "19to21", "21to23" };
textView是用来显示字符串的组件,在手机上就是显示一块文本的区域。
2.2 a()
private void a()
{
try
{
c();
if ((this.j != 0) && (this.i != 0) && (this.h != 0))
{
d();
a(e() + f() + g() + h());
return;
}
this.n.setText(getString(2131427370));
return;
}
catch (Exception localException)
{
for (;;) {}
}
this.n.setText(getString(2131427370));
}
2.3 c()
具体代码解释参考注释
private void c()
{
try
{
str = ((EditText)findViewById(2131165227)).getText().toString();//获取输入
this.j = 0;
this.i = 0;
this.h = 0;
if (str.length() <= 4) { //字符串长度大于4,取出前四位
break label223;
}
localObject = str.substring(0, 4);
}
catch (Exception localException)
{
for (;;)
{
String str;
continue;
label223:
Object localObject = localException;
continue;
localObject = localException;
}
}
this.j = Integer.parseInt((String)localObject); //在这里对前四位的输入做了限制在1984-2006之间,像是生日,hhhh
if ((this.j > 0) && (this.j < 189)) {
this.j = 0;
}
if ((this.j <= 1983) || (this.j >= 2007)) {
this.j = 0;
}
if (str.length() > 6) //同样的输入长度大于6,取5,6位
{
localObject = str.substring(4, 6);
this.i = Integer.parseInt((String)localObject);
if ((this.i < 1) || (this.i > 12)) { //判断是不是在1-12之间,生日无疑
this.i = 0;
}
localObject = str;
if (str.length() > 8) { //输入长度大于8,取出7,8位
localObject = str.substring(6, 8);
}
this.h = Integer.parseInt((String)localObject);
if ((this.h < 1) || (this.h > 31)) //判断是不是在1-31之间
{
this.h = 0;
return;
this.n.setText(getString(2131427370));
}
return;
}
}
总体功能,检查输入的前8位(生日)是不是满足要求
2.4 d()
private void d()
{
try
{
if ((this.j == 1989) || (this.j == 2004)) { //1989.xx.31 or 2004.xx.31
this.h = 31;
}
if ((this.i == 1) || (this.i == 4) || (this.i == 5) || (this.i == 7) || (this.i == 10) || (this.i == 11) || (this.i == 12)) {
this.j = 1999;
} //1999.1.xx or 1999.4.xx or 1999.5.xx or 1999.7.xx or 1999.10.xx or 1999.11.xx or 1999.12.xx
if ((this.j <= 1994) && ((this.i == 2) || (this.i == 6) || (this.i == 8))) {
this.i = 3;
} // 1984-1994.2.3 or 1984-1994.6.3 or 1984-1994.8.3
if ((this.j >= 1996) && ((this.i == 2) || (this.i == 6) || (this.i == 8))) {
this.i = 9;
} //1996-2006.2.9 or 1996-2006.6.9 or 1996-2006.8.9
if ((this.j == 1995) && ((this.h > this.i + 2) || (this.i == this.h))) {
this.i = 6;
} //1995 年,日大于(月+2)或者日月相等,月份等于6
this.g = this.j;
this.f = this.i;
this.e = this.h;
return; //把jih,赋值给gfe
}
catch (Exception localException)
{
for (;;) {}
}
this.n.setText(getString(2131427370));
}
这个函数,对c中输入的年月日,做出了检查
2.5 e()
private int e()
{
try
{
int i1 = this.g;
i1 = this.d[((i1 - 1900) % 60)]; //年份-1900,对60取余
return i1;
}
catch (Exception localException)
{
for (;;) {}
}
this.n.setText(getString(2131427370));
return 0;
}
年份-1900,对60取余,返回余数
2.6 f()
private int f()
{
try
{
int i1 = this.f;
i1 = this.c[(i1 - 1)]; //月份-1,然后与c数组中的值对应
return i1;
}
catch (Exception localException)
{
for (;;) {}
}
this.n.setText(getString(2131427370));
return 0;
}
把月份当成了一个c数组的索引
2.7 g()
private int g()
{
try
{
int i1 = this.e;
i1 = this.b[(i1 - 1)]; //日期-1当作b数组的索引
return i1;
}
catch (Exception localException)
{
for (;;) {}
}
this.n.setText(getString(2131427370));
return 0;
}
日期-1当作b数组的索引
2.8 h()
private int h()
{
try
{
str = ((EditText)findViewById(2131165227)).getText().toString();
str = str.substring(8, str.length()); //第8位后面的
i2 = this.f; //月份
i1 = 0;
}
catch (Exception localException)
{
for (;;)
{
String str;
int i2;
continue;
i1 += 1;
continue;
int i1 = 0;
}
}
if (i1 < this.m.length) {
if (str.equals(this.m[i1])) //如果第八位后面的和m数组中的第i1位相等
{
if ((i2 == 2) && (str.equals(this.m[6]))) { //如果月份=2,后面的字符串="11to13"
return 63;
}
this.k = this.a[i1]; //k是a中第i1个元素
i1 = 1;
if (i1 == 0) {
this.n.setText(getString(2131427370));
}
i1 = this.k;
return i1;
this.n.setText(getString(2131427370));
return 0;
}
}
}
第一编是0,抛异常,i1++,遍历数组,寻找两字符串相等的索引值,然后在a中对应。
2.9 a(int)
回来看a(int)的原因
d();
a(e() + f() + g() + h()); //四个值相加
函数逻辑
private void a(int paramInt)
{
if ((paramInt <= 34) && (paramInt >= 34)) {} //传入的参数必须等于34,不然就要抛异常出来
try
{
this.n.setText(String.format("%s%s", new Object[] { getString(2131427369), this.l[paramInt] }));
((Button)findViewById(2131165273)).setEnabled(false);
return;
}
catch (Exception localException)
{
for (;;) {}
}
this.n.setText(getString(2131427370));
return;
this.n.setText(getString(2131427370));
}
函数逻辑分析完了,接下来直接解吧,穷举就可以,总的逻辑很清楚,输入,然后乱七八糟的变换,还好都不算复杂。
3.暴力破解
java代码如下(来自大佬)
public class CTF {
public static void main(String[] args) {
// TODO Auto-generated method stub
new CTF().run();
}
int[] a = { 16, 6, 7, 10, 9, 16, 10, 8, 8, 9, 6, 6 };
int[] b = { 5, 10, 8, 15, 16, 15, 8, 16, 8, 16, 9, 17, 8, 17, 10, 8, 9, 18, 5, 15, 10, 9, 8, 9, 15, 18, 7, 8, 16, 6 };
int[] c = { 6, 7, 18, 9, 5, 16, 9, 15, 18, 8, 9, 5 };
int[] d = { 7, 7, 9, 12, 8, 7, 13, 5, 14, 5, 9, 17, 5, 7, 12, 8, 8, 6, 19, 6, 8, 16, 10, 6, 12, 9, 6, 7, 12, 5, 9, 8, 7, 8, 15, 9, 16, 8, 8, 19, 12, 6, 8, 7, 5, 15, 6, 16, 15, 7, 9, 12, 10, 7, 15, 6, 5, 14, 14, 9 };
int e;
int f;
int g;
int h;
int i;
int j;
int k;
String[] l = new String[73];
String[] m = { "23to01", "01to03", "03to05", "05to07", "07to09", "09to11", "11to13", "13to15", "15to17", "17to19", "19to21", "21to23" };
private void run() {
int result_len = 0;
for(int a1 = 1983;a1<2007;a1+=1){
for(int a2 = 1;a2<=12;a2+=1)
{
for(int a3 = 1;a3<=31;a3+=1){
for(String item :m)
{
this.j = a1;
this.i = a2;
this.h = a3;
str = item;
d();
int re_e = e();
int re_f = f();
int re_g = g();
int re_h = h();
if(re_e+re_f+re_g+re_h==34)
{
if(re_g==0)
{
}
System.out.println(re_e+" "+" "+re_f+" "+re_g+" "+re_h);
System.out.println(a1+" "+" "+a2+" "+a3+" "+item);
result_len +=1;
}
}
}
}
}
System.out.println("数量:"+result_len);
}
private void d()
{
try
{
if ((this.j == 1989) || (this.j == 2004)) {
this.h = 31;
}
if ((this.i == 1) || (this.i == 4) || (this.i == 5) || (this.i == 7) || (this.i == 10) || (this.i == 11) || (this.i == 12)) {
this.j = 1999;
}
if ((this.j <= 1994) && ((this.i == 2) || (this.i == 6) || (this.i == 8))) {
this.i = 3;
}
if ((this.j >= 1996) && ((this.i == 2) || (this.i == 6) || (this.i == 8))) {
this.i = 9;
}
if ((this.j == 1995) && ((this.h > this.i + 2) || (this.i == this.h))) {
this.i = 6;
}
this.g = this.j;
this.f = this.i;
this.e = this.h;
return;
}
catch (Exception localException)
{
for (;;) {}
}
}
private int e()
{
try
{
int i1 = this.g;
i1 = this.d[((i1 - 1900) % 60)];
return i1;
}
catch (Exception localException)
{
localException.printStackTrace();
// for (;;) {}
}
//this.n.setText(getString(2131427370));
return 0;
}
private int f()
{
try
{
int i1 = this.f;
i1 = this.c[(i1 - 1)];
return i1;
}
catch (Exception localException)
{
// for (;;) {}
}
// this.n.setText(getString(2131427370));
return 0;
}
private int g()
{
try
{
int i1 = this.e;
i1 = this.b[(i1 - 1)];
return i1;
}
catch (Exception localException)
{
// for (;;) {}
}
// this.n.setText(getString(2131427370));
return 0;
}
String str;
private int h()
{
// try
// {
// str = ((EditText)findViewById(2131165227)).getText().toString();
// str = str.substring(8, str.length());
int i2 = this.f;
// i1 = 0;
// }
// catch (Exception localException)
// {
// for (;;)
// {
// String str;
// int i2;
// continue;
// i1 += 1;
// continue;
// int i1 = 0;
// }
// }
//if (i1 < this.m.length)
//这里是个循环
for(int i1=0;i1<this.m.length;i1+=1)
{
if (str.equals(this.m[i1]))
{
if ((i2 == 2) && (str.equals(this.m[6]))) {
return 63;
}
this.k = this.a[i1];
i1 = 1;
if (i1 == 0) {
// this.n.setText(getString(2131427370));
}
i1 = this.k;
return i1;
//this.n.setText(getString(2131427370));
// return 0;
}
}
return i2;
}
}
运行结果
python代码(菜鸡)
b_ary_day = [5, 10, 8, 15, 16, 15, 8, 16, 8, 16, 9, 17, 8, 17, 10, 8, 9, 18, 5, 15, 10, 9, 8, 9, 15, 18, 7, 8, 16, 6];
c_ary_month = [6, 7, 18, 9, 5, 16, 9, 15, 18, 8, 9, 5]
d_ary_year = [7, 7, 9, 12, 8, 7, 13, 5, 14, 5, 9, 17, 5, 7, 12, 8, 8, 6, 19, 6, 8, 16, 10, 6, 12, 9, 6, 7, 12, 5, 9, 8, 7, 8, 15, 9, 16, 8, 8, 19, 12, 6, 8, 7, 5, 15, 6, 16, 15, 7, 9, 12, 10, 7, 15, 6, 5, 14, 14, 9]
m_strList=["23to01", "01to03", "03to05", "05to07", "07to09", "09to11", "11to13", "13to15", "15to17", "17to19", "19to21", "21to23"]
a_ary = [16, 6, 7, 10, 9, 16, 10, 8, 8, 9, 6, 6]
#c 函数
def checkData(year,month,day):
if year<0 and year<189:
return False
if year<=1983 or year>2007:
return False
if month<1 or month>12:
return False
if day>=1 and day<=31:
return True
return False
#d 函数
def d_dayCheck(year,month,day):
ret_year=0
ret_month=0
ret_day=0
if year==1989 or year==2004:
day=31
if month==1 or month==4 or month==5 or month==7 or month==10 or month==11 or month==12:
year=1999
if year<=1994 and (month==2 or month==6 or month==8):
month=3
if year>=1996 and (month==2 or month==6 or month==8):
month=9
if year==1995 and (day>(2+month) or month==day):
month=6
ret_year=year
ret_month=month
ret_day=day
#print "xx"
return ret_year,ret_month,ret_day
#h函数
def check4(month,strxx):
#print month,strxx
v4=0
v0=0
k=0
for i,x in enumerate(m_strList):
if strxx==x:
if month==2 and strxx==m_strList[6]:
k=63
else:
k=a_ary[i]
return k
for y in xrange(1984,2008):
for m in xrange(1,13):
for d in xrange(1,32):
for x in m_strList:
#print y,m,d,checkData(y,m,d)
if checkData(y,m,d):
n_y,n_m,n_d=d_dayCheck(y,m,d)
num1=d_ary_year[(n_y-1900)%60]
num2=c_ary_month[n_m-1]
if n_d>=30:
continue
num3=b_ary_day[n_d-1]
num4=check4(m,x)
if num1+num2+num3+num4==34:
if y==n_y and n_m==m and n_d==d:
print n_y,n_m,n_d,num1,num2,num3,num4
print y,m,d,x
print "key:%04d%02d%02d%s"%(y,m,d,x)
运行结果
大佬脚本跑出来1400组解,哎。。差距,抽空改一下自己的
最后结果