基础android

image-20230103195708866

直接拖进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  // android.support.v7.app.AppCompatActivity
    protected void onCreate(Bundle arg3) {
        super.onCreate(arg3);
        this.setContentView(0x7F04001A);  // layout:acticity_main_1
        this.passWord = (EditText)this.findViewById(0x7F0B0055);  // id:passWord
        this.login = (Button)this.findViewById(0x7F0B0056);  // id:button
        this.login.setOnClickListener(new View.OnClickListener() {
            @Override  // android.view.View$OnClickListener
            public void onClick(View arg7) {
                String str = MainActivity.this.passWord.getText().toString();
                if(new Check().checkPassword(str)) {
                    //这里调了 Check() 的checkPassword
                    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="")

image-20230103201937799

image-20230103201943910

image-20230103202804837

Android2.0

image-20230103210009094

image-20230103210021262

这里可以看到调用了本地库 native 其中方法是getResult

将apk改名zip然后解压

image-20230103210054746

使用dia打开

image-20230103210321662

可见这里首先创建了几个变量 然后调用了Init 我们先看 Init

int __fastcall Init(int result, char *a2, char *a3, const char *a4, int a5)
{
// 123456
    int v5; // r5
    int v6; // r10
    int v7; // r6

    if ( a5 < 1 )
    {
        v6 = 0;
    }
    else
    {
        v5 = 0;
        v6 = 0;
        do
        {
            v7 = v5 % 3;
            if ( v5 % 3 == 2 )
            {
                a3[v5 / 3u] = a4[v5];
//        a3[0] = 1
            }
            else if ( v7 == 1 )
            {
                a2[v5 / 3u] = a4[v5];
//      a2[0] = 2
            }
            else if ( !v7 )
            {
                ++v6;
                *(_BYTE *)(result + v5 / 3u) = a4[v5];
//                这里是将result中的值赋值为 a4[3 ] 其实这里是 ida 的问题 代码应该是和上面一样的 
            }
            ++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; // r1

  for ( i = 0; i != 4; ++i )
    a1[i] = (2 * a1[i]) ^ 0x80;
  return strcmp(a1, "LN^dl") == 0;
    //比较字符串 a1 和 LN^dl 的大小 这里需要返回真所以 a1要和LN^dl相等
}
bool __fastcall Java_com_example_test_ctf03_JNI_getResult(int a1, int a2, int a3)
{
    _BOOL4 v3; // r4
    const char *v4; // r8
    char *v5; // r6
    char *v6; // r4
    char *v7; // r5
    int i; // r0
    int j; // r0

    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) )
//            First(v5)肯定是要返回真的
            goto LABEL_6;
        for ( i = 0; i != 4; ++i )
            v6[i] ^= v5[i];
//        v6与v5异或
        if ( !strcmp(v6, a5) )
//        如果v6等于a5 这里a5其实是未知的
        {
            for ( j = 0; j != 4; ++j )
                v7[j] ^= v6[j];
//            这里是v7和v6异或
            v3 = strcmp(v7, "AFBo}") == 0;
//            如果v7和AFBo} 相等就返回真
        }
        else
        {
            LABEL_6:
            v3 = 0;
        }
    }
    return v3;
}

这里 双击一下a5

image-20230104000156127

image-20230104000302052

我们知道数组是以0结尾 那么这里的 a5就是

 5-0x16x61
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)

image-20230104012701579

APK逆向

image-20230104134629309

逻辑很简单 拿出来执行一下就行

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();
    }
}

image-20230104134659794

人民的名义-抓捕赵德汉1-200

这里可以看到 调用了 loadCheckerObject 返回了 一个 CheckInterface 对象然后调用了其中的 checkPassword方法

image-20230104143507313

image-20230104143909856

这里首先获取了 /ClassEnc 根目录下的 /ClassEnc 加密文件 接着解密这个文件的内容 并创建一个对象返回 那么我们现在需要去解密一下 /ClassEnc 的内容 其中密钥在第一行给了

import os

from Crypto.Cipher import AES
key = "bb27630cf264f8567d185008c10c3f96"
keybyte= bytes.fromhex(key)
aes =AES.new(keybyte,AES.MODE_ECB)
# 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()

image-20230104145238999

image-20230104145319460

这里可以发现进行了 qeuals

image-20230104145350327

其实这里 在源jar包中还有一个class

image-20230104145426760

这里的内容和我们解密出来的是一样的

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类

image-20230104153106002

或者尝试自己去解密下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

image-20230104161222839

可以看到new 了一个alei

image-20230104161357336

在a类的构造方法中SharedPreferences 是android中常见的存储方式 这里就是以 arg2 打开一个文件

image-20230104161523151

因此在 /data/data/packgaename 对应目录下生成对应文件

可以看到以DATA 存储了一串字符串

image-20230104162043289

解码发现有乱码

image-20230104162051077

接着去分析so文件

image-20230104162413195

找到 updateScore

image-20230104162509469

这里应该是按分数追加字符串 接着调用了setStringForKey

image-20230104162601669

然后调用了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结束

image-20230104164512284

MGN0ZntDMGNvUzJkX0FuRHJvMWRfRzBtRV9Zb1VfS24wdz99

image-20230104164609061

easy-apk

image-20230104175913323

可以看到有个 equals 其中new 了一个新的base64

自定义的字符

image-20230104175946291

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="")

image-20230104180011920

image-20230104175849427

frida使用

安装

 pip install frida
 pip install frida-tools

image-20230104233728431

这里可以看到安装的版本是 16.0.8

查看手机信息

adb shell getprop ro.product.cpu.abi

image-20230104234009173

下载frida 服务端 https://github.com/frida/frida/releases

image-20230104234137381

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

image-20230104234951330

frida-ps -U

image-20230104235609960

将一个脚本注入到 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中的表示即可

基本类型数组 用左中括号接上基本类型的缩写

基本类型缩写表示表:

基本类型缩写
booleanZ
byteB
charC
doubleD
floatF
intI
longJ
shortS

例如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对象

image-20230104202713350

image-20230104202751263

其中调用了 Encryto 去处理数据

image-20230104202816817

这里加载了本地的 so文件

image-20230104202854920

打开后直接搜就能发现 这里有AES密钥 128位 ECB模式

直接拿去解下密

image-20230104202943584

发现不对 这里还有一串

image-20230104203838254

image-20230104202457359

frida

./frida-server-16.0.8-android-x86

image-20230107212203803

尝试hook这个decode函数

image-20230105165203368

先查看需要附加的进程

frida-ps -U

image-20230105192719805

import sys

import frida

if __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):
        # 用于接收frida的信息
        if message["type"] == 'send':
            # 如果类型等于 send
            print("[*] {0}".format(message['payload']))
        else:
            print(message)


    device = frida.get_usb_device()
    # 获取当前的usb设备
    print(device)
    process = device.attach("app漏洞第二题")
    # 附加到相关进程
    print(process)
    script = process.create_script(jscode)
    # 进程所需要附加的脚本

    script.on("message", message)
    # 将message函数与脚本内容绑定
    script.load()
    # 加载脚本
    sys.stdin.read()
    # 执行完停止等待用户输入

image-20230105192703689

像这样调用也可以

Java.perform(function () {
    let ClassName = Java.use("com.tencent.testvuln.c.Encryto");
//    这里先获取了源类
    ClassName.doRawData.implementation=function (obj,str) {
        //调用  implementation 修改了 doRawData函数的实现
        let ret = this.doRawData(obj,str);
        console.log("obj=>",obj);
        console.log("st=>",str);
        console.log("doRawData ret =>",ret);
    //    先调用了源函数
        return ret;
    }
})

image-20230105195344149

Java.perform(function () {
    let ClassName = Java.use("com.tencent.testvuln.c.Encryto");
//    这里先获取了源类
    ClassName.doRawData.implementation = function (obj, str) {
        //调用  implementation 修改了 doRawData函数的实现
        let decode = Java.use("com.tencent.testvuln.c.Encryto").decode(obj, "9YuQ2dk8CSaCe7DTAmaqAA==")
        console.log("decode=>", decode);
        return ret;
    }
})

image-20230105195748031

easy-so

image-20230105202851870

image-20230105224041162

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+"}")

image-20230105224030289

app3

image-20230107153550332

下载完成之后发现是 ab结尾的 .ab后缀的文件是android系统的备份文件格式 ,它分为加密和未加密两种类型,.ab文件的前24个字节是类似文件头的东西,如果是加密的,在24个字节中会有aes-256的标志,如果未加密.则在前20个字节中会有none的标识

image-20230107154500153

下载解包软件

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

image-20230107154838996

image-20230107155110505

在onCreate中调用了a()

image-20230107161152537

注意看这里有两个 a类一个是在a包下一个不在

image-20230107161301068

可以看到flag就放在数据库中

image-20230107161352021

这里新建了a 对象 调了两个a() 在调之前调用了 a0.b

image-20230107161722055

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);
    }
}


image-20230107164139958

image-20230107164806955

image-20230107164832964

image-20230107164851732

image-20230107164917965

easyjava

image-20230107200351999

简单分析一下这几个类然后 写一下脚本

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()方法序列中的字符
    a.append(a[0])
    a.remove(a[0])
    s+=s[0]      #对应a类中的a()那段
    s=s[1:]
print(flag)


image-20230107203402754

Ph0en1x-100

需要让我们输入的字符加密后等于getFlag()

image-20230107214613967

先拿getFlag()

image-20230107214812261

import sys

import frida

if __name__ == '__main__':


    def message(message,data):
        # 用于接收frida的信息
        if message["type"] == 'send':
            # 如果类型等于 send
            print("[*] {0}".format(message['payload']))
        else:
            print(message)


    # with open("1.js", 'r', encoding="utf-8") as f:
    #     jscode = f.read()
    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()
    # 获取当前的usb设备
    print(device)
    process = device.attach("Android_crackme")
    # 附加到相关进程
    print(process)
    script = process.create_script(jscode)
    # 进程所需要附加的脚本

    script.on("message", message)
    # 将message函数与脚本内容绑定
    script.load()
    # 加载脚本
    sys.stdin.read()
    # 执行完停止等待用户输入

image-20230107214758982

image-20230107214833685

加密方法在so 发现就是将字符 –

image-20230107214852637

s='ek`fz@q2^x/t^fn0mF^6/^rb`qanqntfg^E`hq|'
for i in s:
    print(chr(ord(i)+1),end="")

image-20230107214921433

黑客精神

简单读一下发现了这个的native方法

image-20230107235816006

但是ida反编译之后并没有发现对应的方法

image-20230107235857775

因此发现是动态注册的 找到JNI_onload

image-20230107235930299

可以看到三个一一对应

image-20230108000041391

n1 initSN

image-20230108002925052

这里可以看到 如果 /sdcard/reg.dat 的内容存在且为 EoPAoY62@ElRD 就将 java对象和1 传入setValue 否则传入 java对象和0

image-20230108001230505

setValue a1 是传入的java对象 a2是传入的值 v4代表取com/gdufs/xman/MyApp这个类 v5是取v4类中的m属性 也就是将 com/gdufs/xman/MyApp 类的静态属性m设置为1或者0

image-20230108001455596

image-20230108005438737

这里简单逆一下 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)

image-20230108005508288

n3 的内容

image-20230108011041690

别人写的frida脚本 做下记录 我没有跑成功

//frida -U -f com.gdufs.xman -l demo.js --no-pause
 
//int fputs(const char *str, FILE *stream);
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();
            //console.log("[fputs] str="+fputs_str);
        },
        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];
                        //console.log(temp);
                        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);
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐