支付宝自定义消息发送

支付宝「自定义」消息发送

实际上不能发送自定义消息,只是通过修改特定类型消息的本地数据,实现类似自定义消息的效果。

支付宝内能转发的消息类型很少,文字、链接、小程序等可以转发。红包、转账卡片等类型的消息不能转发。

消息的类型由templateCode字段确定,消息内容由templateData字段确定,跳转链接由link字段确定。

支付宝消息的数据是加密的,可以通过hook的方式操作数据库。数据库路径中包含关键词chatmsgdb

每个会话都有一张表,表的结构:

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
localId
clientMsgId
createTime
bizType
bizMemo
bizIcon
templateCode
templateData
side
link
appId
msgId
egg
extendData
mediaState
errorCode
errorMemo
sendingState
loadingState
recent
countAsUnread
atMe
isResourceUploaded
action
bizRemind
isEggRead
scene
msgIndex

修改数据的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
SQLiteDatabase.rawQuery.overload('java.lang.String', '[Ljava.lang.String;').implementation = function (arg_0, arg_1) {
if(this.getPath().indexOf('chatmsgdb') > -1){
if(arg_0.indexOf('`personal_chat_2088212830461913`') > -1){
// 调试使用,不直接修改数据库中内容
// arg_0 = 'SELECT localId,clientMsgId,createTime,bizType,bizMemo,bizIcon,templateCode,templateData,side,"alipays://platformapi/startapp?appId=20000067&url=https%3A%2F%2Frender.alipay.com%2Fp%2Fc%2Falipay-red-qrcode%2Fshared.html%3Fchannel%3Dsearch_pwd%26shareId%3D2088202871367592%26token%3D11w10205jcwqoowu4idon47%26campStr%3DkPPFvOxaCL3f85TiKss2wsBZgIjulHjG%26sign%3DKyT9rULQo0hJUn2uSyjyjM%2FcjybyAKnXNiXhgaz%2FuSM%3D%26chInfo%3Dalipay_friend%26c_stype%3Dsearch_pwd&bizType=REDPACKET_NORMAL" as link,appId,msgId,egg,extendData,mediaState,errorCode,errorMemo,sendingState,loadingState,recent,countAsUnread,atMe,isResourceUploaded,action,bizRemind,isEggRead,scene,msgIndex FROM `personal_chat_2088212830461913` ORDER BY `localId` DESC LIMIT 1'

// arg_0 = 'update personal_chat_2088212830461913 set link="alipays://platformapi/startapp?appId=20000067&url=https%3A%2F%2Frender.alipay.com%2Fp%2Fc%2Falipay-red-qrcode%2Fshared.html%3Fchannel%3Dsearch_pwd%26shareId%3D2088202871367592%26token%3D11w10205jcwqoowu4idon47%26campStr%3DkPPFvOxaCL3f85TiKss2wsBZgIjulHjG%26sign%3DKyT9rULQo0hJUn2uSyjyjM%2FcjybyAKnXNiXhgaz%2FuSM%3D%26chInfo%3Dalipay_friend%26c_stype%3Dsearch_pwd&bizType=REDPACKET_NORMAL" where localId="1695536846469"; '
// arg_0 = 'update personal_chat_2088212830461913 set bizMemo="[红包]恭喜发财" where localId="1695536846469"; '
// arg_0 = 'update personal_chat_2088212830461913 set templateData=replace(templateData, "A*n0ECTr9iq9AAAAAAAAAAAAAAKfh3AQ", "A*A7ojS5Yw9eAAAAAAAAAAAAAADiR5AQ") where localId="1695536846469"; '
// arg_0 = 'update personal_chat_2088212830461913 set templateData=replace(templateData, "送出暖暖心意", "恭喜发财") where localId="1695536846469"; '
arg_0 = 'update personal_chat_2088212830461913 set templateData=replace(templateData, "小程序", " ") where localId="1695536846469"; '
}
// this.commit()
console.log(Java.use("android.util.Log").getStackTraceString(Java.use("java.lang.Exception").$new()))
var result = this.rawQuery(arg_0, arg_1);
// console.log(result.getColumnNames())
result.moveToFirst()
console.log('localId:' , result.getString((result.getColumnIndex('localId'))))
console.log('bizType:' , result.getString((result.getColumnIndex('bizType'))))
console.log('bizMemo:' , result.getString((result.getColumnIndex('bizMemo'))))
console.log('templateCode:' , result.getString((result.getColumnIndex('templateCode'))))
console.log('templateData:' , result.getString((result.getColumnIndex('templateData'))))
send(result.getString(9))
send(result.getString(13))
result.moveToFirst()
return result
}
return this.rawQuery(arg_0, arg_1)
}

最终效果:

原始消息

修改后

数据库解密

阿里系都是用的相同的数据库加密方案。

类名:com.alibaba.sqlcrypto.sqlite.SQLiteDatabase

数据的加解密是在native层做的

image-20230924184638581

使用frida获取密钥

1
2
3
4
5
6
7
8
9
10
11
12
13
Interceptor.attach(Module.findExportByName("libdatabase_sqlcrypto.so", "aes_decrypt"), {
onEnter: function (args) {
console.log(hexdump(args[2], {
offset: 0,
length: 0x10,
header: true,
ansi: true
}));
console.log("aes_decrypt...")
},
onLeave: function (retval) {
}
});

image-20230924185024564

解密数据库:

1
2
3
4
5
6
7
8
from Crypto.Cipher import AES

key = b'N4VwrNPjzQbs0WZl'
cryptos = AES.new(key, AES.MODE_ECB)
data = open('chatmsgdb2088202871367592.db', 'rb').read()
plain = cryptos.decrypt(data)
with open('database.db', 'wb') as fo:
fo.write(plain)

image-20230924185123202

参考资料

http://www.javashuo.com/article/p-rnjmxrmi-hd.html

http://www.fenlog.com/?mod=wap&act=View&id=113