[原创]攻防世界Mobile通关记录_apk逆向12_RememberOther_Ph0en1x-100_ill-intentions_wp
2022-10-27 10:59:32 Author: bbs.pediy.com(查看原文) 阅读量:12 收藏

前言

业余时间写的,纯兴趣更新!

apk逆向

题目信息

运行apk,随便输入,点击确定,弹框错误。

分析

将其拖入jeb中,查看类MainActivity中的onCreate方法。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

public void onCreate(Bundle arg3) {

    super.onCreate(arg3);

    this.setContentView(0x7F040019);

    this.setTitle(0x7F06001D);

    this.edit_userName = "Tenshine";

    this.edit_sn = this.findViewById(0x7F0C0051);

    this.btn_register = this.findViewById(0x7F0C0052);

    this.btn_register.setOnClickListener(new View$OnClickListener() {

        public void onClick(View arg5) {

            if(!MainActivity.this.checkSN(MainActivity.this.edit_userName.trim(), MainActivity.this.edit_sn.getText().toString().trim())) {

                Toast.makeText(MainActivity.this, 0x7F06001E, 0).show();

            }

            else {

                Toast.makeText(MainActivity.this, 0x7F06001B, 0).show();

                MainActivity.this.btn_register.setEnabled(false);

                MainActivity.this.setTitle(0x7F060019);

            }

        }

    });

}

即调用了checkSN方法。参数1为 "Tenshine",参数2为我们输入的字符串。

看下checkSN的实现(具体看代码中的分析)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

private boolean checkSN(String str1, String input_str) {

        boolean v7 = false;

        if(str1 != null) {

            try {

                if(str1.length() == 0) {

                    return v7;

                }

                if(input_str == null) {

                    return v7;

                }

                if(input_str.length() != 22) {

                    return v7;

                }

                MessageDigest v1 = MessageDigest.getInstance("MD5");

                v1.reset();

                v1.update(str1.getBytes());//对参数1进行md5加密

                String v3 = MainActivity.toHexString(v1.digest(), "");//并赋值给v3

                StringBuilder v5 = new StringBuilder();

                int v4;

                for(v4 = 0; v4 < v3.length(); v4 += 2) { //将v3密文每隔一位进行舍弃

                    v5.append(v3.charAt(v4));

                }

                if(!"flag{" + v5.toString() + "}".equalsIgnoreCase(input_str)) {

                    return v7;                    //然后拼接上flag{}再与我们的输入将进行比较。

                }

            }

            catch(NoSuchAlgorithmException v2) {

                goto label_40;

            }

            v7 = true;

        }

        return v7;

    label_40:

        v2.printStackTrace();

        return v7;

    }

解题

1

2

3

4

5

6

7

8

9

10

11

12

import hashlib

str1=b"Tenshine"

str2=hashlib.md5(str1).hexdigest()

print(str2)

flag=""

for i in range(len(str2)):

    if(i%2==0):

        flag+=str2[i]

print(flag)

APK逆向-2

题目信息

安装apk安装失败

将其拖入jadx发现AndroidManifest.xml清单文件没有内容

分析

应该是AndroidManifest.xml文件被做了手脚。将其提取出来进行修改。

apktool提取失败。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

>apktool d APK逆向-2.apk

I: Using Apktool 2.6.1 on APK逆向-2.apk

I: Loading resource table...

I: Decoding AndroidManifest.xml with resources...

Exception in thread "main" brut.androlib.err.RawXmlEncounteredException: Could not decode XML

        at brut.androlib.res.decoder.XmlPullStreamDecoder.decode(XmlPullStreamDecoder.java:145)

        at brut.androlib.res.decoder.XmlPullStreamDecoder.decodeManifest(XmlPullStreamDecoder.java:151)

        at brut.androlib.res.decoder.ResFileDecoder.decodeManifest(ResFileDecoder.java:159)

        at brut.androlib.res.AndrolibResources.decodeManifestWithResources(AndrolibResources.java:193)

        at brut.androlib.Androlib.decodeManifestWithResources(Androlib.java:141)

        at brut.androlib.ApkDecoder.decode(ApkDecoder.java:109)

        at brut.apktool.Main.cmdDecode(Main.java:175)

        at brut.apktool.Main.main(Main.java:79)

Caused by: java.io.IOException: Expected: 0x001c0001, got: 0x01001c00

        at brut.util.ExtDataInput.skipCheckChunkTypeInt(ExtDataInput.java:72)

        at brut.androlib.res.decoder.StringBlock.read(StringBlock.java:50)

        at brut.androlib.res.decoder.AXmlResourceParser.doNext(AXmlResourceParser.java:814)

        at brut.androlib.res.decoder.AXmlResourceParser.next(AXmlResourceParser.java:98)

        at brut.androlib.res.decoder.AXmlResourceParser.nextToken(AXmlResourceParser.java:108)

        at org.xmlpull.v1.wrapper.classic.XmlPullParserDelegate.nextToken(XmlPullParserDelegate.java:105)

        at brut.androlib.res.decoder.XmlPullStreamDecoder.decode(XmlPullStreamDecoder.java:138)

        ... 7 more

把apk后缀改成zip,将其拉出来。

找三个正常的清单文件(apk逆向,app1,app2),查看它们文件头哪些是共同的地方。

1

2

3

4

第一行:偏移0x0-0x3 0x6-0xB  0xE-0xF

第二行:偏移0x1-0xB 0xE-0xF

第三行:偏移0x1-0xF

第四行:偏移0x1-0x7 0x9-0xB 0xD-0xF

然后再查看下本题

1

2

3

4

第一行:偏移0x0-0x3(一致) 0x6-0xB(不一致 0x8-0xB刚好反转)  0xE-0xF(一致)

第二行:偏移0x1-0xB(一致) 0xD-0xF(一致)

第三行:偏移0x1-0xF(不一致 0x2位置变成1了)

第四行:偏移0x1-0x7(一致) 0x9-0xB(一致) 0xD-0xF(一致)

所以这里我们将不一致的地方改为一致,即第一行的偏移0x6-0xB位置和第三行的偏移为偏移0x1-0xF

将其再覆盖掉apk中的清单文件,然后便可以安装成功了.

但安装成功后,却没有显示桌面图标.使用am进行打开Mainactivity界面

1

am start com.example.mmsheniq/.Mainactivity

同时看下logcat

先不点安装,点空白处。

点击注册

这里填过信息后点注册一直报错"请输入正确的身份证"。

本想好好分析下代码,可我懒死了.

解题

修复过AndroidManifest.xml文件后拖入jadx中,再看下清单文件中的内容。发现可疑的字符串,其实就是flag。

RememberOther

题目信息

附件是个zip压缩包,解压后

查看FoundItFun.docx,发现只有一句话。

分析

jadx

1

2

3

4

5

6

7

8

9

10

11

12

this.btn_register.setOnClickListener(new View.OnClickListener() { // from class: com.droider.crackme0201.MainActivity.1

            @Override // android.view.View.OnClickListener

            public void onClick(View v) {

                if (!MainActivity.this.checkSN(MainActivity.this.edit_userName.getText().toString().trim(), MainActivity.this.edit_sn.getText().toString().trim())) {

                    Toast.makeText(MainActivity.this, (int) R.string.unsuccessed, 0).show();

                    return;

                }

                Toast.makeText(MainActivity.this, (int) R.string.successed, 0).show();

                MainActivity.this.btn_register.setEnabled(false);

                MainActivity.this.setTitle(R.string.registered);

            }

        });

水啊水啊,如果checkSN方法返回true,则注册成功。

checkSN

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

public boolean checkSN(String userName, String sn) {

    try {

        if (userName.length() == 0 && sn.length() == 0) {//什么都不输入就返回true  哈哈

            return true;

        }

        if (userName == null || userName.length() == 0) {

            return false;

        }

        if (sn == null || sn.length() != 16) {

            return false;

        }

        MessageDigest digest = MessageDigest.getInstance("MD5");

        digest.reset();

        digest.update(userName.getBytes());              //对用户名进行md5加密

        byte[] bytes = digest.digest();

        String hexstr = toHexString(bytes, BuildConfig.FLAVOR);//BuildConfig.FLAVOR为空字符串

        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < hexstr.length(); i += 2) {

            sb.append(hexstr.charAt(i));            //隔一位取一位

        }

        String userSN = sb.toString();

        return userSN.equalsIgnoreCase(sn);         //用户名经过上面的加密后就再与输入的注册码sn相等即返回true

    } catch (NoSuchAlgorithmException e) {

        e.printStackTrace();

        return false;

    }

}

什么都不输入就会返回true的,返回successed对应的字符串。

1

<string name="successed">md5:b3241668ecbeb19921fdac5ac1aafa69</string>

对其进行解密得到

MD5免费在线解密破解_MD5在线加密-SOMD5

解题

但最终的flag需要在后面再添加上ANDROID即

脑洞,无聊。

Ph0en1x-100

题目信息

分析

jadx

1

2

3

4

5

6

7

8

9

10

11

12

13

14

public void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_main);

    this.etFlag = (EditText) findViewById(R.id.flag_edit);

}

public void onGoClick(View v) {

    String sInput = this.etFlag.getText().toString();

    if (getSecret(getFlag()).equals(getSecret(encrypt(sInput)))) {

        Toast.makeText(this, "Success", 1).show();

    } else {

        Toast.makeText(this, "Failed", 1).show();

    }

}

如果getSecret(getFlag())的返回值和getSecret(encrypt(sInput)))相等就证明Success。

即getFlag()的返回值和encrypt(sInput))返回值相等,就证明Success。

这这两个函数都是在phcm的so库中实现的。

1

2

3

4

5

6

7

public native String encrypt(String str);

public native String getFlag();

static {

    System.loadLibrary("phcm");

}

所以分析下so库中的这两个函数

首先这里是getFlag函数,静态分析太累,动态分析太懒,这里其实不用取分析这个算法,它的返回值是固定的,所以直接用frida去hook这个函数打印出它的返回值就好了,也可以通过向smali代码中插入log去打印。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

int __fastcall Java_com_ph0en1x_android_1crackme_MainActivity_getFlag(_JNIEnv *env)

{

  char *v1; // r4

  _JNIEnv *v2; // r7

  char *v3; // r3

  int v4; // r0

  int v5; // r1

  char *v6; // r2

  const char *v7; // r3

  int v8; // r0

  int v9; // r1

  int v10; // r4

  int v11; // r0

  __int16 v12; // r3

  signed int v13; // r8

  signed int v14; // r0

  char *v15; // r9

  char v16; // r3

  char v17; // t1

  int v18; // r1

  char s; // [sp+4h] [bp-5Ch]

  char v21[40]; // [sp+14h] [bp-4Ch]

  char v22; // [sp+40h] [bp-20h]

  v1 = v21;

  v2 = env;

  v3 = (char *)&dword_2770;

  do

  {

    v4 = *(_DWORD *)v3;

    v3 += 8;

    v5 = *((_DWORD *)v3 - 1);

    *(_DWORD *)v1 = v4;

    *((_DWORD *)v1 + 1) = v5;

    v1 += 8;

  }

  while ( v3 != "Hello Ph0en1x" );

  v6 = &s;

  v7 = "Hello Ph0en1x";

  do

  {

    v8 = *(_DWORD *)v7;

    v7 += 8;

    v9 = *((_DWORD *)v7 - 1);

    *(_DWORD *)v6 = v8;

    *((_DWORD *)v6 + 1) = v9;

    v10 = (int)(v6 + 8);

    v6 += 8;

  }

  while ( v7 != "0en1x" );

  v11 = *(_DWORD *)v7;

  v12 = *((_WORD *)v7 + 2);

  *(_DWORD *)v10 = v11;

  *(_WORD *)(v10 + 4) = v12;

  v13 = strlen(&s);

  v14 = strlen(v21) - 1;

  v15 = &v21[v14];

  while ( v14 > 0 )

  {

    v16 = *v15 + 1;

    *v15 = v16;

    v17 = *(v15-- - 1);

    v18 = v14-- % v13;

    v15[1] = ((v16 - v17) ^ *(&v22 + v18 - 60)) - 1;

  }

  v21[0] = (v21[0] ^ 0x48) - 1;

  return ((int (__fastcall *)(_JNIEnv *, char *))v2->functions->NewStringUTF)(v2, v21);

}

然后看encrypt函数,将每个字符对应的ascii进行减一。

1

2

3

4

5

6

7

8

9

10

11

12

int __fastcall Java_com_ph0en1x_android_1crackme_MainActivity_encrypt(_JNIEnv *a1)

{

  _JNIEnv *v1; // r6

  const char *input_str; // r4

  const char *i; // r5

  v1 = a1;

  input_str = (const char *)((int (*)(void))a1->functions->GetStringUTFChars)();

  for ( i = input_str; i - input_str < strlen(input_str); ++i )

    --*i;

  return ((int (__fastcall *)(_JNIEnv *, const char *))v1->functions->NewStringUTF)(v1, input_str);

}

所以这里只要首先获得getFlag函数的返回值,然后再将返回值的每个字符对应的ascii进行加一即可。

解题

利用frida去hook native层的getFlag方法的返回值。

run.py

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

import sys

import frida

process_name = 'Android_crackme'

def on_message(message, data):

    if message['type'] == 'send':

        print(f"[*] {message['payload']}")

    else:

        print(message)

if __name__ == '__main__':

    try:

        device = frida.get_usb_device(timeout=1000)

        print("* get usb device成功")

    except:

        device = frida.get_remote_device(timeout=1000)

        print("* get remote device成功")

    if not device:

        print("* 连接到Frida Server失败")

    else:

        process = device.attach(process_name)

        js = open('hook.js', encoding='utf-8').read()

        print(js)

        script = process.create_script(js)

        script.on('message', on_message)

        script.load()

        input()

        script.unload()

hook.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

Java.perform(function() {

    //获取指定类

    var cls = Java.use('com.ph0en1x.android_crackme.MainActivity');

    //Hook指定函数

    cls.getFlag.overload().implementation = function() {

        //进入函数

        console.log('getFlag-in');

        //调用原函数

        var result = this.getFlag();

        //打印出参

        console.log('getFlag-out', result);

        //返回给原函数的调用

        return result;

    }

});

成功将返回值给打印出来。

然后再将返回值的每个字符对应的ascii进行加一便是我们的输入即flag

1

2

3

4

5

6

flag=""

for i in range(len(str1)):

    flag+=chr(ord(str1[i])+1)

print(flag)

上面其实是hook的java层,也可以去hook native层,效果是一样的

native_hook.js

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

console.log("脚本载入成功");

Java.perform(function () {

    var getflagAddr = Module.findExportByName("libphcm.so", "Java_com_ph0en1x_android_1crackme_MainActivity_getFlag");

    console.log(getflagAddr);

    if (getflagAddr != null) {

        Interceptor.attach(getflagAddr, {

            onEnter: function (args) {

                //args参数数组

                console.log('encode-Enter')

                // console.log(args[0], Memory.readCString(args[0]));

                // console.log(args[1], Memory.readCString(args[1]));

                // console.log(args[2], Memory.readCString(args[2]));

                // console.log(args[3], Memory.readCString(args[3]));

                // console.log(args[4], Memory.readCString(args[4]));

            },

            onLeave: function (retval) {

                //retval函数返回值

                console.log('encode-Leave');

                var String_java = Java.use('java.lang.String'); //因为so库中getFlag函数返回的时候是

                                        //return ((int (__fastcall *)(_JNIEnv *, char *))v2->functions->NewStringUTF)(v2, v21);

                var args_4 = Java.cast(retval,String_java);

                //console.log(args_4);

                send("getFlag()==>"+args_4);

                //console.log(retval.toString());

                console.log('======');

            }

        });

    }

})

这里我们也尝试下通过改smali代码的方式将这个返回值给打印出来吧。

1

invoke-static {v1, v0}, Landroid/util/Log;->d(Ljava/lang/String;Ljava/lang/String;)I

重新编译签名安装运行。成功将其打印出来。

后面就一样了。

ill-intentions

题目信息

打开后
### 分析

jadx(太好用了吧)

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

package com.example.application;

import android.app.Activity;

import android.content.IntentFilter;

import android.os.Bundle;

import android.widget.TextView;

import com.example.hellojni.Manifest;

/* loaded from: classes.dex */

public class MainActivity extends Activity {

    @Override // android.app.Activity

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        TextView tv = new TextView(getApplicationContext());

        tv.setText("Select the activity you wish to interact with.To-Do: Add buttons to select activity, for now use Send_to_Activity");

        setContentView(tv);

        IntentFilter filter = new IntentFilter();

        filter.addAction("com.ctf.INCOMING_INTENT");

        Send_to_Activity receiver = new Send_to_Activity();

        registerReceiver(receiver, filter, Manifest.permission._MSG, null);//注册个广播接收者

    }

}

然后看下这个广播接收者

主要做的就是接收传过来msg值,根据值的不同会跳转到不同的Activity

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

package com.example.application;

import android.content.BroadcastReceiver;

import android.content.Context;

import android.content.Intent;

import android.widget.Toast;

/* loaded from: classes.dex */

public class Send_to_Activity extends BroadcastReceiver {

    @Override // android.content.BroadcastReceiver

    public void onReceive(Context context, Intent intent) {

        String msgText = intent.getStringExtra("msg");

        if (msgText.equalsIgnoreCase("ThisIsTheRealOne")) {

            Intent outIntent = new Intent(context, ThisIsTheRealOne.class);

            context.startActivity(outIntent);

        } else if (msgText.equalsIgnoreCase("IsThisTheRealOne")) {

            Intent outIntent2 = new Intent(context, IsThisTheRealOne.class);

            context.startActivity(outIntent2);

        } else if (msgText.equalsIgnoreCase("DefinitelyNotThisOne")) {

            Intent outIntent3 = new Intent(context, DefinitelyNotThisOne.class);

            context.startActivity(outIntent3);

        } else {

            Toast.makeText(context, "Which Activity do you wish to interact with?", 1).show();

        }

    }

}

ThisIsTheRealOne.class

调用了hello-jni.so中的orThat方法,参数都是固定的,没有用户输入。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

package com.example.application;

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;

import com.example.hellojni.Manifest;

import com.example.hellojni.R;

/* loaded from: classes.dex */

public class ThisIsTheRealOne extends Activity {

    public native String computeFlag(String str, String str2);

    public native String definitelyNotThis(String str, String str2, String str3);

    public native String orThat(String str, String str2, String str3);

    public native String perhapsThis(String str, String str2, String str3);

    @Override // android.app.Activity

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        TextView tv = new TextView(this);

        tv.setText("Activity - This Is The Real One");

        Button button = new Button(this);

        button.setText("Broadcast Intent");

        setContentView(button);

        button.setOnClickListener(new View.OnClickListener() { // from class: com.example.application.ThisIsTheRealOne.1

            @Override // android.view.View.OnClickListener

            public void onClick(View v) {

                Intent intent = new Intent();

                intent.setAction("com.ctf.OUTGOING_INTENT");

                String a = ThisIsTheRealOne.this.getResources().getString(R.string.str2) + "YSmks";

                String b = Utilities.doBoth(ThisIsTheRealOne.this.getResources().getString(R.string.dev_name));

                String c = Utilities.doBoth(getClass().getName());

                intent.putExtra("msg", ThisIsTheRealOne.this.orThat(a, b, c));

                ThisIsTheRealOne.this.sendBroadcast(intent, Manifest.permission._MSG);

            }

        });

    }

    static {

        System.loadLibrary("hello-jni");

    }

}

IsThisTheRealOne.class
调用了hello-jni.so中的perhapsThis方法,参数也都是固定的,没有用户输入。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

package com.example.application;

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;

import com.example.hellojni.Manifest;

import com.example.hellojni.R;

/* loaded from: classes.dex */

public class IsThisTheRealOne extends Activity {

    public native String computeFlag(String str, String str2);

    public native String definitelyNotThis(String str, String str2, String str3);

    public native String orThat(String str, String str2, String str3);

    public native String perhapsThis(String str, String str2, String str3);

    @Override // android.app.Activity

    public void onCreate(Bundle savedInstanceState) {

        getApplicationContext();

        super.onCreate(savedInstanceState);

        TextView tv = new TextView(this);

        tv.setText("Activity - Is_this_the_real_one");

        Button button = new Button(this);

        button.setText("Broadcast Intent");

        setContentView(button);

        button.setOnClickListener(new View.OnClickListener() { // from class: com.example.application.IsThisTheRealOne.1

            @Override // android.view.View.OnClickListener

            public void onClick(View v) {

                Intent intent = new Intent();

                intent.setAction("com.ctf.OUTGOING_INTENT");

                String a = IsThisTheRealOne.this.getResources().getString(R.string.str3) + "\\[email protected]^~";

                String b = Utilities.doBoth(IsThisTheRealOne.this.getResources().getString(R.string.app_name));

                String name = getClass().getName();

                String c = Utilities.doBoth(name.substring(0, name.length() - 2));

                intent.putExtra("msg", IsThisTheRealOne.this.perhapsThis(a, b, c));

                IsThisTheRealOne.this.sendBroadcast(intent, Manifest.permission._MSG);

            }

        });

    }

    static {

        System.loadLibrary("hello-jni");

    }

}

DefinitelyNotThisOne.class

调用了hello-jni.so中的definitelyNotThis方法,参数也都是固定的,没有用户输入。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

package com.example.application;

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.TextView;

import com.example.hellojni.Manifest;

import com.example.hellojni.R;

/* loaded from: classes.dex */

public class DefinitelyNotThisOne extends Activity {

    public native String computeFlag(String str, String str2);

    public native String definitelyNotThis(String str, String str2);

    public native String orThat(String str, String str2, String str3);

    public native String perhapsThis(String str, String str2, String str3);

    @Override // android.app.Activity

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        TextView tv = new TextView(this);

        tv.setText("Activity - Is_this_the_real_one");

        Button button = new Button(this);

        button.setText("Broadcast Intent");

        setContentView(button);

        button.setOnClickListener(new View.OnClickListener() { // from class: com.example.application.DefinitelyNotThisOne.1

            @Override // android.view.View.OnClickListener

            public void onClick(View v) {

                Intent intent = new Intent();

                intent.setAction("com.ctf.OUTGOING_INTENT");

                DefinitelyNotThisOne.this.getResources().getString(R.string.str1);

                String b = Utilities.doBoth(DefinitelyNotThisOne.this.getResources().getString(R.string.test));

                String c = Utilities.doBoth("Test");

                intent.putExtra("msg", DefinitelyNotThisOne.this.definitelyNotThis(b, c));

                DefinitelyNotThisOne.this.sendBroadcast(intent, Manifest.permission._MSG);

            }

        });

    }

    static {

        System.loadLibrary("hello-jni");

    }

}

解题

学了hook后就懒了呢,因为整个程序都没有用户输入,flag肯定是在某个地方进行生成的,猜测是so库中的definitelyNotThis函数返回值或者是orThat函数返回值或者perhapsThis返回值。那我们直接在java层hook这三个函数好了

java_hook.py

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

Java.perform(function() {

    // //获取指定类

    // var cls = Java.use('com.ph0en1x.android_crackme.MainActivity');

    // //Hook指定函数

    // cls.getFlag.overload().implementation = function() {

    //     //进入函数

    //     console.log('getFlag-in');

    //     //调用原函数

    //     var result = this.getFlag();

    //     //打印出参

    //     console.log('getFlag-out', result);

    //     //返回给原函数的调用

    //     return result;

    // }

    let DefinitelyNotThisOne = Java.use("com.example.application.DefinitelyNotThisOne");

    DefinitelyNotThisOne["definitelyNotThis"].implementation = function (str, str2) {

        console.log('definitelyNotThis is called' + ', ' + 'str: ' + str + ', ' + 'str2: ' + str2);

        let ret = this.definitelyNotThis(str, str2);

        console.log('definitelyNotThis ret value is ' + ret);

        return ret;

    };

    let IsThisTheRealOne = Java.use("com.example.application.IsThisTheRealOne");

    IsThisTheRealOne["perhapsThis"].implementation = function (str, str2, str3) {

    console.log('perhapsThis is called' + ', ' + 'str: ' + str + ', ' + 'str2: ' + str2 + ', ' + 'str3: ' + str3);

    let ret = this.perhapsThis(str, str2, str3);

    console.log('perhapsThis ret value is ' + ret);

    return ret;

    };

    let ThisIsTheRealOne = Java.use("com.example.application.ThisIsTheRealOne");

    ThisIsTheRealOne["orThat"].implementation = function (str, str2, str3) {

    console.log('orThat is called' + ', ' + 'str: ' + str + ', ' + 'str2: ' + str2 + ', ' + 'str3: ' + str3);

    let ret = this.orThat(str, str2, str3);

    console.log('orThat ret value is ' + ret);

    return ret;

    };

});

但是现在我们还要解决个问题,我们需要跳转到上面的三个不同Activity中才可以触发hook函数,而看清单中,这三个activity都不是export,也就没办法使用am命令打开。

不过可以将它修改成export后再重新打包,或者将初始Activity指定为上面的其实一个Activity后重新打包。或者按程序逻辑发送不同的广播信息去跳转到不同的Activity中。

(32条消息) 使用am命令发送带参数的广播和服务_sunxiaolin2016的博客-CSDN博客

1

am broadcast -n "com.example.hellojni/com.example.application.Send_to_Activity" -a com.ctf.INCOMING_INTENT -e "msg" ThisIsTheRealOne

这里记录下

-n后面是包名/类名

但是这个apk

包名如果写com.example.application 是不行的

写成com.example.hellojni才可以,填清单文件的包名

hook效果

1

2

3

4

orThat is called, str: IIjsWa}iyYSmks, str2: ODBkNTNhZjRmMGZmMWYtMzhhMDIzMmMwYjcwNzlhMTUwMDczOWNlYjhjMhUWYWYeMzYiZDFkMTY?

, str3: MhMhMGJhMTUhOGYWZThlZDQaYWJkYzkWZTktMTQhMjYhOTgiOTZkODgaNWRkZmFiZTciOGNlNDI?

orThat ret value is KeepTryingThisIsNotTheActivityYouAreLookingForButHereHaveSomeInternetPoints!

继续

1

am broadcast -n "com.example.hellojni/com.example.application.Send_to_Activity" -a com.ctf.INCOMING_INTENT -e "msg" DefinitelyNotThisOne

效果

1

2

3

4

definitelyNotThis is called, str: YjYwYWZjMjRkMhVhZTQhZDIwZGFkNWJhMGZmZGYiYmQaMmFkMjBiMTEhNDAtMzMzMjdlZmEWNzU?

, str2: MzYwNjMeNjgxNWZkNGQeOTFhOTIhNDkiMDVhNDBkYTAyNWQtYhYxNWYwOTUxMzZiMTlmMzciMjM?

definitelyNotThis ret value is Told you so!

继续

1

am broadcast -n "com.example.hellojni/com.example.application.Send_to_Activity" -a com.ctf.INCOMING_INTENT -e "msg" IsThisTheRealOne

效果

1

2

3

4

perhapsThis is called, str: TRytfrgooq|F{i-JovFBungFk\[email protected]^~, str2: ZGFkNGIwYzIWYjEzMTUWNjVjNTVlNjZhOGJkNhYtODIyOGEaMTMWNmQaOTVjZjkhMzRjYmUzZGE?

, str3: MzQxZTZmZjAxMmIiMWUzNjUxMmRiYjIxNDUwYTUxMWItZGQzNWUtMzkyOWYyMmQeYjZmMzEaNDQ?

perhapsThis ret value is Congratulation!YouFoundTheRightActivityHereYouGo-CTF{IDontHaveABadjokeSorry}

ok,成功拿到flag.

1

CTF{IDontHaveABadjokeSorry}

上面是对java层进行的hook,下面是native进行hook

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

function main() {

    function getjstring(jstr) {

        return Java.vm.getEnv().getStringUtfChars(jstr, null).readCString();

    }

    Java.perform(function () {

        console.log("脚本载入成功");

        //var so_addr = Module.findBaseAddress("libhello-jni.so");

        var perhapsThis_addr = Module.findExportByName("libhello-jni.so", "Java_com_example_application_IsThisTheRealOne_perhapsThis");

        console.log("perhapsThis_addr", perhapsThis_addr);

        Interceptor.attach(perhapsThis_addr, {

            onEnter: function (args) {

                console.log("perhapsThis_args:[1]", getjstring(args[2]), "\n    [2]", getjstring(args[3]), "\n    [3]", getjstring(args[4]), "\n");

            },

            onLeave: function (retval) {

                console.log("perhapsThis_result:", getjstring(retval));

            },

        });

        Interceptor.attach(Module.findExportByName("libhello-jni.so", "Java_com_example_application_ThisIsTheRealOne_orThat"), {

            onEnter: function (args) {

                console.log("orThat_args:[1]", getjstring(args[2]), "\n    [2]", getjstring(args[3]), "\n    [3]", getjstring(args[4]), "\n");

            },

            onLeave: function (retval) {

                console.log("orThat_result:", getjstring(retval));

            },

        });

        Interceptor.attach(Module.findExportByName("libhello-jni.so", "Java_com_example_application_DefinitelyNotThisOne_definitelyNotThis"), {

            onEnter: function (args) {

                console.log("definitelyNotThis_args:[1]", getjstring(args[2]), "\n    [2]", getjstring(args[3]), "\n");

            },

            onLeave: function (retval) {

                console.log("definitelyNotThis_result:", getjstring(retval));

            },

        });

    });

}

setImmediate(main);

[2022冬季班]《安卓高级研修班(网课)》月薪两万班招生中~


文章来源: https://bbs.pediy.com/thread-274875.htm
如有侵权请联系:admin#unsafe.sh