pythonvbb转换txt_BUGKU-逆向(reverse)-writeup

前言:在bugku上把能写的逆向都写了,由于大佬们的writeup太深奥或者说太简洁了让我(小白)看得云里雾里。所以我写了这个详细点的writeup(理解错的地方望指出),尽量让大家都看得懂。最近比较忙先写到了这里,未完待续

入门逆向

下载后ida打开,双击_mail函数里就有flag

25bf21b02587c0425435cb73a02f34df

Easy_vb

下载后ida打开,往下翻里就有flag

6cd7969bfba9439c7e2261a02ac9098b

提交flag出错,将MCTF改成flag即可。

Easy_Re

下载后ida打开,双击_mail函数 ,F5翻译为伪C代码

8053c7b2b699b6f841247c9b3999bb20

strcmp()对面输入的值是否等于xmmword_413E34位置的值,双击跟过去,发现了flag

6582f58ca6150018d28afd9ec73d793e

小端存储的问题,看起来反了而已。

a5bc34720fc5f13c6d0b8417f533fbb7

游戏过关

下载后ida打开,看到函数比较多,分享一种快速找关键函数的方法。

首先就是看运行遍程序,了解下程序流程以及关键字符串。然后打开ida

1.Shift+F12查看下字符串。

7555087fce7d1e81f48fe8f611268d1e

2.然后双击过去。

6b7d6edcaa4494ffbc0d945eecdc58bf

3.再按Cirt+X交叉引用显示调用位置

e2eca2749d20a5eb9087b1524f2b32fc

然后F5看下伪代码

fcbf3d8a4abcee282951fdd87f4067db

打印出done!!! the flag is 然后有两个数组按位异或再和0x13异或生成flag

#!usr/bin/env python

#!coding=utf-8

__author__ = 'zhengjim'

array1 = [18,64,98,5,2,4,6,3,6,48,49,65,32,12,48,65,31,78,62,32,49,32,1,57,96,3,21,9,4,62,3,5,4,1,2,3,44,65,78,32,16,97,54,16,44,52,32,64,89,45,32,65,15,34,18,16,0]

array2 = [123,32,18,98,119,108,65,41,124,80,125,38,124,111,74,49,83,108,94,108,84,6,96,83,44,121,104,110,32,95,117,101,99,123,127,119,96,48,107,71,92,29,81,107,90,85,64,12,43,76,86,13,114,1,117,126,0]

flag = ''

for i in range(len(array1)):

flag+= chr(array1[i] ^ array2[i] ^ 0x13 )

print flag

Timer(阿里CTF)

下载文件发现是apk ,先安装运行下发现有一个倒计时,只是时间为200000秒。猜测是让时间走完获取flag。

4a1f66af84284ae36dc4df73fb7736ec

用jadx-gui反编译,双击看MainActivity查看

package net.bluelotus.tomorrow.easyandroid;

import android.os.Bundle;

import android.os.Handler;

import android.support.v7.app.AppCompatActivity;

import android.view.Menu;

import android.view.MenuItem;

import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

int beg = (((int) (System.currentTimeMillis() / 1000)) + 200000);

int k = 0;

int now;

long t = 0;

public native String stringFromJNI2(int i);

public static boolean is2(int n) {

if (n <= 3) {

if (n > 1) {

return true;

}

return false;

} else if (n % 2 == 0 || n % 3 == 0) {

return false;

} else {

int i = 5;

while (i * i <= n) {

if (n % i == 0 || n % (i + 2) == 0) {

return false;

}

i += 6;

}

return true;

}

}

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView((int) R.layout.activity_main);

final TextView tv1 = (TextView) findViewById(R.id.textView2);

final TextView tv2 = (TextView) findViewById(R.id.textView3);

final Handler handler = new Handler();

handler.postDelayed(new Runnable() {

public void run() {

MainActivity.this.t = System.currentTimeMillis();

MainActivity.this.now = (int) (MainActivity.this.t / 1000);

MainActivity.this.t = 1500 - (MainActivity.this.t % 1000);

tv2.setText("AliCTF");

if (MainActivity.this.beg - MainActivity.this.now <= 0) {

tv1.setText("The flag is:");

tv2.setText("alictf{" + MainActivity.this.stringFromJNI2(MainActivity.this.k) + "}");

}

MainActivity mainActivity;

if (MainActivity.is2(MainActivity.this.beg - MainActivity.this.now)) {

mainActivity = MainActivity.this;

mainActivity.k += 100;

} else {

mainActivity = MainActivity.this;

mainActivity.k--;

}

tv1.setText("Time Remaining(s):" + (MainActivity.this.beg - MainActivity.this.now));

handler.postDelayed(this, MainActivity.this.t);

}

}, 0);

}

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.menu_main, menu);

return true;

}

public boolean onOptionsItemSelected(MenuItem item) {

if (item.getItemId() == R.id.action_settings) {

return true;

}

return super.onOptionsItemSelected(item);

}

static {

System.loadLibrary("lhm");

}

}

首先初始化了beg为当前时间加上200000。(System.currentTimeMillis() / 1000)是获得系统的时间,单位为毫秒,转换为秒。

看onCreate方法,找到关键处

if (MainActivity.this.beg - MainActivity.this.now <= 0) {

tv1.setText("The flag is:");

tv2.setText("alictf{" + MainActivity.this.stringFromJNI2(MainActivity.this.k) + "}");

}

所以MainActivity.this.beg - MainActivity.this.now <= 0 就是过了得时间。如果过了200000秒则出现flag。flag是使用native层来打印。

思路:能不能直接跳过200000秒直接出现flag呢?

有一个关键变量k,往下看,看看k有没有什么运算。

if (MainActivity.is2(MainActivity.this.beg - MainActivity.this.now)) {

mainActivity = MainActivity.this;

mainActivity.k += 100;

} else {

mainActivity = MainActivity.this;

mainActivity.k--;

}

将差值用is2函数判断,如果true,就k+100 ,如果false,就k-1。那就要看下is2函数

public static boolean is2(int n) {

if (n <= 3) {

if (n > 1) {

return true;

}

return false;

} else if (n % 2 == 0 || n % 3 == 0) {

return false;

} else {

int i = 5;

while (i * i <= n) {

if (n % i == 0 || n % (i + 2) == 0) {

return false;

}

i += 6;

}

return true;

}

}

直接照着写一个即可,然后可以算出关键变量k

解密脚本

#!usr/bin/env python

#!coding=utf-8

__author__ = 'zhengjim'

def is2(n):

if(n <= 3):

if(n > 1):

return True

return False

elif(n % 2 == 0 or n % 3 == 0):

return False

else:

i = 5

while(i * i <= n):

if (n % i == 0 or n % (i + 2) == 0):

return False

i += 6

return True

k=0

for i in xrange(200000,0,-1):

k = k + 100 if is2(i) else k - 1

print k

算出k = 1616384

然后就可以绕过200000秒将k带入传入进去获取flag。

实现的话,用Androidkiller打开项目,因为跳转后输出了The flag is,所以搜索该字符串,双击跟过去。

eab8fe191b54bae45902f0f1f41a1937

往上看第113行的if-gtz v0, :cond_0。 if-ltz是如果大于0跳转 ,那改成如果小于0跳转就跳过了200000秒等待了。对应的语句为if-ltz v0, :cond_0。

然后要找到赋值k的位置,看第129行-149行,因为k的值是在alictf{和}之间传入的。

看到了139行的的iget v3, v3, Lnet/bluelotus/tomorrow/easyandroid/MainActivity;->k:I,知道v3是k的值。

于是在下面赋值const v3,1616384

6e3f6a9bb8c36002ee4559af81c54dde

然后保存,编译,安装运行就出现flag。

6653f6505243a138165fb077da97a7af

flag

de7c0271285e98bca40d8db6f3d46cb5

逆向入门

下载后发现不是pe文件,右键txt打开,看到data:image/png;base64,iVBORXXXXXX...开头的,为图像文件。

开头添加 ,html打开。有二维码扫描既可。

32d1896064d074ce9a7b31a451b566c6

love

下载来用peid看是C++的,先运行下。

2a1657a845d5315ae10c4bd95b143f6c

要输入flag。用ida打开

按之前说的方法,快速定位到关键函数

66023b414663a2bc6a331cc507f2001c

打F5查看伪代码

0f5ed2d616a3cdbcd6fa68dabae3182a

可以看到有两步加密,第一步是先sub_4110BE(&Str, v0, &v11);用这个函数加密。然后再去循环加密

for ( j = 0; j < v8; ++j )

Dest[j] += j;

然后把加密后的字符串与str2相比较。str2的值为e3nifIH9b_C@n@dH,先把循环逆向了。

#!usr/bin/env python

#!coding=utf-8

__author__ = 'zhengjim'

str2 ='e3nifIH9b_C@n@dH'

flag =''

for i in range(len(str2)):

flag += chr(ord(str2[i])- i)

print flag

得到e2lfbDB2ZV95b3V9

然后看sub_4110BE()函数。一串长算法,发现首先将输入的flag每3位变成4位。然后有64位密码表。其实就是个base64加密(记下来,base64加密算法的特征)。

890510e3bef874e6c589748b8a5ac50f

也就是将刚刚得到的值base64解密就是flag。

LoopAndLoop(阿里CTF)

下载文件发现是apk ,先安装运行下发现有一个输入框,随便输入点getyourflag 跳出Not Right

d9fccbd0b91671aeab235e2ec518aa40

用jadx-gui反编译,双击看MainActivity查看

package net.bluelotus.tomorrow.easyandroid;

import android.os.Bundle;

import android.support.v7.app.AppCompatActivity;

import android.view.Menu;

import android.view.MenuItem;

import android.view.View;

import android.view.View.OnClickListener;

import android.widget.Button;

import android.widget.EditText;

import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

public native int chec(int i, int i2);

public native String stringFromJNI2(int i);

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView((int) R.layout.activity_main);

final TextView tv1 = (TextView) findViewById(R.id.textView2);

final TextView tv2 = (TextView) findViewById(R.id.textView3);

final EditText ed = (EditText) findViewById(R.id.editText);

((Button) findViewById(R.id.button)).setOnClickListener(new OnClickListener() {

public void onClick(View v) {

try {

int in_int = Integer.parseInt(ed.getText().toString());

if (MainActivity.this.check(in_int, 99) == 1835996258) {

tv1.setText("The flag is:");

tv2.setText("alictf{" + MainActivity.this.stringFromJNI2(in_int) + "}");

return;

}

tv1.setText("Not Right!");

} catch (NumberFormatException e) {

tv1.setText("Not a Valid Integer number");

}

}

});

}

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.menu_main, menu);

return true;

}

public boolean onOptionsItemSelected(MenuItem item) {

if (item.getItemId() == R.id.action_settings) {

return true;

}

return super.onOptionsItemSelected(item);

}

public String messageMe(String text) {

return "LoopOk" + text;

}

public int check(int input, int s) {

return chec(input, s);

}

public int check1(int input, int s) {

int t = input;

for (int i = 1; i < 100; i++) {

t += i;

}

return chec(t, s);

}

public int check2(int input, int s) {

int t = input;

int i;

if (s % 2 == 0) {

for (i = 1; i < 1000; i++) {

t += i;

}

return chec(t, s);

}

for (i = 1; i < 1000; i++) {

t -= i;

}

return chec(t, s);

}

public int check3(int input, int s) {

int t = input;

for (int i = 1; i < 10000; i++) {

t += i;

}

return chec(t, s);

}

static {

System.loadLibrary("lhm");

}

}

看到关键代码:

public void onClick(View v) {

try {

int in_int = Integer.parseInt(ed.getText().toString());

if (MainActivity.this.check(in_int, 99) == 1835996258) {

tv1.setText("The flag is:");

tv2.setText("alictf{" + MainActivity.this.stringFromJNI2(in_int) + "}");

return;

}

tv1.setText("Not Right!");

} catch (NumberFormatException e) {

tv1.setText("Not a Valid Integer number");

}

}

流程为:将用户输入作为参数1(one),99作为参数2(two)传入check函数里,如果返回的值为1835996258,则将用户输入作为参数传入stringFromJNI2函数计算,返回值与alictf{和}拼接组成flag 。

于是我们只要逆向出check函数,将1835996258带入得到的值,拿到apk里边运行即可得到flag。

追过去发现check函数调用了chec函数 为Native层的函数

4a1e8f318e3f998fa0f39853f4f257c1

stringFromJNI2函数也为Native层的函数

96b3357d9dd264705c2f04eb74fbba07

加载了System.loadLibrary("lhm");,所以逆向liblhm.so文件。

用IDA打开,还是上面的办法,找到了chec函数

这部分看得比较混乱,查了比较多的资料,所以有不对之处请指出来。

汇编

ad50acedb431fcc0aa4efc89b024c67d

伪代码

90b32a29abe700e8fcacc62129246694

上面是自己加了注释,然后通过看汇编与伪代码分析得出流程,即将传入的99进行2 * i % 3运算,判断得到的余数。

如果等于0,将one与two-1传到JAVA层的check1进行计算

如果等于1,将one与two-1传到JAVA层的check2进行计算

如果等于2,将one与two-1传到JAVA层的check3进行计算

去查看下check123函数

public int check1(int input, int s) {

int t = input;

for (int i = 1; i < 100; i++) {

t += i;

}

return chec(t, s);

}

public int check2(int input, int s) {

int t = input;

int i;

if (s % 2 == 0) {

for (i = 1; i < 1000; i++) {

t += i;

}

return chec(t, s);

}

for (i = 1; i < 1000; i++) {

t -= i;

}

return chec(t, s);

}

public int check3(int input, int s) {

int t = input;

for (int i = 1; i < 10000; i++) {

t += i;

}

return chec(t, s);

}

发现只是简单的遍历然后加减运算,计算完又返回chec函数

只到two小于等于1,输出结果。

于是写逆函数就不难了,check123 加变减,减变加就可以了。本来从99到2(因为two小于等于1),变成从2到99。

#!usr/bin/env python

#!coding=utf-8

__author__ = 'zhengjim'

def check1(input,s):

t = input

for i in range(1,100):

t -= i

return t

def check2(input,s):

t = input

if(s % 2 == 0):

for i in range(1,1000):

t -= i

return t

for i in range(1,1000):

t += i

return t

def check3(input,s):

t = input

for i in range(1,10000):

t -= i

return t

output = 1835996258

for i in range(2,100):

flag = 2 * i % 3

if flag == 0 :

output = check1(output, i-1)

elif flag == 1 :

output = check2(output, i-1)

elif flag == 2 :

output = check3(output, i-1)

print output

得到236492408 ,带入apk运行出现flag。

e2a6a62207572aa40ba9f0dbea31a64c

easy-100(LCTF)

下载文件发现是apk ,先安装运行下(我的逍遥安卓运行失败,不懂为啥)。

用jeb2反编译(用jadx-gui反编译出了问题,a方法重载反编译出了问题),双击看MainActivity查看

package com.example.ring.myapplication;

import android.content.pm.ApplicationInfo;

import android.os.Bundle;

import android.support.v7.a.q;

import java.io.InputStream;

public class MainActivity extends q {

private String v;

public MainActivity() {

super();

}

static String a(MainActivity arg1) {

return arg1.v;

}

static boolean a(MainActivity arg1, String arg2, String arg3) {

return arg1.a(arg2, arg3);

}

private boolean a(String arg4, String arg5) {

return new c().a(arg4, arg5).equals(new String(new byte[]{21, -93, -68, -94, 86, 117, -19, -68, -92, 33, 50, 118, 16, 13, 1, -15, -13, 3, 4, 103, -18, 81, 30, 68, 54, -93, 44, -23, 93, 98, 5, 59}));

}

protected void onCreate(Bundle arg3) {

super.onCreate(arg3);

this.setContentView(2130968602);

ApplicationInfo v0 = this.getApplicationInfo();

v0.flags &= 2;

this.p();

this.findViewById(2131427413).setOnClickListener(new d(this));

}

private void p() {

try {

InputStream v0_1 = this.getResources().getAssets().open("url.png");

int v1 = v0_1.available();

byte[] v2 = new byte[v1];

v0_1.read(v2, 0, v1);

byte[] v0_2 = new byte[16];

System.arraycopy(v2, 144, v0_2, 0, 16);

this.v = new String(v0_2, "utf-8");

}

catch(Exception v0) {

v0.printStackTrace();

}

}

}

首先看onCreate()方法

protected void onCreate(Bundle arg3) {

super.onCreate(arg3);

this.setContentView(2130968602);

ApplicationInfo v0 = this.getApplicationInfo();

v0.flags &= 2;

this.p();

this.findViewById(2131427413).setOnClickListener(new d(this));

}

执行了p()方法,然后创建了一个按钮监听事件在classs d 。

跟过去看下class d

package com.example.ring.myapplication;

import android.view.View$OnClickListener;

import android.view.View;

import android.widget.TextView;

import android.widget.Toast;

class d implements View$OnClickListener {

d(MainActivity arg1) {

this.a = arg1;

super();

}

public void onClick(View arg5) {

if(MainActivity.a(this.a, MainActivity.a(this.a), this.a.findViewById(2131427414).getText().toString())) {

View v0 = this.a.findViewById(2131427412);

Toast.makeText(this.a.getApplicationContext(), "Congratulations!", 1).show();

((TextView)v0).setText(2131099682);

}

else {

Toast.makeText(this.a.getApplicationContext(), "Oh no.", 1).show();

}

}

}

如果a()方法返回真,则输出flag。第一个参数为句柄,第二个参数调用了另外一个a方法返回一个字符串,第三个参数是我们输入的字符串。

跟过去看下a()方法,发现为重载(JAVA重载概念)

static String a(MainActivity arg1) {

return arg1.v;

}

static boolean a(MainActivity arg1, String arg2, String arg3) {

return arg1.a(arg2, arg3);

}

private boolean a(String arg4, String arg5) {

return new c().a(arg4, arg5).equals(new String(new byte[]{21, -93, -68, -94, 86, 117, -19, -68, -92, 33, 50, 118, 16, 13, 1, -15, -13, 3, 4, 103, -18, 81, 30, 68, 54, -93, 44, -23, 93, 98, 5, 59}));

}

static String a(MainActivity arg1)方法直接返回了字符串,返回的是arg1.v

private boolean a(String arg4, String arg5)方法中调用了equals方法进行比较返回布尔值。

由于arg4是类d中传入的MainActivity.a(this.a),所以得先看返回了什么字符串v,而v是MainActivity的String类型的数据成员以及有相应的方法进行赋值p方法。

private void p() {

try {

InputStream v0_1 = this.getResources().getAssets().open("url.png");

int v1 = v0_1.available();

byte[] v2 = new byte[v1];

v0_1.read(v2, 0, v1);

byte[] v0_2 = new byte[16];

System.arraycopy(v2, 144, v0_2, 0, 16);

this.v = new String(v0_2, "utf-8");

}

catch(Exception v0) {

v0.printStackTrace();

}

}

首先读取url.png文件以二进制数据取出来。从文件的144位置开始,读取16字符保存为v。

用winhex打开这张文件,找到144位置,往后的16位为this_is_the_key.

32604bc207710631fdb34f4c35c210bf

得到了key后,要看回三参数的a方法

static boolean a(MainActivity arg1, String arg2, String arg3) {

return arg1.a(arg2, arg3);

}

private boolean a(String arg4, String arg5) {

return new c().a(arg4, arg5).equals(new String(new byte[]{21, -93, -68, -94, 86, 117, -19, -68, -92, 33, 50, 118, 16, 13, 1, -15, -13, 3, 4, 103, -18, 81, 30, 68, 54, -93, 44, -23, 93, 98, 5, 59}));

}

发现三参数a方法调用了两参数a方法,将this_is_the_key. 与用户输入作为参数传了进去。.而两参数a方法是调用类c的两参数a方法.计算完后和后面的字节比较。相等返回真。跟过去看下类c的两参数a方法

public String a(String arg5, String arg6) {

String v0 = this.a(arg5);

String v1 = "";

a v2 = new a();

v2.a(v0.getBytes());

try {

v0 = new String(v2.b(arg6.getBytes()), "utf-8");

}

catch(Exception v0_1) {

v0_1.printStackTrace();

v0 = v1;

}

return v0;

}

首先将this_is_the_key.传入一个参数a方法,然后将返回值赋值给v0。看下一个参数a方法。

private String a(String arg4) {

String v0_2;

try {

arg4.getBytes("utf-8");

StringBuilder v1 = new StringBuilder();

int v0_1;

for(v0_1 = 0; v0_1 < arg4.length(); v0_1 += 2) {

v1.append(arg4.charAt(v0_1 + 1));

v1.append(arg4.charAt(v0_1));

}

v0_2 = v1.toString();

}

catch(UnsupportedEncodingException v0) {

v0.printStackTrace();

v0_2 = null;

}

return v0_2;

}

将传入的字符串每两个字符为一组然后交换这两个字符的位置最后返回改变后的字符串。就是变成htsii__sht_eek.y,可以手动也可以写脚本。

#!usr/bin/env python

#!coding=utf-8

__author__ = 'zhengjim'

key = 'this_is_the_key.'

ckey =""

for i in range(0,len(key),2):

ckey += key[i+1]

ckey += key[i]

print(ckey)

回到类c的两参数a方法,实例化的了类a,然后将用户输入作为参数带入。跟过去看看。

package com.example.ring.myapplication;

import java.io.UnsupportedEncodingException;

import java.security.MessageDigest;

import java.security.NoSuchAlgorithmException;

import javax.crypto.Cipher;

import javax.crypto.NoSuchPaddingException;

import javax.crypto.spec.SecretKeySpec;

public class a {

private SecretKeySpec a;

private Cipher b;

public a() {

super();

}

protected void a(byte[] arg4) {

if(arg4 != null) {

goto label_15;

}

try {

this.a = new SecretKeySpec(MessageDigest.getInstance("MD5").digest("".getBytes("utf-8")), "AES");

this.b = Cipher.getInstance("AES/ECB/PKCS5Padding");

return;

label_15:

this.a = new SecretKeySpec(arg4, "AES");

this.b = Cipher.getInstance("AES/ECB/PKCS5Padding");

}

catch(UnsupportedEncodingException v0) {

v0.printStackTrace();

}

catch(NoSuchAlgorithmException v0_1) {

v0_1.printStackTrace();

}

catch(NoSuchPaddingException v0_2) {

v0_2.printStackTrace();

}

}

protected byte[] b(byte[] arg4) {

this.b.init(1, this.a);

return this.b.doFinal(arg4);

}

}

发现是用AES加密,ECB模式,PKCS5Padding填充。然后要找下密钥。this.a = new SecretKeySpec(arg4, "AES");是将arg4作为密钥。而arg4则是在类c中传入的v0.getBytes(),也就是密钥为htsii__sht_eek.y。

回到类c的两参数a方法,将返回的字符串赋值给v0然后再返回。到了MainActivity的两参数a方法,与那串字符串比较。正确则返回真,就出现Congratulations!。

所以百度了一个AES解密网站。由于网站输出的是base64。所以讲密文转为base64格式。

#!usr/bin/env python

#!coding=utf-8

__author__ = 'zhengjim'

import base64

str1 = [21, -93, -68, -94, 86, 117, -19, -68, -92, 33,50, 118, 16, 13, 1, -15, -13, 3, 4, 103, -18, 81, 30, 68, 54, -93, 44, -23, 93, 98, 5, 59]

ctext = ''

for i in str1:

ctext += chr((i+256)%256)

a = base64.b64encode(ctext)

print(a)

得到了密文为FaO8olZ17bykITJ2EA0B8fMDBGfuUR5ENqMs6V1iBTs=,密钥为htsii__sht_eek.y,AES解密后出现flag。

df3a7c5a45880bb13ddbca073f61503a

SafeBox(NJCTF)

首先下载发现是apk,安装运行下。就一个输入框,其他的按不了。

用jadx-gui反编译下,双击MainActivity查看。

package com.geekerchina.hi;

import android.os.Bundle;

import android.support.v7.app.AppCompatActivity;

import android.view.Menu;

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 savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView((int) R.layout.activity_main);

final EditText Et1 = (EditText) findViewById(R.id.editText);

((Button) findViewById(R.id.button)).setOnClickListener(new OnClickListener() {

public void onClick(View v) {

String strTmp = "NJCTF{";

int i = Integer.parseInt(Et1.getText().toString());

if (i > 10000000 && i < 99999999) {

int t = 1;

int t1 = 10000000;

int flag = 1;

if (Math.abs(((i / 1000) % 100) - 36) == 3 && (i % 1000) % 584 == 0) {

for (int j = 0; j < 4; j++) {

if ((i / t) % 10 != (i / t1) % 10) {

flag = 0;

break;

}

t *= 10;

t1 /= 10;

}

if (flag == 1) {

char c2 = (char) ((i / 10000) % 100);

char c3 = (char) ((i / 100) % 100);

Et1.setText(strTmp + ((char) (i / 1000000)) + c2 + c3 + "f4n}");

}

}

}

}

});

}

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.menu_main, menu);

return true;

}

public boolean onOptionsItemSelected(MenuItem item) {

if (item.getItemId() == R.id.action_settings) {

return true;

}

return super.onOptionsItemSelected(item);

}

}

看到onCreate方法关键位置18行-37行,输入一个8位数满足条件后,将其变换后与NJCTF{和f4n}拼接。

用python脚本来爆破

#!usr/bin/env python

#!coding=utf-8

__author__ = 'zhengjim'

import math

for i in range(10000000, 99999999):

t = 1

t1 =10000000

flag = 1

if (abs(((i / 1000) % 100) - 36) == 3 and (i % 1000) % 584 == 0):

for j in range(4):

if ((i / t) % 10 != (i / t1) % 10):

flag = 0

break

t *= 10

t1 /= 10

if(flag ==1):

print i

c2 = chr((i / 10000) % 100)

c3 = chr((i / 100) % 100)

print('NJCTF{'+chr(i / 1000000)+c2+c3+'f4n}')

得到i应该为48533584 ,flag为NJCTF{05#f4n},但提交却发现错误了。看了好几遍发现没错。再看目录发现了类androidTest。

package com.geekerchina.hi;

import android.os.Bundle;

import android.support.v7.app.AppCompatActivity;

import android.view.Menu;

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 savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView((int) R.layout.build);

final EditText Et1 = (EditText) findViewById(R.id.editText);

((Button) findViewById(R.id.button)).setOnClickListener(new OnClickListener() {

public void onClick(View v) {

String strTmp = "NJCTF{have";

int i = Integer.parseInt(Et1.getText().toString());

if (i > 10000000 && i < 99999999) {

int t = 1;

int t1 = 10000000;

int flag = 1;

if (Math.abs(((i / 1000) % 100) - 36) == 3 && (i % 1000) % 584 == 0) {

for (int j = 0; j < 3; j++) {

if ((i / t) % 10 != (i / t1) % 10) {

flag = 0;

break;

}

t *= 10;

t1 /= 10;

}

if (flag == 1) {

char c2 = (char) ((i / 10000) % 100);

char c3 = (char) (((i / 100) % 100) + 10);

Et1.setText(strTmp + ((char) (i / 1000000)) + c2 + c3 + "f4n}");

}

}

}

}

});

}

public boolean onCreateOptionsMenu(Menu menu) {

getMenuInflater().inflate(R.menu.menu_main, menu);

return true;

}

public boolean onOptionsItemSelected(MenuItem item) {

if (item.getItemId() == R.id.action_settings) {

return true;

}

return super.onOptionsItemSelected(item);

}

}

和MainActivity很像,但有细微不同:

第27行的String strTmp = "NJCTF{have";

第27行的for (int j = 0; j < 3; j++) {

第39行的char c3 = (char) (((i / 100) % 100) + 10);

python脚本爆破

#!usr/bin/env python

#!coding=utf-8

__author__ = 'zhengjim'

import math

for i in range(10000000, 99999999):

t = 1

t1 = 10000000

flag = 1

if (abs(((i / 1000) % 100) - 36) == 3 and (i % 1000) % 584 == 0):

for j in range(3):

if ((i / t) % 10 != (i / t1) % 10):

flag = 0

break

t *= 10

t1 /= 10

if (flag == 1):

print i

c2 = chr((i / 10000) % 100)

c3 = chr((i / 100) % 100 + 10)

print('NJCTF{have' + chr(i / 1000000) + c2 + c3 + 'f4n}')

得到两组答案。

i为48533584 ,flag为NJCTF{have05-f4n}

i为48539584 ,flag为NJCTF{have05if4n}

均提交试试发现第二组为正确。

Mountain climbing

下载后运行,发现要输入最大数字,乱输后跳出error。

7bd3853abaad08474e34d0fa46552af8

用PEID 查看下 发现有 UPX的壳。

b29ebfc0420db994cc4077be7e534c1f

直接用52pojie的脱UPX工具进行脱壳。成功

583e55ddac32a537a1e236fd1c45274e

载入IDA

__int64 main_0()

{

int v0; // edx

__int64 v1; // ST04_8

char v3; // [esp+0h] [ebp-160h]

int v4; // [esp+D0h] [ebp-90h]

int j; // [esp+DCh] [ebp-84h]

int i; // [esp+E8h] [ebp-78h]

char Str[104]; // [esp+F4h] [ebp-6Ch]

srand(0xCu);

j_memset(&unk_423D80, 0, 0x9C40u);

for ( i = 1; i <= 20; ++i )

{

for ( j = 1; j <= i; ++j )

dword_41A138[100 * i + j] = rand() % 100000;

}

((void (__cdecl *)(const char *, char))sub_41134D)("input your key with your operation can get the maximum:", v3);

sub_411249("%s", (unsigned int)Str);

if ( j_strlen(Str) == 19 )

{

sub_41114F(Str);

v4 = 0;

j = 1;

i = 1;

dword_423D78 += dword_41A138[101];

while ( v4 < 19 )

{

if ( Str[v4] == 76 )

{

dword_423D78 += dword_41A138[100 * ++i + j];

}

else

{

if ( Str[v4] != 82 )

{

((void (__cdecl *)(const char *, char))sub_41134D)("error\n", v3);

system("pause");

goto LABEL_18;

}

dword_423D78 += dword_41A138[100 * ++i + ++j];

}

++v4;

}

sub_41134D("your operation can get %d points\n", dword_423D78);

system("pause");

}

else

{

((void (__cdecl *)(const char *, char))sub_41134D)("error\n", v3);

system("pause");

}

LABEL_18:

HIDWORD(v1) = v0;

LODWORD(v1) = 0;

return v1;

}

首先生成一个数组存在。这个数组由伪随机数生成。srand(0xCu)随机数种子一定,那么rand出来的数也是一定的。

然后往下看,下面是自己加入了注释。

69cf0b148a5793c74da5d7f6c1f2dbcd

总得来看就是先将从arr[101] 相加,往下的循环为:

第一次循环:经过用户按"L"或"R"来控制加arr[201]还是arr[202]

第二次循环:(情况1)第一次选择了arr[201]:经过用户按"L"或"R"来控制加arr[301]还是arr[302] (情况2)第二次选择了arr[202]:经过用户按"L"或"R"来控制加arr[302]还是arr[303]

跟两次循环帮组理解,这样子就很清楚了。整个题可以理解成站在山顶往下走,每一行走到的数字累加,每次只能走一格(左或右),走到最后一行。然后最后的数要最大。与题目Mountain climbing呼应

那首先要先找到这座山,看ida反编译后是dword_41A138存在0041A138,我们用OD载入后运后,跟过去看看。

be367dd76f709da318d5f9b2c1275828

dword是4字节的,而且第一个数是存在在[101]位置的。所以首位置存在41A2CC位置。往下的都是往后400节。

fbec0f98bf1d05c8018a1e3e1c2f5e99

还是要注意小端存储问题。所以可以得到arr[101] = 4D(16进制) =77 , arr[201] = 15FC(16进制) = 5628(10进制) , arr[202] = 1858(16进制) = 6232(10进制)

有耐心的话可以一行行扣出来 ,没有的话,直接还原c代码生成"一座山"。

代码如下:

srand(0xCu);

for (int i = 1; i <= 20; ++i)

{

for (int j = 1; j <= i; ++j)

arr[100 * i + j] = rand() % 100000;

}

for (int i = 1; i <= 20; ++i)

{

for (int j = 1; j <= i; ++j)

printf("%5d ", arr[100 * i + j]);

cout << endl;

}

得到完整的,与OD获取的值是一致。

cf983467709ee808c20db76f63b9c7be

接下来就是找到最大解的路线。直接遍历所有路线,然后比较最大数。

首先生成所有路径

#!usr/bin/env python

#!coding=utf-8

__author__ = 'zhengjim'

import itertools

words = "LR"

r = itertools.product(words,repeat=19)

f = open("all_roads.txt",'a')

for i in r:

f.write("".join(i)+"\n")

f.close()

db37923107f2f864ab728e2233a580d2

然后将每个走法得到的值进行判断大小,最大的值就是我们要的答案。

#!usr/bin/env python

#!coding=utf-8

__author__ = 'zhengjim'

s = [

[77],

[5628, 6232],

[29052, 1558, 26150],

[12947, 29926, 11981, 22371],

[4078, 28629, 4665, 2229, 24699],

[27370, 3081, 18012, 24965, 2064, 26890],

[21054, 5225, 11777, 29853, 2956, 22439, 3341],

[31337, 14755, 5689, 24855, 4173, 32304, 292, 5344],

[15512, 12952, 1868, 10888, 19581, 13463, 32652, 3409, 28353],

[26151, 14598, 12455, 26295, 25763, 26040, 8285, 27502, 15148, 4945],

[26170, 1833, 5196, 9794, 26804, 2831, 11993, 2839, 9979, 27428, 6684],

[4616, 30265, 5752, 32051, 10443, 9240, 8095, 28084, 26285, 8838, 18784, 6547],

[7905, 8373, 19377, 18502, 27928, 13669, 25828, 30502, 28754, 32357, 2843, 5401, 10227],

[22871, 20993, 8558, 10009, 6581, 22716, 12808, 4653, 24593, 21533, 9407, 6840, 30369, 2330],

[3, 28024, 22266, 19327, 18114, 18100, 15644, 21728, 17292, 8396, 27567, 2002, 3830, 12564, 1420],

[29531, 21820, 9954, 8319, 10918, 7978, 24806, 30027, 17659, 8764, 3258, 20719, 6639, 23556, 25786, 11048],

[3544, 31948, 22, 1591, 644, 25981, 26918, 31716, 16427, 15551, 28157, 7107, 27297, 24418, 24384, 32438, 22224],

[12285, 12601, 13235, 21606, 2516, 13095, 27080, 16331, 23295, 20696, 31580, 28758, 10697, 4730, 16055, 22208, 2391, 20143],

[16325, 24537, 16778, 17119, 18198, 28537, 11813, 1490, 21034, 1978, 6451, 2174, 24812, 28772, 5283, 6429, 15484, 29353, 5942],

[7299, 6961, 32019, 24731, 29103, 17887, 17338, 26840, 13216, 8789, 12474, 24299, 19818, 18218, 14564, 31409, 5256, 31930, 26804, 9736]]

all_score = {}

with open('all_roads.txt', 'r')as f:

for line in f.readlines():

row = 0

go = 0

score = s[row][go]

for i in line:

if i == 'L':

row += 1

score += s[row][go]

elif i == 'R':

row += 1

go += 1

score += s[row][go]

all_score[line] = score

max_road = max(all_score, key=all_score.get)

print(max_road, all_score[max_road])

得到了最大路径:RRRRRLLRRRLRLRRRLRL和最大值444740。

在程序输入却还是error

0403b5c674261ed31236da05d23759b5

不知道是哪错了,又看了一遍程序,发现第22行的sub_41114F(Str)对我们输入的数据进行了处理。

跟进去,发现又调用了sub_411900(Str)在跟进去,发现调用了sub_4110A5(nullsub_1, sub_411994 - nullsub_1, 4)。再跟进去。

d02ca693275b8b117c5768a5052634ac

再往里跟。sub_411750(lpAddress, a2, a3 = 4);

4ff6852c78e8d36e88db244c4940a310

结合OD来查看。

前面几行是往内存获取值理解成获取用户输入。因为调用到了内存,所以结合OD来查看。

c788b165e020f0c35b9ba323fb4acd46

因为一个dword占了4字节,所以8字节为第二字符。所以就是偶数位的字符与传入的4进行xor运算。

#!usr/bin/env python

#!coding=utf-8

__author__ = 'zhengjim'

max_road = 'RRRRRLLRRRLRLRRRLRL'

flag = ''

for i, s in enumerate(max_road):

if (i - 1) % 2 == 0:

flag += chr(ord(s) ^ 4)

else:

flag += s

print(flag)

得到flag:RVRVRHLVRVLVLVRVLVL

25d3c99c4bef1f2ff411edabd0315eda

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在执行转换之前请将VBB的mysql数据导出为VBB.mdb<br> <br> 方法如下:<br> <br> VBB的此五张数据表<br> <br> <br> <br> <br> <br> post<br> thread<br> user<br> forum <br> <br> 请导出为"表名.Csv"文本文件到一个temp目录下.导出工具可以用phpMyAdmin 2.5.6来导出.<br> <br> 新建一vbb.mdb空数据库<br> <br> 获取外部数据===>选中 "表名.csv" ,注意默认的列名应该是第一行的数据.分隔符号是逗号.<br> <br> 将此vbb.mdb与转换程序translate.asp,dvbbs72.mdb置入同一个目录下.<br> <br> 上传任一可以运行asp程序的目录下运行translate.asp即可.<br> <br> 如果无法明白,请打开压缩包里的mysql==>mdb导出演示教程,即打开"前提教程.swf"<br> <br> <br> <br> <br> <br> A:程序需求环境:ASP<br> B :vbb.mdb是vbb版本的数据库, .<br> C:转到DVBBS的版本是ACCESS Dvbbs 7.0.0 + SP2<br> D:转换完成后默认的dvbbs7.0.0 的超管帐号:User:admin Pass:admin888<br> E:转换后请用转换程序里所带的md5.asp替换DVBBS7.0.0的md5.asp!<br> <br> <br> <br> <br> 转换程序功能:<br> 1, 转换vbb的用户库,<br> 2, 转换vbb论坛目录,<br> 3, 转换vbb论坛贴子数据<br> 4, 转换成功后所有的问题即归DVBBS7.0.0程序功能管理.<br> <br> 关于商业转换:<br> 商业转换费用:500元/次 <br> 联系商业转换QQ:2664538 请注明商业转换,否则不通过!谢谢.<br> <br> 以下请执行转换程序:<br> <br> [注意顺序绝对不可以乱!否则你转换后的结果仍然是零!一次失败,请换用新数据库重新转换!]<br> <br> 如果转换的时间大于20分钟还在转换,请修改程序适应需要,或者寻求商业转换.<br> <br> 转换速度相当快的.<br> <br> <br> <br> <br> <br> <br> <br> >>>>>| 转换用户库 | <<<<<< <br> <br> >>>>>| 转换论坛目录 | <<<<<< <br> <br> >>>>>| 转换贴子数据 | <<<<<<<br> <br> >>>>>| 更新数据 | <<<<<< (请记得执行本步骤!!否则转换后无法进入管理后台者不要怪我!)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值