基础android
直接拖进jeb
package com.example.test.ctf02;import android.content.Intent;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.view.View.OnClickListener;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.Toast;public class MainActivity extends AppCompatActivity { private Button login; private EditText passWord; @Override protected void onCreate (Bundle arg3) { super .onCreate(arg3); this .setContentView(0x7F04001A ); this .passWord = (EditText)this .findViewById(0x7F0B0055 ); this .login = (Button)this .findViewById(0x7F0B0056 ); this .login.setOnClickListener(new View .OnClickListener() { @Override public void onClick (View arg7) { String str = MainActivity.this .passWord.getText().toString(); if (new Check ().checkPassword(str)) { Toast.makeText(MainActivity.this , "Good,Please go on!" , 0 ).show(); Intent intent = new Intent (MainActivity.this , MainActivity2.class); MainActivity.this .startActivity(intent); MainActivity.this .finish(); return ; } Toast.makeText(MainActivity.this , "Failed" , 0 ).show(); } }); } }
package com.example.test.ctf02;public class Check { public boolean checkPassword (String str) { char [] pass = str.toCharArray(); if (pass.length != 12 ) { return false ; } int len = 0 ; while (len < pass.length) { pass[len] = (char )(0x9B - len - pass[len]); if (pass[len] == 0x30 && len < 12 ) { ++len; continue ; } return false ; } return true ; } }
跑一下
for i in range (0 ,12 ): for s in range (1 ,128 ): tmp = 0x9B - i - s if tmp == 0x30 : print (chr (s),end="" )
Android2.0
这里可以看到调用了本地库 native 其中方法是getResult
将apk改名zip然后解压
使用dia打开
可见这里首先创建了几个变量 然后调用了Init 我们先看 Init
int __fastcall Init (int result, char *a2, char *a3, const char *a4, int a5) { int v5; int v6; int v7; if ( a5 < 1 ) { v6 = 0 ; } else { v5 = 0 ; v6 = 0 ; do { v7 = v5 % 3 ; if ( v5 % 3 == 2 ) { a3[v5 / 3u ] = a4[v5]; } else if ( v7 == 1 ) { a2[v5 / 3u ] = a4[v5]; } else if ( !v7 ) { ++v6; *(_BYTE *)(result + v5 / 3u ) = a4[v5]; } ++v5; } while ( a5 != v5 ); } *(_BYTE *)(result + v6) = 0 ; a2[v6] = 0 ; a3[v6] = 0 ; return result; }
也就是说上面的代码就是将 15个字符分组 然后将 a4[0/3/6/9/12] 赋值给了a3 将a4[1/4/7/10/13]赋值给了a3 将a4[2/5/8/11/14]赋值给了result 并返回了result
接着调完Init 又以v5 调用了 First 其中这里的v5就是在init中的result
bool __fastcall First (char *a1) { int i; for ( i = 0 ; i != 4 ; ++i ) a1[i] = (2 * a1[i]) ^ 0x80 ; return strcmp (a1, "LN^dl" ) == 0 ; }
bool __fastcall Java_com_example_test_ctf03_JNI_getResult (int a1, int a2, int a3) { _BOOL4 v3; const char *v4; char *v5; char *v6; char *v7; int i; int j; v3 = 0 ; v4 = (const char *)(*(int (__fastcall **)(int , int , _DWORD))(*(_DWORD *)a1 + 676 ))(a1, a3, 0 ); if ( strlen (v4) == 15 ) { v5 = (char *)malloc (1u ); v6 = (char *)malloc (1u ); v7 = (char *)malloc (1u ); Init(v5, v6, v7, v4, 15 ); if ( !First(v5) ) goto LABEL_6; for ( i = 0 ; i != 4 ; ++i ) v6[i] ^= v5[i]; if ( !strcmp (v6, a5) ) { for ( j = 0 ; j != 4 ; ++j ) v7[j] ^= v6[j]; v3 = strcmp (v7, "AFBo}" ) == 0 ; } else { LABEL_6: v3 = 0 ; } } return v3; }
这里 双击一下a5
我们知道数组是以0结尾 那么这里的 a5就是
r1 = [ord (i) for i in list ("LN^dl" )] r2 = [32 ,53 ,45 ,0x16 ,0x61 ] r3 = [ord (i) for i in list ("AFBo}" )] flag = "" for i in range (5 ): if (i<4 ): r3[i]^=r2[i] r2[i]^=r1[i] r1[i] = (r1[i]^0x80 )//2 flag += chr (r1[i]) flag += chr (r2[i]) flag += chr (r3[i]) print (flag)
APK逆向
逻辑很简单 拿出来执行一下就行
import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;public class Test { public static void main (String[] args) { Test test = new Test (); test.checkSN("Tenshine" ,"1234567890123456789012" ); } private boolean checkSN (String userName, String sn) { if (userName == null ) { return false ; } try { if (userName.length() != 0 && (sn != null && sn.length() == 22 )) { MessageDigest messageDigest0 = MessageDigest.getInstance("MD5" ); messageDigest0.reset(); messageDigest0.update(userName.getBytes()); String s2 = Test.toHexString(messageDigest0.digest(), "" ); StringBuilder sb = new StringBuilder (); int i; for (i = 0 ; i < s2.length(); i += 2 ) { sb.append(((char )s2.charAt(i))); } String z = "flag{" + sb.toString() + "}" ; System.out.println(z); return true ; } } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } return false ; } private static String toHexString (byte [] bytes, String separator) { StringBuilder hexString = new StringBuilder (); int i$; for (i$ = 0 ; i$ < bytes.length; ++i$) { String s1 = Integer.toHexString(bytes[i$] & 0xFF ); if (s1.length() == 1 ) { hexString.append('0' ); } hexString.append(s1).append(separator); } return hexString.toString(); } }
人民的名义-抓捕赵德汉1-200 这里可以看到 调用了 loadCheckerObject 返回了 一个 CheckInterface 对象然后调用了其中的 checkPassword方法
这里首先获取了 /ClassEnc 根目录下的 /ClassEnc 加密文件 接着解密这个文件的内容 并创建一个对象返回 那么我们现在需要去解密一下 /ClassEnc 的内容 其中密钥在第一行给了
import osfrom Crypto.Cipher import AESkey = "bb27630cf264f8567d185008c10c3f96" keybyte= bytes .fromhex(key) aes =AES.new(keybyte,AES.MODE_ECB) data = bytearray (os.path.getsize("ClassEnc" )) with open ("ClassEnc" ,'rb' ) as f : f.readinto(data) f.close() dec = aes.decrypt(data) with open ("dec.class" ,"ba" ) as f: f.write(dec) f.close()
这里可以发现进行了 qeuals
其实这里 在源jar包中还有一个class
这里的内容和我们解密出来的是一样的
monkey99
其实这里利用java去获取会更加简单
下面是修改后的loadCheckerObject方法
private static CheckInterface loadCheckerObject () throws IOException, NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException, ClassFormatError, InstantiationException, IllegalAccessException { CheckPassword mycl = new CheckPassword (); FileInputStream in = new FileInputStream ("src/main/java/ClassEnc" ); ByteArrayOutputStream bout = new ByteArrayOutputStream (); byte [] bytes = new byte [512 ]; while (true ) { int len = in.read(bytes); if (len <= -1 ) { byte [] myClassBytesEnc = bout.toByteArray(); in.close(); SecretKeySpec secretKeySpec = new SecretKeySpec (hexStringToByteArray(hexKey), "AES" ); Cipher decAEScipher = Cipher.getInstance("AES" ); decAEScipher.init(2 , secretKeySpec); byte [] myClassBytes = decAEScipher.doFinal(myClassBytesEnc); FileOutputStream fileOutputStream = new FileOutputStream ("dec.class" ); fileOutputStream.write(myClassBytes); fileOutputStream.close(); return (CheckInterface) mycl.defineClass(null , myClassBytes, 0 , myClassBytes.length).newInstance(); } bout.write(bytes, 0 , len); } }
重新运行下程序就能拿到class类
或者尝试自己去解密下aes
import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.io.*;import java.nio.charset.StandardCharsets;import java.security.Key;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Arrays;import java.util.Base64;public class Test { public static void main (String[] args) throws Exception { String hexKey = "bb27630cf264f8567d185008c10c3f96" ; FileInputStream classEnc = new FileInputStream ("src/main/java/ClassEnc" ); byte [] bytes = classEnc.readAllBytes(); SecretKeySpec keySpec = new SecretKeySpec (hexStringToByteArray(hexKey), "AES" ); Cipher aes1 = Cipher.getInstance("AES" ); aes1.init(2 ,keySpec); byte [] decydata = aes1.doFinal(bytes); FileOutputStream fileOutputStream = new FileOutputStream ("dec.class" ); fileOutputStream.write(decydata); fileOutputStream.close(); } private static byte [] hexStringToByteArray(String s) { int len = s.length(); byte [] data = new byte [len / 2 ]; for (int i = 0 ; i < len; i += 2 ) { data[i / 2 ] = (byte ) ((Character.digit(s.charAt(i), 16 ) << 4 ) + Character.digit(s.charAt(i + 1 ), 16 )); } return data; } }
boomshakalaka-3
可以看到new 了一个alei
在a类的构造方法中SharedPreferences 是android中常见的存储方式 这里就是以 arg2 打开一个文件
因此在 /data/data/packgaename 对应目录下生成对应文件
可以看到以DATA 存储了一串字符串
解码发现有乱码
接着去分析so文件
找到 updateScore
这里应该是按分数追加字符串 接着调用了setStringForKey
然后调用了java层的 setStringForKey
零分的时候的图
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map > <int name ="HighestScore" value ="0" /> <string name ="DATA" > MGN0</string > <boolean name ="isHaveSaveFileXml" value ="true" /> </map >
第一局
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map > <int name ="HighestScore" value ="6000" /> <string name ="DATA" > MGN0ZntDMGNvUzJkX0FuRHJvMWRfRzRVdz99</string > <boolean name ="isHaveSaveFileXml" value ="true" /> </map >
第二局
<?xml version='1.0' encoding='utf-8' standalone='yes' ?> <map > <int name ="HighestScore" value ="6000" /> <string name ="DATA" > MGN0ZntDMGNvUzJkX0FuRHJvMWRfRzRVdz99ZntDMGNvUzJkX0FuRHJvMWRfRzdz99</string > <boolean name ="isHaveSaveFileXml" value ="true" /> </map >
可以发现 MGN0 是刚开始加上的
ZntDMGNvUzJkX0FuRHJvMWRfRzRVdz99 ZntDMGNvUzJkX0FuRHJvMWRfRzRVdz99ZntDMGNvUzJkX0FuRHJvMWRfRzdz99 ZntDMGNvUzJkX0FuRHJvMWRfRzRVdz99ZntDMGNvUzJkX0FuRHJvMWRfRzdz99ZntDMGNvUzJkX0FuRHJvMWdz99ZntDMGNvUzJkX0FuRHJvMWRfRzRVdz99ZntDMGNvUzJkX0FuRHJvMWRfBtdz99
MGN0ZntDMGNvUzJkX0FuRHJv MWRf
这里逐渐发现分数是从MGN0ZntDMGNvUzJkX0FuRHJv 开始添加直到dz99结束
MGN0ZntDMGNvUzJkX0FuRHJvMWRfRzBtRV9Zb1VfS24wdz99
easy-apk
可以看到有个 equals 其中new 了一个新的base64
自定义的字符
a = ['v' , 'w' , 'x' , 'r' , 's' , 't' , 'u' , 'o' , 'p' , 'q' , '3' , '4' , '5' , '6' , '7' , 'A' , 'B' , 'C' , 'D' , 'E' , 'F' , 'G' , 'H' , 'I' , 'J' , 'y' , 'z' , '0' , '1' , '2' , 'P' , 'Q' , 'R' , 'S' , 'T' , 'K' , 'L' , 'M' , 'N' , 'O' , 'Z' , 'a' , 'b' , 'c' , 'd' , 'U' , 'V' , 'W' , 'X' , 'Y' , 'e' , 'f' , 'g' , 'h' , 'i' , 'j' , 'k' , 'l' , 'm' , 'n' , '8' , '9' , '+' , '/' ] for i in a: print (i,end="" )
frida使用 安装 pip install frida pip install frida-tools
这里可以看到安装的版本是 16.0.8
查看手机信息
adb shell getprop ro.product.cpu.abi
下载frida 服务端 https://github.com/frida/frida/releases
adb push .\frida-server-16.0.8-android-x86 /data/local/tmp
adb root adb shell cd /data/local/tmp/ chmod +x frida-server-16.0.8-android-x86 ./frida-server-16.0.8-android-x86
将一个脚本注入到 Android进程
frida -U -l myhook.js com.xxx.xxx -U 指定对use设备操作 -l 指定加载一个javascript脚本 最后指定一个进程名 如果想指定进程pid 使用 -p选项 正在运行的进程用 frida-ps -U查看
frida运行过程中,执行 %resume重新注入 %reload 重新加载脚本 执行exit结束
hook 载入类 java.use方法用于声明一个java类
var StringClass=Java.use("java.lang.String");
函数参数类型表示 对于基本类型直接用java中的表示即可
基本类型数组 用左中括号接上基本类型的缩写
基本类型缩写表示表:
基本类型
缩写
boolean
Z
byte
B
char
C
double
D
float
F
int
I
long
J
short
S
例如int[] 类型要写成[I
任意类 直接写完整类名即可
数组对象 需要加[ 接上完整类名
带参数的构造方法 修改参数为byte[]类型的构造函数的实现
ClassName.$init.overload('[B').implementation=function(param){}
ClassName 是使用java.use定义的类 param是可以在函数体中访问的类
ClassName.$init.overload('[B','int','int').implementation=function(param1,param2,param3){}
调用源构造函数
ClassName.$init.overload().implementation=function(){ this.$init(); }
修改函数名为func,参数为byte[]类型的函数实现
ClassName.func.overload('[B').implementation=function(param){}
调用函数 var ClassName=java.use("com.test.ClassName"); var instance = ClassName.$new(); $new表示一个构造函数 instance.func();
类型转换 var StringClass = Java.use("java.lang.String"); var NewTypeClas=Java.cast(variable,StringClass)
实例场景 import frida, sys jscode = """ if(Java.available){ Java.perform(function(){ var MainActivity = Java.use("com.luoyesiqiu.crackme.MainActivity"); MainActivity.isExcellent.overload("int","int").implementation=function(chinese,math){ console.log("[javascript] isExcellent be called."); send("isExcellent be called."); return this.isExcellent(95,96); } }); } """ def on_message (message, data) : if message['type' ] == 'send' : print(" {0}" .format(message['payload' ])) else : print(message) pass # 查找USB设备并附加到目标进程 session = frida.get_usb_device().attach('com.luoyesiqiu.crackme' ) # 在目标进程里创建脚本 script = session.create_script(jscode) # 注册消息回调 script.on('message' , on_message) print(' Start attach' ) # 加载创建好的javascript脚本 script.load() # 读取系统输入 sys.stdin.read()
app2 这里空校验通过之后初始化了一个 SecondActivity对象
其中调用了 Encryto 去处理数据
这里加载了本地的 so文件
打开后直接搜就能发现 这里有AES密钥 128位 ECB模式
直接拿去解下密
发现不对 这里还有一串
frida ./frida-server-16.0.8-android-x86
尝试hook这个decode函数
先查看需要附加的进程
import sysimport fridaif __name__ == '__main__' : jscode = """ Java.perform(function (){ //Java.perform 用于在jva层执行js代码 var nativePointer= Module.findExportByName("libJNIEncrypt.so","AES_128_ECB_PKCS5Padding_Encrypt"); //用于查找模块中的一个函数 send("native: "+nativePointer); Interceptor.attach(nativePointer,{ //将一个拦截器附加到 函数上 onEnter:function (args) { //这里是在函数调用前执行的 args是函数的参数列表 send(args); //send想frida控制台输出信息 console.log('args[0]',Memory.readByteArray(args[0],20)) //这里使用Memory.readByteArray 读取处理参数的内容 console.log('args[1]',Memory.readByteArray(args[1],20)) }, onLeave:function (retval) { //这里是函数调用结束执行的 retval是函数返回值 send(retval); console.log('output',Memory.readByteArray(retval,30)) } }) }) """ def message (message,data ): if message["type" ] == 'send' : print ("[*] {0}" .format (message['payload' ])) else : print (message) device = frida.get_usb_device() print (device) process = device.attach("app漏洞第二题" ) print (process) script = process.create_script(jscode) script.on("message" , message) script.load() sys.stdin.read()
像这样调用也可以
Java .perform (function ( ) { let ClassName = Java .use ("com.tencent.testvuln.c.Encryto" ); ClassName .doRawData .implementation =function (obj,str ) { let ret = this .doRawData (obj,str); console .log ("obj=>" ,obj); console .log ("st=>" ,str); console .log ("doRawData ret =>" ,ret); return ret; } })
Java .perform (function ( ) { let ClassName = Java .use ("com.tencent.testvuln.c.Encryto" ); ClassName .doRawData .implementation = function (obj, str ) { let decode = Java .use ("com.tencent.testvuln.c.Encryto" ).decode (obj, "9YuQ2dk8CSaCe7DTAmaqAA==" ) console .log ("decode=>" , decode); return ret; } })
easy-so
s=list ('f72c5a36569418a20907b55be5bf95ad' ) for i in range (0 ,len (s)-1 ,2 ): tmp = s[i] s[i] = s[i+1 ] s[i+1 ] = tmp flag= "" .join(s[len (s)>>1 :]+s[0 :len (s)>>1 ]) print ("flag{" +flag+"}" )
app3
下载完成之后发现是 ab结尾的 .ab后缀的文件是android系统的备份文件格式 ,它分为加密和未加密两种类型,.ab文件的前24个字节是类似文件头的东西,如果是加密的,在24个字节中会有aes-256的标志,如果未加密.则在前20个字节中会有none的标识
下载解包软件
https://github.com/nelenkov/android-backup-extractor/releases/download/master-20221109063121-8fdfc5e/abe.jar
"C:\Program Files\Java\jdk-17.0.2\bin\java.exe" -jar D:\Download\abe.jar unpack D:\Download\and.ab and.tar
在onCreate中调用了a()
注意看这里有两个 a类一个是在a包下一个不在
可以看到flag就放在数据库中
这里新建了a 对象 调了两个a() 在调之前调用了 a0.b
import java.security.MessageDigest;public class Test { public static void main (String[] args) { a a0 = new a (); String s = a0.a("Stranger" , "123456" ); String s1 = a0.a(s + a0.b(s, "123456" )); System.out.println(s1.substring(0 , 7 )); } } class b { public static final String a (String s) { int v = 0 ; char [] arr_c = {'0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' }; try { MessageDigest messageDigest0 = MessageDigest.getInstance("MD5" ); messageDigest0.update(s.getBytes()); byte [] arr_b = messageDigest0.digest(); char [] arr_c1 = new char [arr_b.length * 2 ]; int v1 = 0 ; while (v < arr_b.length) { byte b = arr_b[v]; int v2 = v1 + 1 ; arr_c1[v1] = arr_c[b >>> 4 & 15 ]; v1 = v2 + 1 ; arr_c1[v2] = arr_c[b & 15 ]; ++v; } return new String (arr_c1); } catch (Exception exception0) { return null ; } } public static final String b (String s) { int v = 0 ; char [] arr_c = {'0' , '1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' , 'a' , 'b' , 'c' , 'd' , 'e' , 'f' }; try { MessageDigest messageDigest0 = MessageDigest.getInstance("SHA-1" ); messageDigest0.update(s.getBytes()); byte [] arr_b = messageDigest0.digest(); char [] arr_c1 = new char [arr_b.length * 2 ]; int v1 = 0 ; while (v < arr_b.length) { byte b = arr_b[v]; int v2 = v1 + 1 ; arr_c1[v1] = arr_c[b >>> 4 & 15 ]; v1 = v2 + 1 ; arr_c1[v2] = arr_c[b & 15 ]; ++v; } return new String (arr_c1); } catch (Exception exception0) { return null ; } } } class a { private String a; public a () { this .a = "yaphetshan" ; } public String a (String s) { new b (); return b.b(s + this .a); } public String a (String s, String s1) { return s.substring(0 , 4 ) + s1.substring(0 , 4 ); } public String b (String s, String s1) { new b (); return b.a(s); } }
easyjava
简单分析一下这几个类然后 写一下脚本
import java.util.ArrayList;public class Test { public static void main (String[] args) { } private static Boolean b (String s) { int v = 0 ; if (!s.startsWith("flag{" )) { return Boolean.valueOf(false ); } if (!s.endsWith("}" )) { return Boolean.valueOf(false ); } String s1 = s.substring(5 , s.length() - 1 ); b b0 = new b (((int )2 )); a a0 = new a (((int )3 )); StringBuilder stringBuilder0 = new StringBuilder (); int v1 = 0 ; while (v < s1.length()) { stringBuilder0.append(((char )Test.a(((char )s1.charAt(v)) + "" , b0, a0))); Integer integer0 = (int )(((int )b0.b()) / 25 ); if (((int )integer0) > v1 && ((int )integer0) >= 1 ) { ++v1; } ++v; } return Boolean.valueOf(stringBuilder0.toString().equals("wigwrkaugala" )); } private static char a (String s, b b0, a a0) { return a0.a(b0.a(s)); } } class a { public static ArrayList a; static String b; Integer[] c; static Integer d; static { a = new ArrayList (); b = "abcdefghijklmnopqrstuvwxyz" ; d = (int )0 ; } public a (Integer integer0) { this .c = new Integer []{((int ) 7 ), ((int ) 14 ), ((int ) 16 ), ((int ) 21 ), ((int ) 4 ), ((int ) 24 ), ((int ) 25 ), ((int ) 20 ), ((int ) 5 ), ((int ) 15 ), ((int ) 9 ), ((int ) 17 ), ((int ) 6 ), ((int ) 13 ), ((int ) 3 ), ((int ) 18 ), ((int ) 12 ), ((int ) 10 ), ((int ) 19 ), ((int ) 0 ), ((int ) 22 ), ((int ) 2 ), ((int ) 11 ), ((int ) 23 ), ((int ) 1 ), ((int ) 8 )}; int v; for (v = (int ) integer0; v < this .c.length; ++v) { a.add(this .c[v]); } int v1; for (v1 = 0 ; v1 < ((int ) integer0); ++v1) { a.add(this .c[v1]); } } public void a () { d = (int ) (((int ) d) + 1 ); if (((int ) d) == 25 ) { int v = (int ) (((Integer) a.get(0 ))); a.remove(0 ); a.add(Integer.valueOf(v)); d = (int ) 0 ; } } public char a (Integer integer0) { int v = 0 ; Integer integer1 = (int ) 0 ; if (((int ) integer0) == -10 ) { a(); return ' ' ; } while (v < a.size() - 1 ) { if (a.get(v) == integer0) { integer1 = v; } ++v; } a(); return b.charAt(integer1.intValue()); } } class b { public static ArrayList a; static String b; Integer[] c; static Integer d; static { a = new ArrayList (); b = "abcdefghijklmnopqrstuvwxyz" ; d = (int )0 ; } public b (Integer integer0) { this .c = new Integer []{((int ) 8 ), ((int ) 25 ), ((int ) 17 ), ((int ) 23 ), ((int ) 7 ), ((int ) 22 ), ((int ) 1 ), ((int ) 16 ), ((int ) 6 ), ((int ) 9 ), ((int ) 21 ), ((int ) 0 ), ((int ) 15 ), ((int ) 5 ), ((int ) 10 ), ((int ) 18 ), ((int ) 2 ), ((int ) 24 ), ((int ) 4 ), ((int ) 11 ), ((int ) 3 ), ((int ) 14 ), ((int ) 19 ), ((int ) 12 ), ((int ) 20 ), ((int ) 13 )}; int v; for (v = (int ) integer0; v < this .c.length; ++v) { a.add(this .c[v]); } int v1; for (v1 = 0 ; v1 < ((int ) integer0); ++v1) { a.add(this .c[v1]); } } public void a () { int v = (int ) (((Integer) a.get(0 ))); a.remove(0 ); a.add(Integer.valueOf(v)); b = b + "" + ((char ) b.charAt(0 )); b = b.substring(1 , 27 ); d = (int ) (((int ) d) + 1 ); } public Integer a (String s) { int v = 0 ; Integer integer0 = (int ) 0 ; if (b.contains(s.toLowerCase())) { Integer integer1 = (int ) b.indexOf(s); while (v < a.size() - 1 ) { if (a.get(v) == integer1) { integer0 = v; } ++v; } } else { integer0 = s.contains(" " ) ? ((int ) -10 ) : ((int ) -1 ); } a(); return integer0; } public Integer b () { return d; } }
a=[17 , 23 , 7 , 22 , 1 , 16 , 6 , 9 , 21 , 0 , 15 , 5 , 10 , 18 , 2 , 24 , 4 , 11 , 3 , 14 , 19 , 12 , 20 , 13 , 8 , 25 ] b= [21 , 4 , 24 , 25 , 20 , 5 , 15 , 9 , 17 , 6 , 13 , 3 , 18 , 12 , 10 , 19 , 0 , 22 , 2 , 11 , 23 , 1 , 8 , 7 , 14 , 16 ] s='abcdefghijklmnopqrstuvwxyz' c='wigwrkaugala' re=[] flag='' for i in c: re.append(b[s.index(i)]) print (re) flag='' for i in re: d=a[i] flag+=s[d] a.append(a[0 ]) a.remove(a[0 ]) s+=s[0 ] s=s[1 :] print (flag)
Ph0en1x-100 需要让我们输入的字符加密后等于getFlag()
先拿getFlag()
import sysimport fridaif __name__ == '__main__' : def message (message,data ): if message["type" ] == 'send' : print ("[*] {0}" .format (message['payload' ])) else : print (message) jscode=""" Java.perform(function () { let ClassName = Java.use("com.ph0en1x.android_crackme.MainActivity"); // 这里先获取了源类 ek`fz@q2^x/t^fn0mF^6/^rb`qanqntfg^E`hq| ClassName.getFlag.implementation = function (obj,str) { //调用 implementation 修改了 doRawData函数的实现 let flag = this.getFlag(); console.log("flag=>", flag); return flag; } }) """ device = frida.get_usb_device() print (device) process = device.attach("Android_crackme" ) print (process) script = process.create_script(jscode) script.on("message" , message) script.load() sys.stdin.read()
加密方法在so 发现就是将字符 –
s='ek`fz@q2^x/t^fn0mF^6/^rb`qanqntfg^E`hq|' for i in s: print (chr (ord (i)+1 ),end="" )
黑客精神 简单读一下发现了这个的native方法
但是ida反编译之后并没有发现对应的方法
因此发现是动态注册的 找到JNI_onload
可以看到三个一一对应
n1 initSN
这里可以看到 如果 /sdcard/reg.dat 的内容存在且为 EoPAoY62@ElRD 就将 java对象和1 传入setValue 否则传入 java对象和0
setValue a1 是传入的java对象 a2是传入的值 v4代表取com/gdufs/xman/MyApp这个类 v5是取v4类中的m属性 也就是将 com/gdufs/xman/MyApp 类的静态属性m设置为1或者0
这里简单逆一下 n2
s = "W3_arE_whO_we_ARE" s2 = "EoPAoY62@ElRD" flag="" v9 = 2016 for i in range(len(s2)): if i%3 == 1 : v9 = (v9 + 5 )%16 v11 = s[v9+1 ] elif i%3 == 2 : v9 = (v9 + 7 )%15 v11 = s[v9+2 ] else : v9 = (v9 + 3 ) % 13 v11 = s[v9 + 3 ] flag += chr(ord(s2[i]) ^ ord(v11)) print(flag)
n3 的内容
别人写的frida脚本 做下记录 我没有跑成功
var fputs_str = null ;function hook_so_libc_fputs ( ){ var libc_addr = Module .findBaseAddress ("libc.so" ); var fputs_addr = Module .findExportByName ("libc.so" , "fputs" ); console .log ("[libc.so] hooked fputs, fputs_addr=" +fputs_addr); Interceptor .attach (fputs_addr,{ onEnter : function (args ){ fputs_str = args[0 ].readCString (); }, onLeave : function (retval ){ } }); } function algorithm_cracking ( ){ Java .perform (function ( ){ var MyApp = Java .use ("com.gdufs.xman.MyApp" ); var MyApp _instance = MyApp .$new(); var dict = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()" ; var ciphertext = "EoPAoY62@ElRD" ; var ciphertext_array = new Array ("EoP" , "AoY" , "62@" , "ElR" ); var plaintext = "" ; var flag = 0 ; var t,i,j,k,g; var input; for (t = 0 ; t < ciphertext_array.length ; t++) { flag =0 ; for (i = 0 ; i < dict.length ; i++) { if (flag == 1 ) break ; for (j = 0 ; j < dict.length ; j++) { if (flag == 1 ) break ; for (k = 0 ; k < dict.length ; k++) { input = "" + dict[i] + dict[j] + dict[k]; MyApp _instance.saveSN (Java .use ('java.lang.String' ).$new(input)); if (fputs_str == ciphertext_array[t]) { console .log ("input=" + input + "--output=" + fputs_str); plaintext = plaintext + input; console .log ("plaintext=" +plaintext); flag = 1 ; break ; } } } } } for (g = 0 ; g < dict.length ; g++) { input = "" + dict[g]; MyApp _instance.saveSN (Java .use ('java.lang.String' ).$new(input)); if (fputs_str == "D" ){ plaintext =plaintext + input; break ; } } console .log ("plaintext=" +plaintext); console .log ("algorithm_cracking finished" ); }); } setImmediate (hook_so_libc_fputs);setTimeout (algorithm_cracking,3 *1000 );