QAQ~QL 发表于 2024-2-28 16:33

【2024春节】解题领红包题解 Android3~5题

本帖最后由 QAQ~QL 于 2024-2-28 17:07 编辑

# ***【2024春节】解题领红包题解3-Android初级题***

## GDA分析



一眼webview js交互

打开apk



经典404页面,盲猜成功后返回flag

偷偷看一眼`ys.mp4`



典!!!

## 题解

既然是webview交互js,还是个抓小猫游戏,那肯定是赢了出结果

那就看`JavaScriptInterface`实现在哪

```java
this.webView.addJavascriptInterface(new MyJavaScriptInterface(this), "AndroidInterface");


public class MyJavaScriptInterface
{
    private Context mContext;

    public void MyJavaScriptInterface(Context p0){
       super();
       this.mContext = p0;
    }
    public void onSolverReturnValue(int p0){
       if (p0 == -1) {
          this.mContext.startActivity(new Intent(this.mContext, YSQDActivity.class));
       }
       return;
    }
}
```

启动了新的界面`YSQDActivity`

```java
protected void onCreate(Bundle p0){
   super.onCreate(p0);
   this.setContentView(R$layout.activity_ysqdactivity);
   this.setRequestedOrientation(0);
   this.tv = this.findViewById(R$id.textView);
   this.playVideo(this.filePath);
}

public void playVideo(String p0){
   this.getWindow().setFlags(1024, 1024);
   if (this.getSupportActionBar() != null) {
      this.getSupportActionBar().hide();
   }
   VideoView videoView = this.findViewById(R$id.videoView);
   videoView.setVideoURI(Uri.parse(p0));
   videoView.setMediaController(new MediaController(this));
   videoView.setOnPreparedListener(new YSQDActivity$1(this));
   videoView.setOnCompletionListener(new YSQDActivity$2(this));//结束动作
   videoView.start();
   return;
}
```

`OnCompletionListener => onCompletion`

```java
public void onCompletion(MediaPlayer p0){
   YSQDActivity.access$000(this.this$0).setText(YSQDActivity.extractDataFromFile(this.this$0.filePath));
}

public static String extractDataFromFile(String p0){
   int i;
   try{
      RandomAccessFile randomAccess = new RandomAccessFile(p0, "r");
      long l = randomAccess.length();
      p0 = "flag{";
      long l1 = Math.max((l - (long)30), 0);
      while (true) {
         if ((l1 - l) < 0) {
            randomAccess.seek(l1);
            byte[] uobyteArray = new byte;
            randomAccess.read(uobyteArray);
            String str = new String(uobyteArray, StandardCharsets.UTF_8);
            if ((i = str.indexOf(p0)) != -1) {
               randomAccess.close();
               return str.substring(i).split("\\}")+"}";
            }else {
               l1 = l1 + 1;
            }
         }else {
            randomAccess.close();
            break ;
         }
      }
   }catch(java.lang.Exception e9){
      e9.printStackTrace();
   }
   return null;
}

```

废话不多说,直接启动Activity

```shell
adb shell su -c am start-activity -n com.zj.wuaipojie2024_1/.YSQDActivity
```

或`frida`主动调用

```JS
function checkClass(targetClass){
      try{
                Java.use(targetClass);
      }catch(error){
                Java.enumerateClassLoaders({onMatch: function (loader) {
                try{
                        if(loader.findClass(targetClass)) {
                              Java.classFactory.loader = loader;
                        }
                }catch(error){
                        console.log('classloader failed' + error);
                }},onComplete: function () {}
      });}
}
Java.perform(function() {
      var targetClass=decodeURIComponent('com.zj.wuaipojie2024%5f1.YSQDActivity');
      checkClass(targetClass);
      var gclass = Java.use(targetClass);
      var ret=gclass['extractDataFromFile']("/data/user/0/com.zj.wuaipojie2024_1/files/ys.mp4");
      console.log(ret);
})

//flag{happy_new_year_2024}
```



# ***【2024春节】解题领红包题解4-Android初级题***

## 原!妙不可言





抽卡小游戏,盲猜出金(flag)



**这不直接改倍率?????**

## 题解

先查看文本





`WishActivity`祈愿界面,点击事件

```java
package com.kbtx.redpack_simple.WishActivity;
import a.b.c.h;
import java.util.Timer;
import b.b.a.b;
import android.os.Bundle;
import com.kbtx.redpack_simple.WishActivity$a;
import java.util.TimerTask;
import android.view.View;
import b.b.a.a;
import android.view.View$OnClickListener;

public class WishActivity extends h      // class@0003cb from classes.dex
{
    public int[] o;
    public int[] p;
    public Timer q;
    public Runnable r;

    public void WishActivity(){
       super();
       this.o = new int{10,0,0};
       this.p = new int{1,2,4,8,16,32,64,128};
       this.q = new Timer();
       this.r = new b(this);
    }
    public void onCreate(Bundle p0){
       super.onCreate(p0);
       this.setContentView(R.layout.activity_wish);
       //计时器
       this.q.schedule(new WishActivity$a(this), 1000, 1000);
       //点击事件
       this.findViewById(R.id.btn_action).setOnClickListener(new a(this));
    }
}
```

`b.b.a.a`

```java
public final void run() {
    WishActivity wishActivity = this.b;
    TextView textView = (TextView) wishActivity.findViewById(0x7f0700f1);
    int[] iArr = wishActivity.o;
    if (iArr > 0) {
      iArr = iArr - 1;
    } else {
      if (iArr < 10) {
            iArr = iArr + 1;
      }
      wishActivity.o = wishActivity.p + iArr) - 10, wishActivity.p.length - 1)];
    }
    int[] iArr2 = wishActivity.o;
    textView.setText(iArr2 < 10 ? String.format(Locale.SIMPLIFIED_CHINESE, "当前已完成%d次祈愿,拥有%d个纠缠之缘\n%d秒后将为你补充一个", Integer.valueOf(iArr2), Integer.valueOf(wishActivity.o), Integer.valueOf(wishActivity.o)) : String.format(Locale.SIMPLIFIED_CHINESE, "当前已完成%d次祈愿,当前拥有%d个纠缠之缘\n纠缠之缘已满,%d秒后将溢出一个,请尽快使用!", Integer.valueOf(iArr2), Integer.valueOf(wishActivity.o), Integer.valueOf(wishActivity.o)));
}
```

`b.b.a.a`

```java
public final void onClick(View view) {
    String str;
    WishActivity wishActivity = this.b;
    if (wishActivity.o < 10) {
      str = "纠缠之缘不足,无法进行祈愿";
    } else {
      for (int i = 0; i < 10; i++) {
            int[] iArr = wishActivity.o;
            iArr = iArr - 1;
            iArr = iArr + 1;
            double random = Math.random();
            int[] iArr2 = wishActivity.o;
            if (random < (iArr2 <= 80 ? 0.006d : (iArr2 - 80) * 0.1d)) {
                Toast.makeText((Context) wishActivity, (CharSequence) "恭喜你十连出金了,奖品为 flag 提示!", 1).show();
                wishActivity.startActivity(new Intent((Context) wishActivity, (Class<?>) FlagActivity.class));
                return;
            }
      }
      str = "哎呀呀,(又)没抽中,一会再试试吧";
    }
    Toast.makeText((Context) wishActivity, (CharSequence) str, 0).show();
}
```

`FlagActivity`

嘿嘿,还有`signature`签名校验

```java
public class FlagActivity extends h {
    public static byte[] o = {86, -18, 98, 103, 75, -73, 51, -104, 104, 94, 73, 81, 125, 118, 112, 100, -29, 63, -33, -110, 108, 115, 51, 59, 55, 52, 77};

    /* JADX WARN: Multi-variable type inference failed */
    public void onCreate(Bundle bundle) {
      byte[] bArr;
      Signature[] signatureArr;
      super.onCreate(bundle);
      setContentView(0x7f0a001c);
      byte[] bArr2 = o;
      try {
            signatureArr = getPackageManager().getPackageInfo(getPackageName(), 64).signatures;
      } catch (PackageManager.NameNotFoundException unused) {
            bArr = new byte;
      }
      if (signatureArr != null && signatureArr.length >= 1) {
            byte[] byteArray = signatureArr.toByteArray();
            ByteBuffer allocate = ByteBuffer.allocate(bArr2.length);
            for (int i = 0; i < bArr2.length; i++) {
                allocate.put((byte) (bArr2 ^ byteArray));
            }
            bArr = allocate.array();
            StringBuilder d = a.d("for honest players only: \n");
            d.append(new String(bArr));
            ((TextView) findViewById(0x7f0700f0)).setText(d.toString());
      }
      bArr = new byte;
      StringBuilder d2 = a.d("for honest players only: \n");
      d2.append(new String(bArr));
      ((TextView) findViewById(0x7f0700f0)).setText(d2.toString());
    }
}
```

### 关联分析

WishActivity.o=[ 纠缠之缘个数, 已祈愿次数, 倒计时 ]

### 解题思路很宽泛

- 原神,启动!!!(~bushi)

直接启动FlagActivity

```bash
adb shell am start-activity -n com.kbtx.redpack_simple/.FlagActivity
```

- 炸! 直接拉满抽奖次数

修改o数组初始值

```java
// this.o = new int{10,0,0};
this.o = new int{999999,0,0};

//Smali
new-array v0, v0, [I
fill-array-data v0, :array_24
iput-object v0, p0, Lcom/kbtx/redpack_simple/WishActivity;->o:[I
      
:array_24
.array-data 4
      0x3b9ac9ff
      0x0
      0x0
.end array-data

```



- 我要暗改倍率!!!!

0.006d???不存在的,我要1.0

```java
// if (random < (iArr2 <= 80 ? 0.006d : (iArr2 - 80) * 0.1d))
if (random < (iArr2 <= 80 ? 1.0d : (iArr2 - 80) * 1.0d))
      
      
//Smali
const-wide v6, 0x3f789374bc6a7efaL# 0.006
const-wide v8, 0x3fb999999999999aL# 0.1

const-wide v6, 0x3FF0000000000000L# 1.0
const-wide v8, 0x3FF0000000000000L# 1.0
```

- 硬算

```java
byte[] bArr2 = {86, -18, 98, 103, 75, -73, 51, -104, 104, 94, 73, 81, 125, 118, 112, 100, -29, 63, -33, -110, 108, 115, 51, 59, 55, 52, 77};
String a = "30820300308201e8020101300d06092a864886f70d01010b050030463110300e06035504030c076b6274787765723110300e060355040b0c073532706f6a69653110300e060355040a0c073532706f6a6965310e300c06035504070c054368696e61301e170d3234303131363036333332335a170d3439303130393036333332335a30463110300e06035504030c076b6274787765723110300e060355040b0c073532706f6a69653110300e060355040a0c073532706f6a6965310e300c06035504070c054368696e6130820122300d06092a864886f70d01010105000382010f003082010a0282010100804804135f57c1539809a9f6291dd0da83c5f9fc57caf199a48cfe37b9a0cc46a1052132a6344e309a31b80ee715dffcbd207f84b07e620c8bc3232b093050473f829ecb74a545ee8fd429feae8480e4284c35bc69dc43d3c130ebf7b0a16c6d10857613c224202ae77126fcfdc9642144158287fe7a196963bc52a47bb942f0f75eb059236bfa64c52718c73547a4c362f1174ec642b98a31a4d7cbe8e991dfa27db70035d451c879b5def12dbb7db19fd5ab211110256c4156cd0be73d22d2c0603144dea18e657a12c0c0146ba5c5eba6b973a227b815fc8efed4c4a2754771854bd3c044686ecbe6505a540e7afaa0999339410b5a9feb826407452c288b0203010001300d06092a864886f70d01010b050003820101005e1024ed95992cd8d88334eaf360fd5699ed7d2333634dded65e9cda6222bb5ae68189db6afb5898ca9f3437959db2e50ce6d19fc3b982ede7e9ed5dd92488ded66928ad5939fdf318b17b4a95f0205bb6da27a3de4f7b69700d94a25e80767d256b630014e254648b2a37d689959ef47293a772c1509db31b0dded377964fe93366d8ef808e207d1b238bbbd51b6a1d38330e0c29bee1afdd0674e5003b4cd2555d25a0cf523f11791d2a97ff7d13235432fef0b44fb7a34c690ab51de4d54ba7876812eb1f6919d2bd2df97c37e9e927ce0d0f41a84f533e010fc8e1a7fa203c1c7c3e5d0873b2b9fc6cd74d9e538132c6d725c305bc5f760b0fb87101a8a2";//原始签名(可通过frida hook)
byte[] byteArray = new BigInteger(a, 16).toByteArray();
ByteBuffer allocate = ByteBuffer.allocate(bArr2.length);
for (int i = 0; i < bArr2.length; i++) {
      allocate.put((byte) (bArr2 ^ byteArray));
}
String flag = new String(allocate.array());
System.out.println(flag);
```

`frida脚本`

```js
function bytesToHex(arr) {
          var str = "";
          var _split = "";
          var k, j;
          for (var i = 0; i < arr.length; i++) {
                  k = arr;
                  j = k;
                  if (k < 0) {
                        j = k + 256;
                  }
                  if (j < 16) {
                        str += "0";
                  }
                  str += j.toString(16) + _split;
          }
          return str;
}

Java.perform(function () {
          var Signature = Java.use("android.content.pm.Signature");
          Signature.toByteArray.implementation = function () {
                  console.log("toByteArray", bytesToHex(this.toByteArray()));
                  return this.toByteArray();
          };
});
```





# ***【2024春节】解题领红包题解5-Android中级题***

## 分析



GestureUnlock密码界面,isError(密码)做验证回调



DexClassLoader加载了一个外置dex来判断和解密文本





不像是正常的东西,`getStaticMethod`方法中还有对dex的修复

```java
Class[] uClassArray = new Class[]{Context.class,String.class,int[].class};
Object[] objArray = new Object[]{this,p0,this.getResources().getIntArray(R$array.A_offset)};// this,密码,数据
isValidate(Context p0,String p1,int[] p2)


C.getStaticMethod(p0, p2, "com.zj.wuaipojie2024_2.A", "d", uClassArray).invoke(null, objArray)
private static Method getStaticMethod(Context p0,int[] p1,String p2,String p3,Class[] p4){
   String str = null;
   try{
      File uFile = C.fix(C.read(p0), p1, p1, p1, p0);//修复dex
      File dir = p0.getDir("fixed", 0);
      uFile.delete();
      new File(dir, uFile.getName()).delete();
      return new DexClassLoader(uFile.getAbsolutePath(), dir.getAbsolutePath(), str, p0.getClass().getClassLoader()).loadClass(p2).getDeclaredMethod(p3, p4);
   }catch(java.lang.Exception e6){
      e6.printStackTrace();
      return str;
   }
}
private static ByteBuffer read(Context p0){
   ByteBuffer uByteBuffer = null;
   try{
      File uFile = new File(p0.getDir("data", 0), "decode.dex");
      if (!uFile.exists()) {
         return uByteBuffer;
      }
      FileInputStream uFileInputSt = new FileInputStream(uFile);
      byte[] uobyteArray = new byte;
      uFileInputSt.read(uobyteArray);
      uFileInputSt.close();
      return ByteBuffer.wrap(uobyteArray);
   }catch(java.lang.Exception e0){
      return e0;
   }
}
```

看来还是得看解密后的dex

## 题解

### 1. 分析进入的checkPassword函数





hook `checkPassword`,看看入参是啥

```js
setImmediate(function () {
    Java.perform(function () {
      var targetClass = decodeURIComponent("com.zj.wuaipojie2024%5f2.MainActivity");
      var methodName = "checkPassword";
      var gclass = Java.use(targetClass);
      gclass.overload("java.lang.String").implementation = function (arg0) {
            console.log("checkPassword(java.lang.String)" + "\n\targ0 = " + arg0);
            var i = this(arg0);
            console.log("return " + i);
            return i;
      };
    });
});
```

```shell
checkPassword(java.lang.String)
      arg0 = 012345678
return false


# 即九宫格
# 0 1 2
# 3 4 5
# 6 7 8
```

此处应有暴力解!!!!!

### 2. 分析解密函数getStaticMethod

重新审视`getStaticMethod`方法,发现fix仅与传入的 int[] p2有关

即`this.getResources().getIntArray(R$array.A_offset)`





```java
int[] p2 =
```

要不,修好的文件直接拦截删除?

但是!!!!这dex载不进去啊



索性直接JEB导出gradle项目,自己解看看了

### 3. 手动构建解密

先MT修复dex,使用JEB打开,导出`com.zj.wuaipojie2024_2.*`的java

新建一个Android项目,导入java文件,如下



运行,发现`decode.dex`不存在

apk搜索,发现只有`com.zj.wuaipojie2024_2.C#read`有使用,盲猜就是这个`assets/classes.dex`

改名,放到对应的路径`/data/data/包名/app_data/decode.dex`

删除`getStaticMethod`方法中的删除操作



运行!!!



`2.dex`成功导出!!!

已知,`isValidate`调用`com.zj.wuaipojie2024_2.A.d(Context,密码)`

```java
// 2.dex                com.zj.wuaipojie2024_2.A.d

public static String d(Context context, String str) {
    MainActivity.sSS(str);//frida检测
    String signInfo = Utils.getSignInfo(context);//签名校验
    if (signInfo == null || !signInfo.equals("fe4f4cec5de8e8cf2fca60a4e61f67bcd3036117")) {
      return "";
    }
    StringBuffer stringBuffer = new StringBuffer();
    int i = 0;
    while (stringBuffer.length() < 9 && i < 40) {
      int i2 = i + 1;
      String substring = "0485312670fb07047ebd2f19b91e1c5f".substring(i, i2);
      if (!stringBuffer.toString().contains(substring)) {
            stringBuffer.append(substring);
      }
      i = i2;
    }
   
    //stringBuffer.toString().toUpperCase()                锁屏密码 048531267
    return !str.equals(stringBuffer.toString().toUpperCase()) ? "" : "唉!哪有什么亿载沉睡的玄天帝,不过是一位被诅咒束缚的旧日之尊,在灯枯之际挣扎的南柯一梦罢了。有缘人,这份机缘就赠予你了。坐标在B.d";
}


//感觉这个B.d还是不对,可能还得解密
public static String d(String str) {
    return "?" + str + "?";
}
```

这时候就得灵机一动了,这玩意卡了我好几天,在查看dex时发现有如下字样



说明`B.d`方法肯定是没解出来,fix是根据int数组进行修复的,这想起了另一个数组B_offset = ,解密得

```java
public static String d(String str) {
    return "机缘是{" + Utils.md5(Utils.getSha1("password+你的uid".getBytes())) + "}";
}
```

## 安卓代码

```java
package com.ql.test;

import static com.zj.wuaipojie2024_2.C.isValidate;

import android.os.Bundle;
import android.widget.Button;

import androidx.appcompat.app.AppCompatActivity;

import com.zj.wuaipojie2024_2.Utils;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      Button button = findViewById(R.id.button);
      button.setOnClickListener(v -> test());
      System.out.println(test2("048531267"));
      System.out.println(test3("048531267", "838695"));
    }


    public void test() {
      try {
            int[] A_offset = new int;
            A_offset = 0;
            A_offset = 3;
            A_offset = 7908;
            isValidate(this, "123456", A_offset);

            A_offset = 1;
            A_offset = 1;
            A_offset = 8108;
            isValidate(this, "123456", A_offset);
      } catch (Exception e) {
            e.printStackTrace();
      }
    }

    public String test2(String str) {
      StringBuffer stringBuffer = new StringBuffer();
      int i = 0;
      while (stringBuffer.length() < 9 && i < 40) {
            int i2 = i + 1;
            String substring = "0485312670fb07047ebd2f19b91e1c5f".substring(i, i2);
            if (!stringBuffer.toString().contains(substring)) {
                stringBuffer.append(substring);
            }
            i = i2;
      }
      System.out.println("锁屏密码:" + stringBuffer.toString().toUpperCase());
      return !str.equals(stringBuffer.toString().toUpperCase()) ? "" : "唉!哪有什么亿载沉睡的玄天帝,不过是一位被诅咒束缚的旧日之尊,在灯枯之际挣扎的南柯一梦罢了。有缘人,这份机缘就赠予你了。坐标在B.d";

    }

    public String test3(String password, String uid) {
      try {
            return "机缘是{" + Utils.md5(Utils.getSha1((password + uid).getBytes())) + "}";
      } catch (Exception e) {
            e.printStackTrace();
            return "";
      }
    }
}
```

## 花絮

sSS方法是个frida检测,有简单的`/proc/self/maps`检测



密码的样子




ccczl 发表于 2024-3-27 13:37

原神那个,我一开始改Activity,发现打开一片白,我蒙了以为改坏了,后面用玩游戏玩过了,发现也是一片白,于是拿起手机来,突然一阵bgm响起...才意识到我一开始就过了{:1_911:}

QAQ~QL 发表于 2024-2-28 18:02

Hmily 发表于 2024-2-28 17:59
@QAQ~QL 辛苦了,感觉最好能把所有的合并到一个给精华,单独分开给2个优秀感觉又不如一个精华好,但编辑确 ...

不折腾了,打算py写个附件上传模块,受不了了,这么改附件改图片太费时间了
主要是我七牛的图床竟然这么不靠谱,太奇怪了

sydr1145 发表于 2024-2-28 17:10

66666666666666666666666

sydr1145 发表于 2024-2-28 17:11

不错学习了,多谢!{:1_893:}

Hmily 发表于 2024-2-28 17:59

@QAQ~QL 辛苦了,感觉最好能把所有的合并到一个给精华,单独分开给2个优秀感觉又不如一个精华好,但编辑确实很辛苦。

winjie1975 发表于 2024-2-28 23:17

谢谢,辛苦了。顶一个。

心雨飞飞 发表于 2024-2-29 16:06

牛啊!佩服佩服。

苏落大神 发表于 2024-2-29 17:33

做不出来,根本出不来

Thanatosc 发表于 2024-2-29 17:35

膜拜大佬

kiritozhj 发表于 2024-2-29 18:53

太强了佬
页: [1] 2 3 4 5
查看完整版本: 【2024春节】解题领红包题解 Android3~5题