wuxianyu

正常情况下,http请求是charles可以获取请求信息的。

但是,有的app可能不走网络设定的代理,比如说MM应用市场

如何确定不走代理呢,你wifi设置了charles代理,抓不到包,然后把代理软件关了,再运行app。发现app还是能正常运行,说明此app网络不走代理

此时我们需要使用app proxydroid 代理机器人,去把手机上所有的app网络强制走代理机器人设置的端口

只用设置地址和端口就可以了

 

将代理切换打开,后将wifi设置的代理关闭,再进行app的抓取,此时会发现数据请求包已经走charles了(反正我的手机没成功,显示未root)

问了王平大佬,说没必要用这个,用httpcany可以直接再手机抓包(我还不会,下次补写)

第二种就是ssl ping(单向认证),ssl认证的问题,就是app不认可charles证书,证书不通过,此时你访问app就会保网络未链接,或者抓到的包显示unknown

这时我们需要一个app 叫justtrustme ,github上有,从github上下载,安装好以后,在xposed里面勾选上,重启设备,再进行网络抓包就可以抓到数据了

(注意:这种justTrustme只适用于安卓7.0以下,安卓7。0用过之后无法正常访问浏览器,安卓7.0需要吧证书放到根目录下就可以了,文章末尾有写怎么做,)

第三种就是ssl ping(双向认证)正常情况下是单向认证,也就说客户端是不需要有CA证书的,客户端只效验从服务器发过来的证书,

如果是双向认证吗,客户端是需要有CA证书的, 并且服务器会效验客户端发过来的CA证书,此时justtruestme就不行了, 我们需要在charles倒入此app的CA证书

我们用apktool 把进行反编译,把编译后的文件打开,ca证书一般都是以.p12或者.Pem结尾,一般都放在assets资产文件目录里。

 

 我们找到此证书,打开charles

在ssl proxy setting里面 

 

 

 

选择add 

host 一般是*.appyuming.com 这种的, 代表的是要抓取 的app数据域名

port一般是443 代表https

我这里填写* *不知道对不对

此处要输入client.p12的证书密码。证书密码我们需要用frida hook技术获取,代码如下


import frida
import sys

i = 0
ext = \'\'

def on_message(message, data):
global i, ext
if (message[\'type\'] == \'send\' and \'event\' in message[\'payload\']):
if (message[\'payload\'][\'event\'] == \'+found\'):
i += 1
print("\n[+] Hooked keystore" + str(i) + "...")

elif (message[\'payload\'][\'event\'] == \'+type\'):
print(" [+] Cert Type: " + \'\'.join(message[\'payload\'][\'certType\']))
if (message[\'payload\'][\'certType\'] == \'PKCS12\'):
ext = \'.jks\'

elif (message[\'payload\'][\'event\'] == \'+pass\'):
print(" [+] Password: " + \'\'.join(message[\'payload\'][\'password\']))

elif (message[\'payload\'][\'event\'] == \'+write\'):
print(" [+] Writing to file: keystore" + str(i) + ext)
f = open(\'keystore\' + str(i) + ext, \'wb\')
f.write(bytes.fromhex(message[\'payload\'][\'cert\']))
f.close()
else:
print(message)



hook_root = """
Java.perform(
function(){
var DeviceUtils = Java.use("utils.DeviceUtils");
//DeviceUtils.isDeviceRooted.implementation = function(){
// var flg = this.isDeviceRooted();
//send("flg is:"+flg);
// return flg;
//};
});
"""


# 双向认证 打印出证书需要的密码
dy_spt = """
setTimeout(function() {
Java.perform(function () {
var keyStoreLoadStream = Java.use(\'java.security.KeyStore\')[\'load\'].overload(\'java.io.InputStream\', \'[C\');

/* following function hooks to a Keystore.load(InputStream stream, char[] password) */
keyStoreLoadStream.implementation = function(stream, charArray) {

/* sometimes this happen, I have no idea why, tho... */
if (stream == null) {
/* just to avoid interfering with app\'s flow */
this.load(stream, charArray);
return;
}

/* just to notice the client we\'ve hooked a KeyStore.load */
send({event: \'+found\'});

/* read the buffer stream to a variable */
var hexString = readStreamToHex (stream);

/* send KeyStore type to client shell */
send({event: \'+type\', certType: this.getType()});

/* send KeyStore password to client shell */
send({event: \'+pass\', password: charArray});

/* send the string representation to client shell */
send({event: \'+write\', cert: hexString});

/* call the original implementation of \'load\' */
this.load(stream, charArray);

/* no need to return anything */
}
});
},0);

/* following function reads an InputStream and returns an ASCII char representation of it */
function readStreamToHex (stream) {
var data = [];
var byteRead = stream.read();
while (byteRead != -1)
{
data.push( (\'0\' + (byteRead & 0xFF).toString(16)).slice(-2) );
/* <---------------- binary to hex ---------------> */
byteRead = stream.read();
}
stream.close();
return data.join(\'\');
}
"""

hook_open = """
Interceptor.attach(Module.findExportByName(null, "fopen"), {
onEnter: function(args) {
//console.log("Interceptor attached onEnter...");
var name = Memory.readUtf8String(args[0]);

if (name.indexOf("proc") == -1) {
send("open("+Memory.readUtf8String(args[0])+","+args[1]+")");
}
},
onLeave: function(args) {
//console.log("Interceptor attached onLeave...");
}
})"""

hook_assert="""
Java.perform(function () {
var AssetManager = Java.use("android.content.res.AssetManager");
var FileInputStream = Java.use("java.io.FileInputStream");
AssetManager.open.overload("java.lang.String").implementation = function(str) {
send("hook asset:"+str);
if(str.endsWith(".xxx")){
return FileInputStream.$new("/data/local/tmp/xxxxx");
}
return this.open(str)
}
});"""


oncreate_script = """
//打印调用堆栈
function printstack() {
send(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()));
}

//array 转成 string
function array2string(array){
var buffer = Java.array(\'byte\', array);
//console.log(buffer.length);
var result = "";
for(var i = 0; i < buffer.length; ++i){
result+= (String.fromCharCode(buffer[i]));
}
return result;
}

Java.perform(
function () {
var CertificatePinner = Java.use(\'com.squareup.okhttp.CertificatePinner\');

CertificatePinner.Builder.implementation = function (bytesarray) {
send(\'I am here 0:\');
this.Builder();
printstack();
};
}
);
"""


test_module_script = """
var walk = Process.enumerateModules();
for (var i = 0; i<walk.length; i++){
send(walk[i].name);
}

var base_address = Module.findBaseAddress("libc.so");
send(\'base_address:\'+base_address);

var mod_address = Module.findExportByName("libc.so" , "dlopen");
send(\'mod_address:\'+mod_address);


var lib_module = Process.findModuleByAddress(base_address);
send("lib_module_name:"+lib_module.name);

Interceptor.attach(mod_address, {
onEnter: function(args) {
send("open("+Memory.readUtf8String(args[0])+","+args[1]+")");
},
onLeave:function(retval){
send("retval:"+retval);
}
});
"""


# process = frida.get_usb_device().attach(\'com.iCitySuzhou.suzhou001\')
# script = process.create_script(oncreate_script)
# script.on(\'message\', on_message)
# print(\'[*] Running CTF\')
# script.load()
# sys.stdin.read()

# device = frida.get_usb_device()
# pid = device.spawn([\'com.iCitySuzhou.suzhou001\'])
# process = device.attach(pid)

# script = process.create_script(oncreate_script)
# script.on(\'message\', on_message)
# print(\'[*] Running CTF\')
# script.load()
# device.resume(pid)
# sys.stdin.read()

device = frida.get_usb_device(-1)
pid = device.spawn([\'cn.soulapp.android\'])
process = device.attach(pid)

script = process.create_script(dy_spt)
script.on(\'message\', on_message)
print(\'[*] Running CTF\')
script.load()
device.resume(pid)
sys.stdin.read()

这是打印的结果

 

把password填写进去就好了

这步导入证书让服务端信任证书,我们还需要让客户端信任证书,老样子,justtrustme 一起作用,再打开app进行抓包 就可以了(因app损坏,未测试,此处无图)

注意事项:
1.安卓7.0(包括7.0)以后,安卓系统不再信任用户安装的证书。(例如 fiddler/charles的代理抓包证书) mumu
模拟器
解决7.0以后用户证书不信任问题两种方法:
(1)root手机,把代理证书放到系统证书根目录下
用户CA证书目录
/data/misc/user/0/cacerts-added
系统CA证书目录
/system/etc/security/cacerts
问题:
有的手机root /system 分区是只能读 仍然不能把证书放到系统根目录, 比如魅族pro5
df 命令
mount -o rw,remount /system
(2) hook 系统方法,强行让系统信任用户证书:
比如:使用xposed框架的justtrustme模块 和 frida的DroidSSLUnpinning
tricks:
1.优先使用安卓系统低版本抓包
优先使用安卓4.0 5.0 6.0抓包
2.优先使用APP低版本抓包
比如微信7.0以下 在安卓4.0-6.0上 容易抓包微信小程序
优先使用能用的APP最低版本
猿人学
APP历史版本:
https://wap.pp.cn/
apkpure.com
当安卓抓包很艰难时,试试iOS抓包
3.工欲善其事必先利其器
工具准备充分fiddler/charles/wirseshark/模拟器4.0 5.0 6.0/root了的安卓手机 4.0 5.0 6.0 7.0/苹果手机
猿人学

 参考猿人学

分类:

技术点:

相关文章: