【愚公系列】2021年12月 攻防世界-简单题-MOBILE-011(黑客精神)

举报
愚公搬代码 发表于 2021/12/24 10:59:19 2021/12/24
【摘要】 一、黑客精神题目链接:https://adworld.xctf.org.cn/task/task_list?type=mobile&number=6&grade=0&page=1 二、使用步骤 1.运行app 2.jadx反编译apkimport android.content.Intent;import android.os.Bundle;import android.os.Proces...

一、黑客精神

题目链接:https://adworld.xctf.org.cn/task/task_list?type=mobile&number=6&grade=0&page=1

二、使用步骤

1.运行app

在这里插入图片描述

2.jadx反编译apk

import android.content.Intent;
import android.os.Bundle;
import android.os.Process;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

/* loaded from: classes.dex */
public class MainActivity extends Activity {
    private static String workString;
    private Button btn1;

    @Override // android.app.Activity
    public void onCreate(Bundle savedInstanceState) {
        String str2;
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("com.gdufs.xman m=", "Xman");
        MyApp myApp = (MyApp) getApplication();
        int m = MyApp.m;
        if (m == 0) {
            str2 = "未注册";
        } else if (m == 1) {
            str2 = "已注册";
        } else {
            str2 = "已混乱";
        }
        setTitle("Xman" + str2);
        this.btn1 = (Button) findViewById(R.id.button1);
        this.btn1.setOnClickListener(new View.OnClickListener() { // from class: com.gdufs.xman.MainActivity.1
            @Override // android.view.View.OnClickListener
            public void onClick(View v) {
                MyApp myApp2 = (MyApp) MainActivity.this.getApplication();
                if (MyApp.m == 0) {
                    MainActivity.this.doRegister();
                    return;
                }
                ((MyApp) MainActivity.this.getApplication()).work();
                Toast.makeText(MainActivity.this.getApplicationContext(), MainActivity.workString, 0).show();
            }
        });
    }

    public void doRegister() {
        new AlertDialog.Builder(this).setTitle("注册").setMessage("Flag就在前方!").setPositiveButton("注册", new DialogInterface.OnClickListener() { // from class: com.gdufs.xman.MainActivity.3
            @Override // android.content.DialogInterface.OnClickListener
            public void onClick(DialogInterface dialog, int which) {
                Intent intent = new Intent();
                intent.setComponent(new ComponentName(BuildConfig.APPLICATION_ID, "com.gdufs.xman.RegActivity"));
                MainActivity.this.startActivity(intent);
                MainActivity.this.finish();
            }
        }).setNegativeButton("不玩了", new DialogInterface.OnClickListener() { // from class: com.gdufs.xman.MainActivity.2
            @Override // android.content.DialogInterface.OnClickListener
            public void onClick(DialogInterface dialog, int which) {
                Process.killProcess(Process.myPid());
            }
        }).show();
    }

    public void work(String str) {
        workString = str;
    }

    @Override // android.app.Activity
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }
}

在这里插入图片描述
RegActivity 界面

package com.gdufs.xman;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Process;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

/* loaded from: classes.dex */
public class RegActivity extends Activity {
    private Button btn_reg;
    private EditText edit_sn;

    @Override // android.app.Activity
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_reg);
        this.btn_reg = (Button) findViewById(R.id.button1);
        this.edit_sn = (EditText) findViewById(R.id.editText1);
        this.btn_reg.setOnClickListener(new View.OnClickListener() { // from class: com.gdufs.xman.RegActivity.1
            @Override // android.view.View.OnClickListener
            public void onClick(View v) {
                String sn = RegActivity.this.edit_sn.getText().toString().trim();
                if (sn == null || sn.length() == 0) {
                    Toast.makeText(RegActivity.this, "您的输入为空", 0).show();
                    return;
                }
                ((MyApp) RegActivity.this.getApplication()).saveSN(sn);
                new AlertDialog.Builder(RegActivity.this).setTitle("回复").setMessage("您的注册码已保存").setPositiveButton("好吧", new DialogInterface.OnClickListener() { // from class: com.gdufs.xman.RegActivity.1.1
                    @Override // android.content.DialogInterface.OnClickListener
                    public void onClick(DialogInterface dialog, int which) {
                        Process.killProcess(Process.myPid());
                    }
                }).show();
            }
        });
    }
}

在这里插入图片描述
myApp函数

package com.gdufs.xman;

import android.app.Application;
import android.util.Log;

/* loaded from: classes.dex */
public class MyApp extends Application {
    public static int m = 0;

    public native void initSN();

    public native void saveSN(String str);

    public native void work();

    static {
        System.loadLibrary("myjni");
    }

    @Override // android.app.Application
    public void onCreate() {
        initSN();
        Log.d("com.gdufs.xman m=", String.valueOf(m));
        super.onCreate();
    }
}

在这里插入图片描述

3.IDA打开so文件

接着分析libmyjni.so文件,在JNI_OnLoad函数中注册了initSN、saveSN和work函数,代码如下:

jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
  if ( !(*vm)->GetEnv(vm, (void **)&g_env, 65542) )
  {
    j___android_log_print(2, "com.gdufs.xman", "JNI_OnLoad()");
    native_class = (*(int (__fastcall **)(int, const char *))(*(_DWORD *)g_env + 24))(g_env, "com/gdufs/xman/MyApp");
    if ( !(*(int (__fastcall **)(int, int, char **, int))(*(_DWORD *)g_env + 860))(g_env, native_class, off_5004, 3) )
    {
      j___android_log_print(2, "com.gdufs.xman", "RegisterNatives() --> nativeMethod() ok");
      return 65542;
    }
    j___android_log_print(6, "com.gdufs.xman", "RegisterNatives() --> nativeMethod() failed");
  }
  return -1;
}

双击off_5004查找注册函数
在这里插入图片描述
发现

  • initSN()
  • saveSN(String str)
  • work()

4.函数分析

initSN

首先分析initSN函数,其中setValue函数的作用是设置com/gdufs/xman/MyApp类的静态字段m的值

int __fastcall n1(int a1)
{
  int v2; // r0
  int v3; // r4
  int v4; // r0
  int v5; // r7
  const char *v6; // r5
  int v8; // r0
  int v9; // r1

  v2 = j_fopen("/sdcard/reg.dat", "r+");
  v3 = v2;
  if ( !v2 )
  {
    v4 = a1;
    return setValue(v4, 0);
  }
  j_fseek(v2, 0, 2);
  v5 = j_ftell(v3);
  v6 = (const char *)j_malloc(v5 + 1);
  if ( !v6 )
  {
    j_fclose(v3);
    v4 = a1;
    return setValue(v4, 0);
  }
  j_fseek(v3, 0, 0);
  j_fread(v6, v5, 1, v3);
  v6[v5] = 0;
  if ( !j_strcmp(v6, "EoPAoY62@ElRD") )
  {
    v8 = a1;
    v9 = 1;
  }
  else
  {
    v8 = a1;
    v9 = 0;
  }
  setValue(v8, v9);
  return j_fclose(v3);
}

initSN会读取/sdcard/reg.dat文件(这个文件默认没有),如果文件内容和字符串
“EoPAoY62@ElRD” 匹配,那么myaap.m值为1,代表已经注册。我们尝试手动修改这个
文件的值

saveSN

int __fastcall n2(int a1, int a2, int a3)
{
  int v5; // r7
  const char *v7; // r6
  const char *v8; // r5
  int v9; // r4
  int v10; // r0
  char v11; // r2
  signed int v12; // [sp+8h] [bp-38h]
  char v13[20]; // [sp+10h] [bp-30h] BYREF

  v5 = j_fopen("/sdcard/reg.dat", "w+");
  if ( !v5 )
    return j___android_log_print(3, "com.gdufs.xman", byte_2E5A);
  strcpy(v13, "W3_arE_whO_we_ARE");
  v7 = (const char *)(*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a3, 0);
  v8 = v7;
  v12 = j_strlen(v7);
  v9 = 2016;
  while ( 1 )
  {
    v10 = v8 - v7;
    if ( v8 - v7 >= v12 )
      break;
    if ( v10 % 3 == 1 )
    {
      v9 = (v9 + 5) % 16;
      v11 = v13[v9 + 1];
    }
    else if ( v10 % 3 == 2 )
    {
      v9 = (v9 + 7) % 15;
      v11 = v13[v9 + 2];
    }
    else
    {
      v9 = (v9 + 3) % 13;
      v11 = v13[v9 + 3];
    }
    *v8++ ^= v11;
  }
  j_fputs(v7, v5);
  return j_fclose(v5);
}

saveSN也只干了一件事,把输入的注册码经过异或运算之后存到了/sdcard/reg.dat文件中

直接把/sdcard/reg.dat文件的内容替换成EoPAoY62@ElRD运行app
在这里插入图片描述
我们的flag格式为xman{注册码}

5.脚本爆破

js版本爆破

代码爆破

var fputs_str = null;

function Hook() {
    Java.perform(function () {
        const imports = Module.enumerateImportsSync("libmyjni.so");
        const imports_len = imports.length;
        var fputs_addr = null;
        for (var i = 0; i < imports_len; i++) {
            if (imports[i].name == "fputs") {
                fputs_addr = imports[i].address;
                break;
            }
        }
        if (fputs_addr != null) {
            Interceptor.attach(fputs_addr, {
                onEnter: function (args) {
                    fputs_str = args[0].readCString();
                },
                onLeave: function (retval) {
                }
            })
        }
    })
}

function Invoke(try_str) {
    Java.perform(function () {
        Java.choose("com.gdufs.xman.MyApp", {
            onMatch: function (instance) {
                instance.saveSN(try_str);
            },
            onComplete: function () {
            }
        })
    })
}

function Main() {
    Hook();
    Java.perform(function () {
        const three_character_array = new Array("EoP", "AoY", "62@", "ElR");
        const last_character = "D";
        const my_dict = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()?_"
        const myapp_class_obj = Java.use("com.gdufs.xman.MyApp");
        const three_character_array_len = three_character_array.length;
        const my_dict_len = my_dict.length
        const myapp = Java.use("com.gdufs.xman.MyApp").$new();
        var flag = "";
        for (var i = 0; i < three_character_array_len; i++) {
            var found = false;
            for (var j = 0; j < my_dict_len; j++) {
                if (found == true) {
                    break;
                }
                for (var k = 0; k < my_dict_len; k++) {
                    if (found == true) {
                        break;
                    }
                    for (var m = 0; m < my_dict_len; m++) {
                        const try_str = my_dict[j] + my_dict[k] + my_dict[m];
                        console.log(`try_str: ${try_str}`);
                        myapp.saveSN(try_str);
                        if (three_character_array[i] == fputs_str) {
                            flag += try_str;
                            console.log(`found: ${try_str}`);
                            found = true;
                            break;
                        }
                    }
                }
            }
        }

        for (var i = 0; i < my_dict_len; i++) {
            const try_str = my_dict[i];
            console.log(`try_str: ${try_str}`);
            Invoke(try_str);
            if (last_character == fputs_str) {
                flag += try_str;
                console.log(`found: ${try_str}`);
                break;
            }
        }
        console.log(`flag: xman{${flag}}`);
    })
}

setTimeout(Main, 500);

rpc爆破

var myapp = null;

function Hook() {
    Java.perform(function () {
        myapp = Java.use("com.gdufs.xman.MyApp").$new();
        const imports = Module.enumerateImportsSync("libmyjni.so");
        const imports_len = imports.length;
        var fputs_addr = null;
        for (var i = 0; i < imports_len; i++) {
            if (imports[i].name == "fputs") {
                fputs_addr = imports[i].address;
                break;
            }
        }
        if (fputs_addr != null) {
            Interceptor.attach(fputs_addr, {
                onEnter: function (args) {
                    send(args[0].readCString());
                },
                onLeave: function (retval) {
                }
            })
        }
    })
}

function Invoke(try_str) {
    Java.perform(function () {
        myapp.saveSN(try_str);
    })
}

rpc.exports = {
    hook: Hook,
    invoke: Invoke
}

python爆破

from itertools import permutations
import sys
import time
import frida

result = ""
received = False

def MessageHandler(message, data):
    if message["type"] == "send":
        global result
        global received
        result = message["payload"]
        received = True
    else:
        print(message)

def GeneratePossibilities(dict, count, repetitive=False):
    """

    :param dict: Dictionary
    :param count: Number of characters per group
    :param repetitive: Whether there are duplicate characters in each group
    :return: return permutations(dict, count)
    """
    if repetitive and (count > 1):
        src_dict = dict
        for i in range(count - 1):
            dict += src_dict
    return permutations(dict, count)

device = frida.get_device_manager().add_remote_device("192.168.1.5:8888")
pid = device.spawn("com.gdufs.xman")
session = device.attach(pid)
with open("index.js") as file_descriptor:
    script = session.create_script(file_descriptor.read(), runtime="v8")
    script.on("message", MessageHandler)
    script.load()
    time.sleep(1)
    device.resume(pid)

goal_three_character_array = ["EoP", "AoY", "62@", "ElR"]
goal_three_character_array_length = len(goal_three_character_array)
goal_last_character = "D"
my_dict = "abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*()?_"
flag = ['*'] * 13
possibilities = GeneratePossibilities(my_dict, 3, True)
time.sleep(1)
time_begin = time.time()
script.exports.hook()
first_part_times = 0
try_count = 0
for p in possibilities:
    character1, character2, character3 = p
    try_str = character1 + character2 + character3
    try_count += 1
    sys.stdout.write("[{}]Try_str: {}\r".format(try_count, "".join(try_str)))
    sys.stdout.flush()
    received = False
    script.exports.invoke(try_str)
    while not received:
        pass
    for i in range(goal_three_character_array_length):
        if result == goal_three_character_array[i]:
            if flag[i * 3] == "*":
                first_part_times += 1
            flag[i * 3: i * 3 + 3] = character1, character2, character3
            break
    sys.stdout.write("
 Flag: xman{{{}}} ".format("".join(flag)))
    sys.stdout.flush()
    if first_part_times == 4:
        break
# 清屏
print("\033c")
try_count = 0
for try_str in my_dict:
    sys.stdout.write("[{}]Try_str: {}\r".format(try_count, "".join(try_str)))
    script.exports.invoke(try_str)
    if result == goal_last_character:
        flag[12] = try_str
        break
    sys.stdout.write("
 Flag: xman{{{}}} ".format("".join(flag)))
    sys.stdout.flush()
# 清屏
print("\033c")
print("
 Flag: xman{{{}}}".format("".join(flag)))
time_end = time.time()
cost_time = time_end - time_begin
print(" cost time: " + str(cost_time // 60) + "min")

得到flag:xman{201608Am!2333}


总结

  • pixel 3 android 9.0(不能用模拟器,因为被hook的libmyjni.so只有arm架构的)
  • jadx
  • IDA
  • Frida
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。