文章目录
- 一、反编译查看源代码
- 二、源代码分析
- 三、方法1:正向暴力破解
- 四、方法二:按照筛选条件,逐步缩小范围
先下载软件,发现是个安卓的apk安装包,安装之后打开:
bugku-百家乐凯发k8
只有一个输入框,其他的点不了。应该是要输入某个字符串然后判断是否正确,之后返回flag。
打开apk反编译:
发现有两个activity,而且代码高度相似,查看androidmanifest.xml:
多个activity可以显示多个不同的界面,setcontentview就是设置一个activity的显示界面,使用setcontentview可以在activity中动态切换显示的view,这样,不需要多个activity就可以显示不同的界面。
其中它们调用的id:2130968603 = 0x7f04001b,2130968604 = 0x7f04001c,
在public.xml中找到相应id对应的资源:
打开layout文件夹,找到activity_main.xml和build.xml文件:
发现两个文件一模一样:
再查看两个类的源代码:
mainactivity.class
package com.geekerchina.hi;
import android.os.bundle;
import android.support.v7.app.appcompatactivity;
import android.view.menu;
import android.view.menuinflater;
import android.view.menuitem;
import android.view.view;
import android.view.view.onclicklistener;
import android.widget.button;
import android.widget.edittext;
public class mainactivity
extends appcompatactivity
{
protected void oncreate(bundle parambundle)
{
super.oncreate(parambundle);
setcontentview(2130968603);
((button)findviewbyid(2131427415)).setonclicklistener(new view.onclicklistener()
{
public void onclick(view paramanonymousview)
{
int i1 = integer.parseint(this.val$et1.gettext().tostring());
int k;
int i;
int n;
int j;
if ((i1 > 10000000) && (i1 < 99999999))
{
k = 1;
i = 10000000;
n = 1;
if ((math.abs(i1 / 1000 % 100 - 36) == 3) && (i1 % 1000 % 584 == 0)) {
j = 0;
}
}
for (;;)
{
int m = n;
if (j < 4)
{
if (i1 / k % 10 != i1 / i % 10) {
m = 0;
}
}
else
{
if (m == 1)
{
char c1 = (char)(i1 / 1000000);
char c2 = (char)(i1 / 10000 % 100);
char c3 = (char)(i1 / 100 % 100);
this.val$et1.settext("njctf{" c1 c2 c3 "f4n}");
}
return;
}
k *= 10;
i /= 10;
j = 1;
}
}
});
}
public boolean oncreateoptionsmenu(menu parammenu)
{
getmenuinflater().inflate(2131558400, parammenu);
return true;
}
public boolean onoptionsitemselected(menuitem parammenuitem)
{
if (parammenuitem.getitemid() == 2131427439) {
return true;
}
return super.onoptionsitemselected(parammenuitem);
}
}
androidtest.class
package com.geekerchina.hi;
import android.os.bundle;
import android.support.v7.app.appcompatactivity;
import android.view.menu;
import android.view.menuinflater;
import android.view.menuitem;
import android.view.view;
import android.view.view.onclicklistener;
import android.widget.button;
import android.widget.edittext;
public class androidtest
extends appcompatactivity
{
protected void oncreate(bundle parambundle)
{
super.oncreate(parambundle);
setcontentview(2130968604);
((button)findviewbyid(2131427415)).setonclicklistener(new view.onclicklistener()
{
public void onclick(view paramanonymousview)
{
int i1 = integer.parseint(this.val$et1.gettext().tostring());
int k;
int i;
int n;
int j;
if ((i1 > 10000000) && (i1 < 99999999))
{
k = 1;
i = 10000000;
n = 1;
if ((math.abs(i1 / 1000 % 100 - 36) == 3) && (i1 % 1000 % 584 == 0)) {
j = 0;
}
}
for (;;)
{
int m = n;
if (j < 3)
{
if (i1 / k % 10 != i1 / i % 10) {
m = 0;
}
}
else
{
if (m == 1)
{
char c1 = (char)(i1 / 1000000);
char c2 = (char)(i1 / 10000 % 100);
char c3 = (char)(i1 / 100 % 100 10);
this.val$et1.settext("njctf{have" c1 c2 c3 "f4n}");
}
return;
}
k *= 10;
i /= 10;
j = 1;
}
}
});
}
public boolean oncreateoptionsmenu(menu parammenu)
{
getmenuinflater().inflate(2131558400, parammenu);
return true;
}
public boolean onoptionsitemselected(menuitem parammenuitem)
{
if (parammenuitem.getitemid() == 2131427439) {
return true;
}
return super.onoptionsitemselected(parammenuitem);
}
}
两个类主要的差别就是:
mainactivity.class
androidtest.class
androidtest相比于mainactivity,就是为了不限制中间4,5两位的数字必须相等,而且出c3有加10再转换成字符。
直接用反编译的class代码暴力破解:
mainactivity.java
public class mainactivity {
public static void main(string[] args) {
int k;
int i;
int n;
int j;
for(int i1 = 10000001; i1 < 99999999 ; i1){
k = 1;
i = 10000000;
n = 1;
if ((math.abs(i1 / 1000 % 100 - 36) == 3) && (i1 % 1000 % 584 == 0)) {
j = 0;
}else {
continue;
}
for (;;) {
int m = n;
if (j < 3) {
if (i1 / k % 10 != (i1 / i % 10) ) {
//m = 0;
//当m = 0 时,表明当前的i1已经不符合条件,直接break;否则程序还会进入下一层循环,m又会重新等于n,导致程序还会继续运行,在j=4时始终会输出
break;
}
} else {
if (m == 1) {
char c1 = (char) (i1 / 1000000);
char c2 = (char) (i1 / 10000 % 100);
char c3 = (char) (i1 / 100 % 100 10);
system.out.println(i1);
system.out.println("njctf{have" c1 c2 c3 "f4n}");
}
break;
}
k *= 10;
i /= 10;
j = 1;
}
}
}
}
运行结果截图:
得到的flag为njctf{05#f4n}。
androidtest.java
public class androidtest {
public static void main(string[] args) {
int k;
int i;
int n;
int j;
for(int i1 = 10000001; i1 < 99999999 ; i1)
{
k = 1;
i = 10000000;
n = 1;
if ((math.abs(i1 / 1000 % 100 - 36) == 3) && (i1 % 1000 % 584 == 0)) {
j = 0;
}else {
continue;
}
for (;;)
{
int m = n;
if (j < 3)
{
if (i1 / k % 10 != i1 / i % 10) {
m = 0;
break;
}
}
else
{
if (m == 1)
{
char c1 = (char)(i1 / 1000000);
char c2 = (char)(i1 / 10000 % 100);
char c3 = (char)(i1 / 100 % 100 10);
system.out.println(i1);
system.out.println("njctf{have" c1 c2 c3 "f4n}");
}
return;
}
k *= 10;
i /= 10;
j = 1;
}
}
}
}
运行结果截图:
得到的flag为:njctf{have05-f4n}和njctf{have05if4n}。
bugku的提示:flag格式njctf{xxx} 并且 xxx只包含[a-z][a-z][0-9]。
所以真正的flag是njctf{have05if4n}。
其实网鼎杯原题的题目提示是:tips:don’t believe what you saw.
the flag’s format is njctf{xxx} and xxx only include [a-z][a-z][0-9].让我们不要相信看到的,意思就是程序的入口mainactivity这个表面上的类得到的答案是错误的,隐藏在它后面相似的androidtest才是真正的答案。
il要满足的条件:
1、首先范围是在10000000到99999999之间,一个8位数
2、之后是满足math.abs(i1 / 1000 % 100 - 36) == 3 && (i1 % 1000 % 584 == 0):
math.abs(i1 / 1000 % 100 - 36) == 3:il/1000得到的就是前5位的数字,i1 / 1000 % 100得到的就是第4、5位的数字,math.abs(i1 / 1000 % 100 - 36) == 3就是第4、5位的数字减36的绝对值是3,所以第4、5位是33或者39,符合条件的数格式为abc33efg或者abc39efg;
i1 % 1000 % 584 == 0:i1 % 1000,得到的就是后面的第6、7、8位的数字,i1 % 1000 % 584 == 0就是第6、7、8位的数字是584的倍数,第6、7、8位就是一个三位数所以就是584,符合条件的数格式为abc33584或者abc39584。
3、这里就是mainactivity和androidtest有差异的地方:
mainactivity.class:
for (;; )
{
int m = n;
if (j < 4)
{
if (i1 / k % 10 != i1 / i % 10) {
m = 0;
}
}
else
{
if (m == 1)
{
char c1 = (char)(i1 / 1000000);
char c2 = (char)(i1 / 10000 % 100);
char c3 = (char)(i1 / 100 % 100);
this.val$et1.settext("njctf{" c1 c2 c3 "f4n}");
}
return;
}
k *= 10;
i /= 10;
j = 1;
}
androidtest.class:
for (;; )
{
int m = n;
if (j < 3)
{
if (i1 / k % 10 != i1 / i % 10) {
m = 0;
}
}
else
{
if (m == 1)
{
char c1 = (char)(i1 / 1000000);
char c2 = (char)(i1 / 10000 % 100);
char c3 = (char)(i1 / 100 % 100 10);
this.val$et1.settext("njctf{have" c1 c2 c3 "f4n}");
}
return;
}
k *= 10;
i /= 10;
j = 1;
}
这两段代码的主要判断是相似的,在mainactivity类中就是if (j < 4){ if (i1 / k % 10 != i1 / i % 10) { m = 0;}}
k *= 10;
i /= 10;
j = 1;
k的初始值是1,i的初始值是10000000,所以i1 / k % 10就是分别获得第8、7、6、5位上的数字,i1 / i % 10就是分别获得第1、2、3、4位上的数字,再要求它们分别相等,也就是abcddcba的形式,结合上面的条件,符合条件的数为48533584。
48533584经过
char c1 = (char)(i1 / 1000000);//第1、2位数字
char c2 = (char)(i1 / 10000 % 100);//第3、4位数字
char c3 = (char)(i1 / 100 % 100);//第3、4位数字
this.val$et1.settext("njctf{" c1 c2 c3 "f4n}");
得到的字符串为njctf{05#f4n}。
而在androidtest类中,androidtest相比于mainactivity,就是j<4变成j<3,目的是为了不限制中间4,5两位的数字必须相等,这样就只用第8、7、6位上的数字和第1、2、3位的数字分别相等了,也就是abcdecba的形式,结合上面的条件得到了符合条件的两个数为48533584和48539584。
48533584和48539584经过
char c1 = (char)(i1 / 1000000);//第1、2位数字
char c2 = (char)(i1 / 10000 % 100);//第3、4位数字
char c3 = (char)(i1 / 100 % 100 10);//第5、6位数字再加10
this.val$et1.settext("njctf{have" c1 c2 c3 "f4n}");
得到的字符串为njctf{have05-f4n}和njctf{have05if4n}。 其中c3字符有额外加10再转换成字符,是为了让第5、6位上的数字转化的字符#和_变成字符-和i,从而让i在[a-z][a-z][0-9]的范围内,得到唯一一个满足题目提示flag格式njctf{xxx} 并且 xxx只包含[a-z][a-z][0-9]的flag:njctf{have05if4n}。