目录
入门逆向
Easy_vb
Easy_Re
游戏过关
Timer(阿里CTF)
逆向入门
love
LoopAndLoop(阿里CTF)
easy-100(LCTF)
SafeBox(NJCTF)
Mountain climbing
前言:在bugku上把能写的逆向都写了,由于大佬们的writeup太深奥或者说太简洁了让我(小白)看得云里雾里。所以我写了这个详细点的writeup(理解错的地方望指出),尽量让大家都看得懂。最近比较忙先写到了这里,未完待续
入门逆向
下载后ida打开,双击_mail函数里就有flag
Easy_vb
下载后ida打开,往下翻里就有flag
提交flag出错,将MCTF改成flag即可。
Easy_Re
下载后ida打开,双击_mail函数 ,F5翻译为伪C代码
strcmp()对面输入的值是否等于xmmword_413E34位置的值,双击跟过去,发现了flag
小端存储的问题,看起来反了而已。
游戏过关
下载后ida打开,看到函数比较多,分享一种快速找关键函数的方法。
首先就是看运行遍程序,了解下程序流程以及关键字符串。然后打开ida
1.Shift+F12查看下字符串。
2.然后双击过去。
3.再按Cirt+X交叉引用显示调用位置
然后F5看下伪代码
打印出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。
用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,所以搜索该字符串,双击跟过去。
往上看第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
然后保存,编译,安装运行就出现flag。
flag
逆向入门
下载后发现不是pe文件,右键txt打开,看到data:image/png;base64,iVBORXXXXXX...开头的,为图像文件。
开头添加
![]()
,html打开。有二维码扫描既可。
love
下载来用peid看是C++的,先运行下。
要输入flag。用ida打开
按之前说的方法,快速定位到关键函数
打F5查看伪代码
可以看到有两步加密,第一步是先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加密算法的特征)。
也就是将刚刚得到的值base64解密就是flag。
LoopAndLoop(阿里CTF)
下载文件发现是apk ,先安装运行下发现有一个输入框,随便输入点getyourflag 跳出Not Right
用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层的函数
stringFromJNI2函数也为Native层的函数
加载了System.loadLibrary("lhm");,所以逆向liblhm.so文件。
用IDA打开,还是上面的办法,找到了chec函数
这部分看得比较混乱,查了比较多的资料,所以有不对之处请指出来。
汇编
伪代码
上面是自己加了注释,然后通过看汇编与伪代码分析得出流程,即将传入的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。
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