BUGKU-逆向(reverse)-writeup

目录 入门逆向 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
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信