文章目录
- 一、反编译查看源代码
- 二、源代码分析
- 三、方法1:正向暴力破解
- 四、方法二:按照筛选条件,逐步缩小范围
先下载软件,发现是个安卓的apk安装包,安装之后打开:
一、反编译查看源代码
只有一个输入框,其他的点不了。应该是要输入某个字符串然后判断是否正确,之后返回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再转换成字符。
三、方法1:正向暴力破解
直接用反编译的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}。