APP爬虫初阶课程笔记(上)
此笔记内容全部来源于r0ysue大佬的《APP爬虫入门课程》,干货满满,内容硬核详实,我的笔记只是自己学后的心得体会,实际课程内容远多于此,建议大家感兴趣的都去找r0ysue买一个vip会员,你值得拥有~
101 环境
虚拟机配置
用虚拟机的原因
- 不会破坏主机
- 拍快照,试错成本低
- 重新解压虚拟机,获得纯净环境
修改系统时间
dpkg-reconfigure tzdata
设置鼠标滚轮
安装工具
- jnettop、htop
- QtScrcpy
- wifiadb
- genymotion&translation
- kali-linux
- termux
Android Studio
-
第一次同步很慢,可以设置代理
-
只建议在linux上编译安卓程序
-
platform目录下自带adb,只需加入系统路径即可
wifi感叹号问题
- “已连接,但无法访问互联网”开机后wifi有感叹号,部分网站无法访问
在手机的shell里以root用户执行:
#settings put global captive_portal_http_url https://www.google.cn/generate_204
#settings put global captive_portal_https_url https://www.google.cn/generate_204
#settings put global ntp_server 1.hk.pool.ntp.org
#reboot
后续只要把时区调对,时间会自动同步的。
书籍
- 《第一行代码》
- 《零基础学安卓》
- 《精彩编程200例》
- 《安卓项目开发实战入门》
- 《疯狂安卓讲义》(难度高)
- 《安卓应用防护和安全分析》
102 基础知识
开头
- APP加载过程
- so加载过程
- APP四大组件
- 安卓系统架构
终端命令
- 安卓本质上是linux系统
neofetch
查看系统信息net hunter
进入安卓的kali子系统ps -e
ps -T -p + 进程号
:查看线程netstat -tunlp|grep 7001
:查看端口对应的进程号netstat -alpe |grep -i https/22
:查看当前正在运行的进程tree -Nucfh
树状排列- 在termux中通过
pkg install htop
安装htop命令
adb命令
-
adb shell
-
adb connect ip
-
adb devices
-
adb -s ip:port shell
-
adb shell dumpsys activity top
-
adb input
-
adb logcat
-
adb shell screencap
:截屏
termux
- 宝藏APP,为安卓系统提供了一系列命令
/data/data/com.termux/files/usr/bin
- 需要赋予APP应用存储权限
Genymotion
- 精简版的安卓系统,删了很多库
103 刷机
pixel2刷机
刷机准备
- lineage zip
- twrp img
- magisk zip(github上下的是APK,需要把后缀改为zip)
刷机步骤
-
首先需要一个底包,这里我用的出厂自带的google官方系统,没有重新刷入
-
手机上打开usb调试,关闭屏幕超时锁屏,打开OEM锁
-
手机完全关机,按住向下键重启(或者通过
adb reboot bootloader
命令),进入bootloader,注意要确保bl锁处于unlocked状态 -
当bootloader屏幕上显示Start时,用下面命令刷入twrp,很快(fastboot和adb都在Android Studio的Sdk工具包里)
fastboot flash recovery recovery.img
但这个命令在pixel2手机上执行会报错,因为pixel2手机是ab分区,没有独立的recovery分区,只能刷到boot分区中,所以将命令改为
fastboot flash boot recovery.img
-
刷完后手机重启,进入bootloader,按两下向下键,选择进入recovery模式,选择Advanced->adb sideload,用adb刷入lineage包,时间比较长,屏幕上会有红色报错,不用管
adb sideload lineage-signed.zip
-
用adb将magisk.zip push到sdcard目录下,然后进入recovery模式的install选项,选择安装magisk,安装完后重启设备
- 解决红色报错的办法:安装包时把usb线拔掉,防止自动挂载system分区
-
重启进入lineage os后,安装magisk.apk,至此手机成功获得root权限
104/105 动静态分析
动静态分析
静态
- jadx(稍弱)/jeb/gda轮换着看
- 得到包下面的类,名字以内存里的为主,在内存中确认有没有这个类
动态
-
frida server+objection
-
调系统库用objection,快速定位
-
定位到非常小的范围内,定位算法才用smali
-
hook toString
命令
frida server启动
frida-server -l 0000:8888
objection启动
objection -N -h host -p port -g package explore
objection查看日志
cat .objection/objection.log | grep com.example.demo10
pyenv切换
-
用pyenv切换下环境
pyenv local 版本号
wallbreaker插件
# 加载
-P ~/.objection/Plugins
# 使用
Plugin wallbreak classdump
查看端口
netstat -aple
netstat -tuulp
lsof -p
ps -e
106 自动化分析和定位
objection免root调试
-
用objection重打包APK,将frida-gadget.so注入APK中
proxychains4 objection patchapk --architecture armeabi-v7a --use-aapt2 --source hello2.apk
-
再用objection连接frida
objection explorer
objection内存漫游
# 内存搜索模块
memory list modules
# 内存搜索导出库
memory list exports so库
# 在堆上查找类的实例
android heap search instances bitmap类
# 主动调用类的方法,在google devloper上找类方法
android heap execute hash getHeight --return-string
# 调用带参的方法
objection组件控制
# 查看任务
jobs list
# 切换activity
android intent launch_activity 类名
objection远程调用
-
objection内置flask,提供rpc调用接口,可直接使用curl控制手机
-
objection上开发flask端口
objection --api-port 8000 explore --enable-api
-
curl远程调用api
curl -s "http://127.0.0.1:8888/rpc/invoke/androidHookingListActivities"
-
可上传执行脚本,可调用带参函数,更多例子见rpc接口
objection脱壳
-
无法对加壳的程序重打包,必须要root才能启frida
-
多运行一会,等待APP进入自己的主程序
-
将dexdump放到plugin目录下
git clone https://github.com/hluwa/frida-dexdump ~/.objection/plugins
-
启用脱壳
# 加载 -P ~/.objection/Plugins # 使用 plugin dexdump dump
107 去升级重打包
wallbreaker搜索类
# 搜索类实例
plugin wallbreaker objectsearch 类名
# 打印实例信息
plugin wallbreaker objectdump 实例编号
定位升级弹窗的方法
- 搜索字符串不科学
- 用动态调试的方法,hook弹窗的api,打印调用栈,找到主程序中弹窗的代码逻辑
objection启动时hook
-
frida有两种模式:spawn和attach
-
objection启动时如果没有找到类名,则以spawn模式启动,通知Zygote孵化进程,如果找到类名则attach
-
加上参数,用来一启动即hook
--startup-command "YOUR CMD"
apktool打包解包
# 解包
apktool d apk
# 打包
apktool b apk
自签名
- 安卓系统对签名的校验很弱
keytool生成证书
keytool -genkey -alias hello -keyalg RSA -validity 20000 -keystore hello.keystore
jarsigner自签名
jarsigner -verbose -keystore hello.keystore -signedjar demo_signed.apk demo.apk hello.keystore
安卓包管理命令
am
pm
108 带壳去升级重打包
三代壳
- 一代壳:整体性的壳,用工具可脱完全
- 二代壳:函数式的壳,脱完后只能看到函数名,没有函数体
- 三代壳:vmp、ollvm
APK安全性校验
- 大部分APK都带安全性校验,重打包、脱壳后APK会失效
判断函数能否被hook
# 把所有类中能被hook的打印出来
android hooking list classess
如何选择需要objection注入的类
- 通过top查看活跃进程
- 通过
frida-ps -U
查看活跃进程
dexdump脱壳的三种模式
objection内启用插件
dexdump插件目录下运行main.py
- 先运行frida-server
- 再运行main.py,会自动判断前台进程,对前台进程内存进行扫描dump
frida-dexdump
pip install frida-dexdump
frida-dexdump -U -f pkgname
脱壳后如何找到包含mainactivity的dex文件
du -h *
选最大的文件grep -rn -i MainActivity
搜索包含的文件
定位目标函数方法
-
先尝试hook弹窗的类,定位到dialog类
-
所见即所得:点击弹窗周围无用,猜测调用了setConceled API
-
hook函数,证实调用,打出调用栈,找到程序中调用的逻辑
-
修改原有逻辑,重打包
Android弹窗API
- 查看android develop官网
PopupWindow类
dialog类
AlertDialog类
新建window
新建Activity
APK带壳重打包的方法
dexump去壳
- 将生成的dex文件从大到小命名为
classes.dex, classes2.dex, classess3.dex ...
apktool解包
# 保留dex解包
apktool -s -d packagename
# 将去壳的dex全部替换到解包的dex文件
apktool封包并签名
- 签名后运行APK观察是否正常
apktool解包,修改dex文件
apktool封包并签名
109 APP加固
安卓原生和linux原生
- 所谓原生程序,指更接近接近解释器
- 安卓原生就是libart.so来解释、linux原生就是CPU直接解释
加固技术发展
-
静态混淆
-
动态加载
- 安卓动态加载dex:链接
-
native加固
- 使用libsecshell.so等
-
核心数据、功能云端化
-
反调试、反反编译
加固技术手段
一代整体壳
- 文件落地加载、不落地加载
二代代码抽取
- 对抗:dexdump主动加载
- 对抗:FART
dex保护
-
dex vmp虚拟机保护:自编译一个dalvik解释器,执行效率很低
-
java2c:用NDK编写应用
native保护
-
ollvm:通过编写
PASS
来控制中间代码以达到混淆的目的 -
自定义elf格式,用专门的linker加载so
-
段加解密
动态调试对抗类名、函数名混淆
- 静态搜索关键字
- 通过objection查看类的结构和属性,进行确认
201 Xposed介绍
Xposed和Frida对比
- xposed直接替换art虚拟机,更持久和稳定,但只能hook java层
- frida本质是ptrace,注入到进程中,能hook so层
Xposed衍生品
- edXposed
- virtual app
- 平头哥
- 太极
Xposed安装
- 先root,推荐SuperSU
- apk(需要科学)或者zip安装
- 下载插件
- gravitybox
- Xdebuggable
- justtrustme
202 手调Smali
Android Studio带源码调试
Jeb无源码调试
快捷键
Java和Smali间切换
- tab键
断点
- ctrl+b
交叉引用
- x
类方法搜索
- 选择环绕搜索
无源码调试
- 定位包和类
# 搜索包
pm list packages | grep 包名
# 搜索类
dumpsys activity top grep 包名
- 调试模式启动APP,并停留在启动入口
adb shell am start -D -n 包名 ./.mianactivity
-
jeb拖入APK,选择debug->start,附加到APP进程
-
改局部变量类型,辅助分析
debug模式
debug模式和普通模式的区别
- 区别在于启动过程,能否在创建界面时调试,即能否附加onCreate
若APK不是debug模式
- 法一:重打包,改mainfest为debug,不一定成功
- 法二:用xposed的xdebuggable插件,hook系统对debug的校验
其他Jeb相关
改局部变量类型,辅助分析
-
$1是内部类
-
objdump不包含基类方法和属性,jeb看到的是全的
-
apktool分割成多个单独的smali类
smali和frida调试的选择
Smali调试的场景:小项目、抠算法细节、全是本地变量、变形的标准算法
Frida调试的场景:大项目、快速定位、各种执行流、API过滤和trace
DDMS
- Android 开发环境中的Dalvik虚拟机调试监控服务
An error has occurred.See the log file错误
- as自带jdk很老,做个软链接过去
ln -s /root/Desktop/android-studio/jre /root/Android/Sdk/tools/lib/monitor-x86_64
./monitor
辅助增强工具
- 监控线程和进程、内存情况
dumpview
- 获取组件类及ID,帮助frida定位
view的继承关系
methodprofile
- 函数trace记录
203 Frida开发和调试
模拟器vs真机
- 模拟器是精简版系统,很多api都没有,没有so层
- 利用的时候可以用模拟器,逆向分析和抓包的时候用真机
Frida环境
- BlissRoms给N5安装android 10
pyenv安装
0. 首先请确保系统已经是root用户、并且默认shell切换为了bash: https://t.zsxq.com/UFa6my3
1. git clone GitHub - pyenv/pyenv: Simple Python version manage... ~/.pyenv
2. 在~/.bashrc文件的最后加上:
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init --path)"
eval "$(pyenv init -)"
4. sudo apt-get update; sudo apt-get install make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev
5. 重启(必须)
6.pyenv install 版本号
frida安装
# 指定安装frida版本
pip install frida==12.8.0
# 上frida github查看对应tools版本
pip install frida-tools==
# 上pypi objection,找frida发布后最近一次的objection
pip install objection==
frida常用命令
# 查看前台应用
frida -H host:port -F
frida开发
js开发
1. git clone https://github.com/oleavr/frida-agent-example.git
2. cd frida-agent-example/
3. npm install
4. 使用VSCode等IDE打开此工程,在agent下编写typescript,会有智能提示。
5. npm run watch会监控代码修改自动编译生成js文件
6. frida -U -f com.example.android --no-pause -l _agent.js
python开发
- 调用frida接口
204 抓包环境配置及原理
抓包环境配置
- 物理机和手机连接同一wifi
- 虚拟机设置桥接网卡(不复制网络),一定要桥接wifi网卡
wifi http代理抓包
- wifi设置代理
- 应用层抓包,容易被api检测
vpn代理抓包
- 网络层抓包,位于路由表第一条,所有流量都走vpn虚拟网卡
postern全局代理配置
- 设置代理服务器ip和端口
- 配置代理规则
- 开启vpn
burp和charless抓包
- 应用层抓包的核心原理是中间人代理
- 设置代理服务器
抓http和https
- ssl是会话层,http可以放到ssl中
- 关闭ssl:不解密抓包,可以抓到http明文和https的密文
- 打开ssl:尝试解密,此时未放入charless签发证书,抓https会失败
205 https抓包实战
http的危险性
-
明文可能被窃听
-
通信方可能伪装
- 运营商劫持http包,插入广告信息
- 运营商劫持dns,返回错误结果GTW
-
报文可能被篡改
- IOT渗透测试:升级包覆盖原有固件,留下后门
- 就算做了签名,签名本身也可能被篡改
网页:客户端校验服务器
app:还有服务器校验客户端:将app的证书导入到charless,伪装成客户端
https=http+加密+认证+完整性保护
ssl
- https是披着ssl外衣的http
- 刚开始的认证阶段速度慢,后续可以保持一段时间用session key通信
- ssl提供加密和证书
证书机制
- https://zhuanlan.zhihu.com/p/58955297
- openssl自签名证书
混合加密机制
- 先用证书信任的公开密钥进行加密通信,再用共享密钥session key
- https://zhuanlan.zhihu.com/p/58955297
双向绑定
-
https还提供客户端证书进行校验,web中很少,常见于app中
-
安全性极高的业务可采用客户端证书认证,缴纳费用,u盾
ssl pinning
- 极少见,再代码中对证书再进行校验
charless抓https包
-
插入中间人,分割成两端独立的https通信,需要证书保持三者的session key一致,才能通信
-
charless开启代理服务,socks比http/https更强
-
手机连接代理
手机导入charless证书
- 手机浏览器输入
Charles
的证书下载地址chls.pro/ssl
- 下载证书并安装到手机中
- 将用户证书安装到系统目录中
cd /data/misc/user/0/cacerts-added
mount -o remount,rw /
cp * /etc/security/cacerts/
mount -o remount,ro /
- 重启
charless添加app证书
-
两种方法获取app证书文件
- 解包apk,搜索p12结尾的文件
- hook打开文件的api,找到文件路径
-
获取证书解密公钥
- frida hook脚本,不同框架使用不同的api
-
将证书文件和公钥添加到charless中
抓国外APP的科学方法
- charless选择外部代理,连接科学链路
206 基于hook的抓包
缺点:不如抓包软件全面
优点:无视证书、基于HOOK直接得到参数、打调用栈得到参数的构成和来源
hook抓包的步骤
- 通过objection打印所有类
- 在objection目录下cp日志
- grep搜索常用安卓网络通信方法类,猜测可能用到的框架
- httpurlconnection
- okhttp
- hook网络方法类定位请求地址与参数
frida批量hook的方法
objection批量hook
- objection打印所有类
- excel生成批量语句,按照制表符分割导出为txt
- objection -c txt批量hook
zentracer批量hook
- 基于pyqt和frida
记录trace的工具
- zentracer
- ddms:用于录制函数调用,不推荐
- android开头的类属于安卓系统提供的java库,也可以引入第三方java库,都被打包到了dex文件中
推荐的hook抓包工具
OkHttpLogger
- 用于抓OkHttp框架的java API
ssl_logger
-
hook libssl的read和write,在加密前和解密后获取明文数据
-
tcpdump抓的是加密过的流量
-
抓取日志中搜索
grep - 100 phone_num
207 FRIDA基本使用
FRIDA的两种操作模式
命令行
- REPL交互式控制台界面
# HelloWorld.js
# 到java虚拟机中打印helloworld
Java.perform(function(){
console.log("HelloWorld!")
})
# perform和performnow的区别
虚拟机起来,类加载器还没起来就执行的是performnow
# USB、前台进程
frida -UF -l HelloWorld.js
# 使用es语法
es6的箭头表达式要加--runtime=v8
# 查看手机的执行引擎
Script.runtime
RPC
- 通过python脚本调用frida的方法
- objection本质是一个python rpc
import frida
#device = frida.get_device_manager().add_remote_device("")
device = frida.get_usb_device()
print(device.get_frontmost_application())
pid = device.get_frontmost_application().pid
session = device.attach(pid)
with open("HelloWorld.js") as f:
script = session.create_script(f.read())
script.load()
input()
FRIDA操作APP的两种模式
attach
- 可以指定pid
- attach前台进程
- 用于有壳的APP
spawn
-
frida -U -f 包名
-
spawn时可能会遇到对象未创建的问题
-
--no-pause
恢复主线程执行,或者手动%resume
FRIDA三板斧
- 先hook、看参数和返回值:定位:命令行
- 再构造参数、主动调用:利用:命令行
- 最后配RPC导出结果:规模化利用:PYTHON
- hook类、方法、返回值和参数,不能hook语句
- frida的核心是反射
java类加载器和反射
- 正常加载:第一次使用该类,new一个对象就可以分为两个过程:加载并初始化类和创建对象。
- 反射机制:Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。本质是JVM得到class对象之后,再通过class对象进行反编译,从而获取对象的各种信息。
- 动态加载(热加载):Java属于先编译再运行的语言,程序中对象的类型在编译期就确定下来了,而当程序在运行时可能需要动态加载某些类,这些类因为之前用不到,所以没有被加载到JVM。通过反射,可以在运行时动态地创建对象并调用其属性,不需要提前在编译期知道运行的对象是谁。
- 反射的应用场合:类的名称放在XML文件中,属性和属性值放在XML文件中,需要在运行时读取XML文件,动态获取类的信息
208 FRIDA hook
objection定位
-
用于定位,所见即所得
-
确定类是否被加载、对象是否被创建、方法是否被调用,再挂frida脚本
Frida hook
- frida可以热部署,js保存后立即生效
- 打印调用栈,参数和返回值
function main(){
Java.perform(function(){ //只要是java代码都要跑在perform里
Java.use("类名").dip2px.implementation = function(args){ 替换原函数的实现
var result = this.dip2px(args) //主动调用原函数
console.log("args==>", args) //打印参数
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Throwable").$new())); //打调用栈
return result
}
}
}
setImmediate(main)
- 修改参数、返回值,注意重载函数需要用overload标明哪一个
function main(){
Java.perform(function(){
Java.use("类名").setText.overload("java.lang.CharSequence").implementation = function(args){
# js字符串和java字符串要转换
var javaString = Java.use("java.lang.String")
var newString = javaString.$new("z5onk0")
var result = this.setText(newString);
return result
}
}
}
setImmediate(main)
objection和frida联合调试
-
可以同时用,但不能同时hook函数
-
objection主动调用,动态方法要找到实例
android heap search instances 类名
android heap execute hash 方法名
- frida hook自吐,此时的调用栈会不一样
hook的时候两个原则:
离数据越近越好
离动作越近越好
hook用不上Java.choose的
hook函数时不分动静态
hook不上的问题
- 对于大的基础库,hook可能会崩溃
- 换进程,换安卓系统
查看APP崩溃日志
- logcat | grep 进程名
混淆后的hook
- 方法名直接复制黏贴、先编码再解码
- 参考
209 FRIDA主动调用和批量自动化
主动调用
访问静态域静态方法
-
静态域和静态方法可以直接在Java.use后访问
Java.use("类名").静态变量名.value Java.use("类名").静态方法名(参数)
-
android.os.Build用来改机,改序列号,改系统名,手机信息,从设置APP里进去权限较高
访问动态域动态方法
-
动态域动态方法需要通过Java.choose枚举实例后访问
Java.choose("类名", { onMatch:function(instance) { console.log("instace text is ==>", instance.text.value); instance.clear("123"); }, onComplete:function(){ console.log("Search Complete!") } }
-
或者通过new构造一个实例来访问
-
实例方法内存中唯一份,hook时不用Java.choose枚举
模拟点击
- 调用安卓系统的shell命令
# input是shell命令,keyevent是模拟按键
function subCommand(){
Java.perform(function(){
var process = Java.use("java.lang.Runtime").getRuntime().exec("input keyevent 25")
console.log("subcommand success process is :", process)
})
}
挂脚本
- js中导出rpc函数
rpc.exports = {
subcommand: subCommand
}
- python中调用rpc函数
command = ""
while 1 == 1:
command = input("Enter command:\n1: Exit\n2: Call secret function\nchoice:")
if command == "1":
break
elif command == "2":
script.exports.subcommand()
重启应用
pid = device.spawn(["包名"])
device.resume(pid)
time.sleep(1)
session = device.attach(pid)
RPC多机器批量调用
- frida通过网络连接多台机器,挂python脚本rpc
- 在数组中枚举ip、script
远控内网机器
-
用nps做内网穿透,将frida_server端口映射到vps,python链接vps端口实现远控
-
只需在一台内网机器上部署nps客户端,可以搭建多条隧道,映射多台内网机器
打造4g代理
-
手机通过4g/5g信号与基站的交换机进行数据传输
-
内网手机上开启http/socks代理
-
将内网手机的代理端口映射到vps外网端口
-
通过requests/scrapy访问该vps端口