hans7 发表于 2022-7-26 00:59

【CTF 安卓逆向】[SCTF2019]Strange_apk——安卓父子组件通信机制

本帖最后由 hans7 于 2022-7-26 01:04 编辑

本文52pojie:https://www.52pojie.cn/thread-1666622-1-1.html

本文juejin:https://juejin.cn/post/7124354639419408414/

**作者:(https://blog.csdn.net/hans774882968)以及(https://juejin.cn/user/1464964842528888)**

### 获取另一个APK

打开JEB:

```java
    @Override// android.content.ContextWrapper
    protected void attachBaseContext(Context base) {
      super.attachBaseContext(base);
      try {
            File _ = this.getDir("sctf_odex", 0);
            File l = this.getDir("sctf_lib", 0);
            this.odexPath = _.getAbsolutePath();
            this.libPath = l.getAbsolutePath();
            this.apkFileName = _.getAbsolutePath() + "/sctf.apk";
            File File = new File(this.apkFileName);
            Log.i("demo", "apk size:" + File.length());
            if(!File.exists()) {
                File.createNewFile();
                this._(this.__("data"));
            }

            Object currentActivityThread = s.invokeStaticMethod("android.app.ActivityThread", "currentActivityThread", new Class, new Object);
            String packageName = this.getPackageName();
            WeakReference v5 = Build.VERSION.SDK_INT >= 19 ? ((WeakReference)((ArrayMap)s.getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mPackages")).get(packageName)) : ((WeakReference)((HashMap)s.getFieldOjbect("android.app.ActivityThread", currentActivityThread, "mPackages")).get(packageName));
            DexClassLoader dLoader = new DexClassLoader(this.apkFileName, this.odexPath, this.libPath, ((ClassLoader)s.getFieldOjbect("android.app.LoadedApk", v5.get(), "mClassLoader")));
            s.setFieldOjbect("android.app.LoadedApk", "mClassLoader", v5.get(), dLoader);
            Log.i("demo", "classloader:" + dLoader);
      }
      catch(Exception e) {
            Log.i("demo", "error:" + Log.getStackTraceString(e));
            e.printStackTrace();
      }
    }
```

这里有一个特殊的知识点:`attachBaseContext`方法调用时机早于`onCreate`。另外,查看代码易知`this._(this.__("data"));`会获得一个新的apk。那我们写个获取新apk的脚本:

```python
def get_byte(v):
    return v.to_bytes(1, byteorder='little')


with open('data', 'rb') as f:
    a = f.read()
    with open('sctf.apk', 'wb') as g:
      for i in range(len(a)):
            g.write(get_byte(a ^ ord("syclover")))
```

### 安卓父子组件通信机制

逐一查看`sctf.demo.myapplication`下的每个类。我们发现`t`这个类是主界面。如下:

```java
package sctf.demo.myapplication;

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.TextView;
import android.widget.Toast;
import java.math.BigInteger;
import java.security.MessageDigest;

public class t extends AppCompatActivity {
    @Override// android.support.v4.app.FragmentActivity
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      String v6;
      TextView tv = (TextView)this.findViewById(0x7F07008B);// id:textView2
      Button bu = (Button)this.findViewById(0x7F070023);// id:button2
      if(requestCode == 1 && resultCode == -1) {
            String key = "";
            try {
                MessageDigest md = MessageDigest.getInstance("MD5");
                md.update("syclover".getBytes());
                v6 = new BigInteger(1, md.digest()).toString(16);
            }
            catch(Exception e) {
                e.printStackTrace();
                goto label_29;
            }

            key = v6;
      label_29:
            if(f.encode(data.getStringExtra("data_return"), key).equals("~8t808_8A8n848r808i8d8-8w808r8l8d8}8")) {
                tv.setVisibility(0);
                bu.setVisibility(4);
                return;
            }

            Toast.makeText(this.getApplicationContext(), "one more step", 1).show();
      }
    }

    @Override// android.support.v7.app.AppCompatActivity
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      this.setContentView(0x7F09001C);// layout:activity
      ((Button)this.findViewById(0x7F070023)).setOnClickListener(new View.OnClickListener() {// id:button2
            @Override// android.view.View$OnClickListener
            public void onClick(View v) {
                Intent intent = new Intent("sctf.demo.myapplication.MAIN");
                intent.addCategory("sctf.demo.myapplication.LAUNCHER");
                t.this.startActivityForResult(intent, 1);
            }
      });
    }
}
```

这里使用了`Intent`类,找到参考链接1,可知这是安卓开发里**父子组件的通信机制**。因为`sctf.demo.myapplication`下就那几个类,我们很容易猜到子组件是`s`类:

```java
package sctf.demo.myapplication;

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 s extends AppCompatActivity {
    @Override// android.support.v7.app.AppCompatActivity
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      this.setContentView(0x7F09001D);// layout:activity_main
      Button bu = (Button)this.findViewById(0x7F070022);// id:button
      this.findViewById(0x7F07008A);// id:textView
      bu.setOnClickListener(new View.OnClickListener() {
            @Override// android.view.View$OnClickListener
            public void onClick(View v) {
                String s1 = "";
                String s2 = "";
                int i = 0;
                String v0 = ((EditText)this.findViewById(0x7F070037)).getText().toString();// id:editText
                if(v0.length() == 30) {
                  while(i < 12) {
                        s1 = s1 + v0.charAt(i);
                        ++i;
                  }

                  String v1_1 = f.sctf(s1);
                  while(i < 30) {
                        s2 = s2 + v0.charAt(i);
                        ++i;
                  }

                  if(v1_1.equals("c2N0ZntXM2xjMG1l")) {
                        Intent intent = new Intent();
                        intent.putExtra("data_return", s2);
                        s.this.setResult(-1, intent);
                        s.this.finish();
                        return;
                  }

                  Toast.makeText(s.this.getApplicationContext(), "something wrong", 1).show();
                  return;
                }

                Toast.makeText(s.this.getApplicationContext(), "something wrong", 1).show();
            }
      });
    }
}
```

由参考链接1可知,`"data_return"`属性用来传递输入串的最后18个字符。

`f.sctf()`是base64编码,所以把`"c2N0ZntXM2xjMG1l"`进行base64解码,就是flag的前12个字符。

`f.encode()`如下:

```java
    public static String encode(String str, String key) {
      int s = str.length();
      int c = key.length();
      StringBuilder t = new StringBuilder();
      int f;
      for(f = 0; f < s; ++f) {
            t.append(str.charAt(f));
            t.append(key.charAt(f / c));
      }
      return t.toString();
    }
```

所以取出`"~8t808_8A8n848r808i8d8-8w808r8l8d8}8"`下标为偶数的(0-indexed)字符就是flag的后18个字符。

```python
import base64

lef = base64.b64decode("c2N0ZntXM2xjMG1l").decode()
print(lef)

enc, ky = "", ""
goal = "~8t808_8A8n848r808i8d8-8w808r8l8d8}8"
for i in range(len(goal)):
    if i % 2:
      ky += goal
    else:
      enc += goal
print(enc, ky)
print("ans = %s" % (lef + enc))
```

### 参考链接

1. 安卓父子组件通信机制:`onActivityResult() + Intent()`:https://blog.csdn.net/weixin_41008021/article/details/90346700

Stkj 发表于 2022-7-27 08:31

挺不错,值得下载。

xuwei9663 发表于 2022-8-3 21:21

完全不懂啊

Natrium 发表于 2022-8-4 00:02

感谢楼主教学

hans7 发表于 2022-8-4 01:26

xuwei9663 发表于 2022-8-3 21:21
完全不懂啊

可以实操一遍,往往能有所收获(我觉得最难的还是装软件qwq)。
如果还是不懂就放弃这题,补上相关的软件和基础知识以后再回来看,就发现它变简单了。
页: [1]
查看完整版本: 【CTF 安卓逆向】[SCTF2019]Strange_apk——安卓父子组件通信机制