好友
阅读权限 20
听众
最后登录 1970-1-1
red1y
发表于 2022-9-24 21:29
本文包括
说明:这个缓存文件的解密很简单,本文主要是为大家提供定位关键逻辑代码的一种思路
一、Java层定位缓存文件加载逻辑
前置工作
这个apk很大,有19个dex文件,往jeb里加载之前要把jeb的可用内存设置的大一些,我设置了8G
从启动Activity大致阅览一些,发现这个发布版包含了大量log日志记录的代码,并没有剔除
因此可以使用查看应用log的方式定位我们所关注的逻辑代码
LogUtil.i("SplashBaseActivity", "isDexActivityInRoot: id=" + v4 + ",numOfActivityes=" + v5 + ",topActivity=" + v6 + ",baseActivity=" + v1_2);
LogUtil.i("SplashBaseActivity", "isDexActivityInRoot: rootActivity is special activity");
LogUtil.e("SplashBaseActivity", "unexpected intent.");
LogUtil.i("SplashBaseActivity", "relogin tag = " + v2_1);
查看应用日志
打开DDMS
,在安装AndroidStudio
的时候会安装在SDK
的tools
文件夹下,一个monitor.bat
的脚本
连接手机,运行app,查看应用进程号adb shell ps | findstr com.tencxxxt.kxxx
,后面的是app的包名,linux
使用grep
过滤
在DDMS
的logcat
窗口新建过滤器,根据进程pid
过滤,此时已经可以看到app的运行日志了
观察缓存文件加载逻辑
先播放一个音乐,音乐加载完成后就会在本地生成一个缓存文件
把网络断掉,清空日志
从新播放刚才的音乐
此时加载缓存文件的逻辑已经在日志中记录下来了
截取的部分日志
09-24 17:44:37.433: I/RefactorDetailInfoController(10385): [, , 0]:[UI]QueryPayTaskStatusReq error:-1 msg:网络不可用, 请检查网络设置
09-24 17:44:37.433: I/FeedAudioOperateController(10385): [, , 0]:[UI]mFeedPlayListener notifyHideLoading
09-24 17:44:37.433: I/FeedAudioOperateView(10385): [, , 0]:[UI]hideLyricPage
09-24 17:44:37.433: I/FeedAudioOperateController(10385): [, , 0]:[UI]mFeedPlayListener notifyHideLoading
09-24 17:44:37.434: I/FeedAudioOperateView(10385): [, , 0]:[UI]hideLyricPage
09-24 17:44:37.434: I/FeedMediaController(10385): [, , 0]:[UI]notifyPlaySongListChange actionType = [1]
09-24 17:44:37.434: I/FeedMediaController(10385): [, , 0]:[UI]setCurrentPlay null
09-24 17:44:37.434: I/WifiDialogUtil(10385): [, , 0]:[UI]call closeNoWifiDialog function
09-24 17:44:37.435: I/MusicPlayer(10385): [, , 0]:[UI][MusicPlayerUtils] 无网络,忽略 ugc 获取作品信息步骤
09-24 17:44:37.435: I/PlayManager(10385): [, , 0]:[UI]online song 忽然之间
09-24 17:44:37.435: I/PlaySongInfoDbService(10385): [, , 0]:updatePlaySongList
09-24 17:44:37.444: I/OpusMemCache(10385): [, , 0]:[UI]addMemCache, info: OpusCacheInfo{path='/storage/emulated/0/Android/data/com.tencent.karaoke/files/opus/-1991663251', bitrateLevel=48, vid='1021_e5e7eaee8e3718ba547e47ef80f9cdfd7c81bfac', cacheKey='1021_e5e7eaee8e3718ba547e47ef80f9cdfd7c81bfac_0'}
09-24 17:44:37.444: I/PlayManager(10385): [, , 0]:[UI]can PlayOffline
09-24 17:44:37.445: I/KaraPlayerService(10385): [, , 0]:[UI]updateCurrentPlaySong 18819739_1531398846_805
09-24 17:44:37.445: I/OpusMemCache(10385): [, , 0]:[UI]addMemCache, info: OpusCacheInfo{path='/storage/emulated/0/Android/data/com.tencent.karaoke/files/opus/-1991663251', bitrateLevel=48, vid='1021_e5e7eaee8e3718ba547e47ef80f9cdfd7c81bfac', cacheKey='1021_e5e7eaee8e3718ba547e47ef80f9cdfd7c81bfac_0'}
09-24 17:44:37.445: I/PlayManager(10385): [, , 0]:[UI]online song 忽然之间
09-24 17:44:37.446: I/OpusMemCache(10385): [, , 0]:[UI]addMemCache, info: OpusCacheInfo{path='/storage/emulated/0/Android/data/com.tencent.karaoke/files/opus/-1991663251', bitrateLevel=48, vid='1021_e5e7eaee8e3718ba547e47ef80f9cdfd7c81bfac', cacheKey='1021_e5e7eaee8e3718ba547e47ef80f9cdfd7c81bfac_0'}
09-24 17:44:37.446: I/PlayManager(10385): [, , 0]:[UI]can PlayOffline
09-24 17:44:37.446: I/KaraPlayerService(10385): [, , 0]:[UI]playSong can startPlay
09-24 17:44:37.447: I/lib_player:PlayProxy(10385): [, , 0]:[UI]setAudioProcesser: audioProcesser = h.w.n.j.u0.v.y0.d@84c18b
09-24 17:44:37.447: I/lib_player:PlayProxy(10385): [, , 0]:[UI]setAudioProcesser: audioProcesser = h.w.n.j.u0.v.y0.g@800f268
09-24 17:44:37.447: I/lib_player:PlayProxy(10385): [, , 0]:[UI]setTimeOut: timeOut = 5000
09-24 17:44:37.447: I/lib_player:ExoPlayerBuilder(10385): [, , 0]:[UI]setBufferSize: set buffer size 1000
09-24 17:44:37.447: I/lib_player:PlayProxy(10385): [, , 0]:[UI]buildPlayer: useSpeedLimit = false
09-24 17:44:37.447: I/lib_player:ExoPlayerBuilder(10385): [, , 0]:[UI]buildPlayer: fromTag is 12
09-24 17:44:37.448: I/lib_player:DefaultRenderersFactory(10385): Loaded Libgav1VideoRenderer.
09-24 17:44:37.451: I/lib_player:ExoPlayerImpl(10385): Init 6b31681 [ExoPlayerLib/2.16.1] [platina, MI 8 Lite, Xiaomi, 29]
09-24 17:44:37.451: I/lib_player:ExoPlayerImplInternal(10385): [, , 0]:[UI]init h.j.a.a.u1@3e21326 : create player PB 12
09-24 17:44:37.452: D/AudioManager(10385): getStreamVolume isRestricted mode = 0
09-24 17:44:37.455: I/lib_player:PlayProxy(10385): [, , 0]:[UI]setAudioStreamType: streamtype = 3
09-24 17:44:37.455: I/lib_player(10385): [, , 0]:[UI]audioAttributes [eventTime=0.00, mediaPos=0.00, window=0, 2,0,1,1]
09-24 17:44:37.455: I/KaraProxyPlayer:5f59b5a(10385): [, , 0]:[UI]createPlayer: playerType EXOPLAYER Player :526cd14
09-24 17:44:37.455: I/MusicPlayer(10385): [, , 0]:[UI][MusicPlayerStateLiveData] music player state change to 2(PREPARING)
09-24 17:44:37.455: I/KaraProxyPlayer:5f59b5a(10385): [, , 0]:[UI]initPlayer Player : 526cd14
09-24 17:44:37.455: I/KaraProxyPlayer:5f59b5a(10385): vid = [1021_e5e7eaee8e3718ba547e47ef80f9cdfd7c81bfac], playScene = [1], bitrateLevel = [48], hasEncrypted = [false], ugcId = [18819739_1531398846_805], sha1sum = [], ugcLoudness = [1.0], useSuperSound = [true], fmtID = [-1], cacheKey = [null], ktvDataInPlayer = [null], recyBufferSize = [0]
09-24 17:44:37.456: I/OpusMemCache(10385): [, , 0]:[UI]addMemCache, info: OpusCacheInfo{path='/storage/emulated/0/Android/data/com.tencent.karaoke/files/opus/-1991663251', bitrateLevel=48, vid='1021_e5e7eaee8e3718ba547e47ef80f9cdfd7c81bfac', cacheKey='1021_e5e7eaee8e3718ba547e47ef80f9cdfd7c81bfac_0'}
09-24 17:44:37.456: I/KaraProxyPlayer:5f59b5a(10385): [, , 0]:[UI]initPlayer: 业务没有传入验证的sha1值,不做验证,直接播放缓存
09-24 17:44:37.456: I/lib_player:PlayProxy(10385): [, , 0]:[UI]setHasEncrypted: true
09-24 17:44:37.456: I/lib_player:PlayProxy(10385): [, , 0]:[UI]setDataSource: filePath = /storage/emulated/0/Android/data/com.tencent.karaoke/files/opus/-1991663251
09-24 17:44:37.456: I/lib_player:PlayProxy(10385): [, , 0]:[UI]setWakeMode: context = com.tencent.karaoke.KaraokeApplication@d2a531c, mode = 1
09-24 17:44:37.457: I/lib_player:PlayProxy(10385): [, , 0]:[UI]prepareAsync
09-24 17:44:37.457: I/lib_player(10385): [, , 0]:[UI]timeline [eventTime=0.00, mediaPos=0.00, window=0, periodCount=1, windowCount=1, reason=PLAYLIST_CHANGED
09-24 17:44:37.457: I/lib_player(10385): [, , 0]:[UI] period [?]
09-24 17:44:37.457: I/lib_player(10385): [, , 0]:[UI] window [?, seekable=false, dynamic=true]
09-24 17:44:37.457: I/lib_player(10385): [, , 0]:[UI]]
09-24 17:44:37.457: I/lib_player(10385): [, , 0]:[UI]mediaItem [eventTime=0.00, mediaPos=0.00, window=0, reason=PLAYLIST_CHANGED]
09-24 17:44:37.458: I/lib_player(10385): [, , 0]:[UI]state [eventTime=0.00, mediaPos=0.00, window=0, BUFFERING]
09-24 17:44:37.458: I/GlobalPlaySongManager(10385): [, , 0]:[UI]refreshPlaySongListAfterStartPlay -> mPlayingSongIdentif
09-24 17:44:37.458: I/MusicPlayer(10385): [, , 0]:[UI][MusicPlayer] 正在播放: UGC作品 忽然之间(18819739_1531398846_805), 下一首: UGC作品 横冲直撞(326668413_1634048914_780)
09-24 17:44:37.459: I/DetailDataManager(10385): [, , 0]:[UI]loadVideoSizeFromUgcInfo: stream=0-0, norma=0-0
09-24 17:44:37.459: I/DetailDataManager(10385): [, , 0]:[UI]loadVideoSizeFromUgcInfo cancel with empty
09-24 17:44:37.459: I/RefactorPlayController(10385): [, , 0]:[UI]adjustVideoViewLayoutOnUiThread:videoHeight=1, videoWidth=1, isEffectTemplateShow=false
定位java代码
从日志中找出自己认为比较关键的信息,然后在jeb里查找对应的
我这里选在initPlayer Player :
这一条,即初始化播放器
搜索这个字符串,定位到相关代码处
往下阅读处理流程可以看到出现了解密成功
、解密失败等字样
跟进前面的判断函数
成功定位到关键的加密类
二、Hook加解密函数观察参数
这一步的目的是查看各函数的参数,因为目的是解密缓存文件,因此这里只关注解密函数
可以看到decrypt
函数有三个重载,其中一个为native
,另外两个参数个数不同;
private native int decrypt(int arg1, ByteBuffer arg2, int arg3);
public int decrypt(int arg6, byte[] arg7, int arg8);
public int decrypt(int arg6, byte[] arg7, int arg8, int arg9);
两个Java
层的函数最终也都调用了native
的函数
可以自己hook
一下java
层的函数,看一下最终调用的是哪一个,以及参数特征
我这里测试,调用的是四个参数的decrypt
,之后hook
一下native
的函数,看一下传给native
的参数,ByteBuffer
不会打印kkk
[decrypt]
arg1: 0
arg3: 8
[decrypt]
arg1: 8
arg3: 8
[decrypt]
arg1: 16
arg3: 8
[decrypt]
arg1: 24
arg3: 4
[decrypt]
arg1: 28
arg3: 8
[decrypt]
arg1: 36
arg3: 8
[decrypt]
arg1: 44
arg3: 8
[decrypt]
arg1: 52
arg3: 8
[decrypt]
arg1: 60
arg3: 8
[decrypt]
arg1: 68
arg3: 8
观察可以得出结论,这个类似一个流加解密,每次传给native
层8
(除个别外)个字节解密,其他的参数是一些记录偏移的
三、分析还原Native层解密算法
IDA
看一下对应的函数,这个就很简单,根据算出来的偏移取密码表里的字节和原始字节异或就得到了明文
__int64 __fastcall Java_com_tencent_karaoke_audiobasesdk_KaraMediaCrypto_decrypt(__int64 a1, __int64 a2, int start, __int64 a4, int size)
{
__int64 data; // x1
__int64 result; // x0
__int64 i; // x9
int offset; // w16
int v11; // w16
int v12; // w18
int v13; // w16
data = (*(__int64 (__fastcall **)(__int64, __int64))(*(_QWORD *)a1 + 1840LL))(a1, a4);
result = (unsigned int)size;
if ( (start & 0x80000000) != 0 )
return 4294967294LL;
if ( size > 0 )
{
i = 0LL;
do
{
if ( ((start + i) & 0x8000000000000000LL) != 0 )
{
offset = 0;
}
else
{
offset = start + i;
if ( start + i >= 0x8000 )
offset %= 0x7FFF;
}
v11 = offset * offset;
v12 = v11 + 80923;
v13 = v11 + 81178;
if ( v12 >= 0 )
v13 = v12;
*(_BYTE *)(data + i++) ^= byte_7191359B20[v12 - (v13 & 0xFFFFFF00)]; // 和密码表对应的字节映射
}
while ( size != (_DWORD)i );
}
return result;
}
python
照着写一遍,把密码表copy
下来就行了
python
实现的时候可以发现很多if
条件都触发不了,因此可以自己精简一下
四、解密缓存文件测试
正常播放
免费评分
参与人数 12 威望 +1
吾爱币 +33
热心值 +10
收起
理由
junjia215
+ 1
+ 1
谢谢@Thanks!
固水
+ 1
+ 1
我很赞同!牛皮,虽然看不懂
huo99
+ 1
谢谢@Thanks!
daiqing
+ 1
+ 1
感谢发布原创作品,吾爱破解论坛因你更精彩!
RUO
+ 2
+ 1
有点硬核,谢谢分享
axin0529
+ 1
+ 1
热心回复!
坚持啊啊
+ 1
+ 1
我很赞同!
爱飞的猫
+ 2
mp4 后缀可以考虑替换为 m4a 或 aac
wanfon
+ 1
+ 1
热心回复!
imumu1239
+ 1
+ 1
感谢发布原创作品,吾爱破解论坛因你更精彩!
hxd97244
+ 1
+ 1
感谢发布原创作品,吾爱破解论坛因你更精彩!
正己
+ 1
+ 20
+ 1
感谢发布原创作品,吾爱破解论坛因你更精彩!
查看全部评分