首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
8
8
某钓鱼木马APP分析
发表于: 2026-1-27 14:13
3887
某钓鱼木马APP分析
Shangwendada
4
2026-1-27 14:13
3887
## 前言 自动拾取忘记关了,捡到了一个7.37M的短剧APP。WDF!什么短剧APP居然有如此惊为天人的压缩技术,于是... ## 现象分析 木马伪装成了一个短剧APP并设置弹窗伪装需要开启“加速器”来诱导用户对其进行授权,点开后实际请求的是无障碍权限,获取无障碍权限之后就能监控屏幕了。   ## 代码分析 ### 壳APP 这个APP我们拿到的包实际上是一个壳APP,没有真正的业务逻辑。 首先注意到这个温馨提示的弹窗功能如(org.tools.pro.AccessibilityGuide.showBrandSpecificDialog)  这里看到设置的onclick回调函数a,点进去看,就可以发现他在安装一个新的APP。   解密资源逻辑,其实就是异或了一个90,异或的文件为.dat的,其实这里都不需要看FileName,Assets就一个.dat结尾的,直接异或90再次JADX。   ### 木马APP #### 脱壳分析 直接jadx反编译发现实际上是加固的,什么类都看不到并且有很明显的加固字样。  没看出来是什么加固,没有时间管那么多了,直接看看他加载的so文件,这里我选择的是Arm64版本的。  分析的话首先还是看init_array段,发现没有逻辑,那么只能在JNI_onLoad了。  清晰明了    发现了加固逻辑,释放了一个vm.dex 然后在 sub_1E680 进行AES解密填充 最开始以为是抽取方案,后面看看sub_1E680 里面的代码,反射调用的InMemoryDexClassLoader,那么实则是采用动态加载dex的方案了。  那么既然如此,我们可以通过Hook InMemoryDexClassLoader 的方案来脱壳,但是InMemoryDexClassLoader 最终还是要走loadMethod的,所以通用Hook DefineClass的脱壳方案也是可以用的。 frida代码: ~~~java function get_self_process_name() { var openPtr = Module.getExportByName('libc.so', 'open'); var open = new NativeFunction(openPtr, 'int', ['pointer', 'int']); var readPtr = Module.getExportByName("libc.so", "read"); var read = new NativeFunction(readPtr, "int", ["int", "pointer", "int"]); var closePtr = Module.getExportByName('libc.so', 'close'); var close = new NativeFunction(closePtr, 'int', ['int']); var path = Memory.allocUtf8String("/proc/self/cmdline"); var fd = open(path, 0); if (fd != -1) { var buffer = Memory.alloc(0x1000); var result = read(fd, buffer, 0x1000); close(fd); result = ptr(buffer).readCString(); return result } return "-1" } function Mkdir(path) { if (path.indexOf("com") == -1) { console.log("[Mkdir]-> Pass:", path); return 0; } var mkdirPtr = Module.getExportByName('libc.so', 'mkdir'); var mkdir = new NativeFunction(mkdirPtr, 'int', ['pointer', 'int']); var opendirPtr = Module.getExportByName('libc.so', 'opendir'); var opendir = new NativeFunction(opendirPtr, 'pointer', ['pointer']); var closedirPtr = Module.getExportByName('libc.so', 'closedir'); var closedir = new NativeFunction(closedirPtr, 'int', ['pointer']); var cPath = Memory.allocUtf8String(path); var dir = opendir(cPath); if (dir != 0) { closedir(dir); return 0 } mkdir(cPath, 0o755); chmod(path) console.log("[Mkdir]->", path); } function chmod(path) { var chmodPtr = Module.getExportByName('libc.so', 'chmod'); var chmod = new NativeFunction(chmodPtr, 'int', ['pointer', 'int']); var cPath = Memory.allocUtf8String(path); chmod(cPath, 755) } function dump_dex() { var libart = Process.findModuleByName("libart.so"); var addr_DefineClass = null; var symbols = libart.enumerateSymbols(); for (var index = 0; index < symbols.length; index++) { var symbol = symbols[index]; var symbol_name = symbol.name; if (symbol_name.indexOf("ClassLinker") >= 0 && symbol_name.indexOf("DefineClass") >= 0 && symbol_name.indexOf("Thread") >= 0 && symbol_name.indexOf("DexFile") >= 0) { console.log(symbol_name, symbol.address); addr_DefineClass = symbol.address } } var dex_maps = {} ; var dex_count = 1; console.log("[DefineClass:]", addr_DefineClass); if (addr_DefineClass) { Interceptor.attach(addr_DefineClass, { onEnter: function (args) { var dex_file = args[5]; var base = ptr(dex_file).add(Process.pointerSize).readPointer(); var size = ptr(dex_file).add(Process.pointerSize + Process.pointerSize).readUInt(); if (dex_maps[base] == undefined) { dex_maps[base] = size; var magic = ptr(base).readCString(); if (magic.indexOf("dex") == 0) { var process_name = get_self_process_name(); if (process_name != "-1") { /* var dex_dir_path = "/data/data/" + process_name + "/files/dump_dex_" + process_name; Mkdir(dex_dir_path */ var dex_dir_path = "/data/data/" + process_name + "/files" Mkdir(dex_dir_path) dex_dir_path += "/dump_dex" Mkdir(dex_dir_path) var dex_path = dex_dir_path + "/classes" + (dex_count == 1 ? "" : dex_count) + ".dex"; console.log("[find dex]:", dex_path); var fd = new File(dex_path, "wb"); if (fd && fd != null) { dex_count++; var dex_buffer = ptr(base).readByteArray(size); fd.write(dex_buffer); fd.flush(); fd.close(); console.log("[dump dex]:", dex_path) } } } } } , onLeave: function (retval) { } }) } } var is_hook_libart = false; function hook_dlopen() { Interceptor.attach(Module.findExportByName(null, "dlopen"), { onEnter: function (args) { var pathptr = args[0]; if (pathptr !== undefined && pathptr != null) { var path = ptr(pathptr).readCString(); if (path.indexOf("libart.so") >= 0) { this.can_hook_libart = true; console.log("[dlopen:]", path) } } } , onLeave: function (retval) { if (this.can_hook_libart && !is_hook_libart) { dump_dex(); is_hook_libart = true } } }); Interceptor.attach(Module.findExportByName(null, "android_dlopen_ext"), { onEnter: function (args) { var pathptr = args[0]; if (pathptr !== undefined && pathptr != null) { var path = ptr(pathptr).readCString(); if (path.indexOf("libart.so") >= 0) { this.can_hook_libart = true; console.log("[android_dlopen_ext:]", path) } } } , onLeave: function (retval) { if (this.can_hook_libart && !is_hook_libart) { dump_dex(); is_hook_libart = true } } }) } setImmediate(dump_dex); ~~~ 这里直接使用frida -U -f 启动本木马APK你会会发现直接报错了,因为这个APP是没有正常的Luncher的, 他不接受 android.intent.action.MAIN  但是他接受BOOT_COMPLETED ,ndroid.provider.Telephony.SMS_RECEIVED 来进行拉起自己,典型的木马行为。那这样要如何才能Hook到他是个问题,这里其实最好的思路是hook 壳app的接入点,去Hook木马APP。 但,没有更方便的办法了吗? 没有更方便的办法了吗? 真的没有了吗? 其实突然我想到了算法助手PRO,如有神助,为何呢,因为算法助手基于LSP可以很方便的就监控到APP的启动,正好算法助手又有加载Frida的功能,这简直太爽了。   随便点点触发到木马APP的启动逻辑就行,算法助手就自动Hook了  同时加载的dex也被脱下来了,爽爽爽  #### 业务逻辑分析 该 APK 在申请屏幕捕获权限后会启动 MediaProjection 前台服务并持续抓取屏幕帧,压缩后通过 WebSocket 外传到 C2;同时具备摄像头/麦克风、短信、通讯录/通话记录、位置、文件、支付密码与锁屏密码窃取以及反卸载等能力。 ##### 屏幕捕获链 / 屏幕截取行为链 liliao.mine.king.liliao.SimplifiedConnectionManager.handleMessage ,此处接受C2下发的 order ,发送给屏幕捕获的入口   liliao.mine.king.liliao.accessibility.ScreenCaptureManager 负责创建 VirtualDisplay/ImageReader 进行抓屏,帧被压缩为 JPEG 并标记 type=video_frame 后上送。  传输通道是 WebSocket,地址端口都在资源文件里   ##### 恶意能力 / 恶意行为能力 ###### 摄像头取流/拍照并 Base64 回传: liliao.mine.king.liliao.b   ###### 麦克风实时音频流与定时录音文件外传 liliao.mine.king.liliao.h  ###### 通讯录与通话记录批量读取并上送 liliao.mine.king.liliao.d  ###### 凭据窃取:伪装锁屏 PIN、支付密码覆盖层、安装密码覆盖层并回传 liliao.mine.king.liliao.phishing.d(负责保存骗取的锁屏密码)  liliao.mine.king.liliao.accessibility.smartmonitor.w0.j(匹配支付类型)  ###### 模拟点击操作用户设备 liliao.mine.king.liliao.accessibility.e.e 自动解锁  实际负责执行的是: liliao.mine.king.liliao.accessibility.RemoteAccessibilityService  ### 总结 最后看了这个APP所有的操作,我们尝试还原一下他是如何转账走钱财的: 1. 远控进入受害者手机 通过 WebSocket 远程下发命令(C2):liliao.mine.king.liliao.SimplifiedIOSocket 无障碍执行点击/滑动/系统键: 指令处理:liliao.mine.king.liliao.accessibility.e 实际手势注入:liliao.mine.king.liliao.accessibility.b0 服务入口:liliao.mine.king.liliao.accessibility.RemoteAccessibilityService 2. 获取屏幕画面与操作反馈 申请 MediaProjection 权限并自动点击“允许”,随后持续抓屏上传帧:liliao.mine.king.liliao.ScreenCapturePermissionActivity,liliao.mine.king.liliao.accessibility.s0,iliao.mine.king.liliao.accessibility.ScreenCaptureManager 3. 获取支付/解锁密码 支付密码覆盖层输入并保存(含支付宝/微信等): liliao.mine.king.liliao.accessibility.smartmonitor.w0 伪锁屏 PIN/解锁密码捕获并上报:liliao.mine.king.liliao.phishing.b,liliao.mine.king.liliao.phishing.d 保存后会在 reportPaymentPasswords() 里打包上送:liliao.mine.king.liliao.SimplifiedConnectionManager 4. 最终转账动作 攻击者利用远控打开银行/支付宝/微信等 App,按真实步骤输入收款账号与金额,并使用已获取的支付密码完成转账。
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
#逆向分析
#脱壳反混淆
收藏
・
8
点赞
・
8
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
trackL
你的分享对大家帮助很大,非常感谢!
2026-4-17 09:45
Gift1a
为你点赞!
2026-3-9 14:35
逆天而行
感谢你的贡献,论坛因你而更加精彩!
2026-3-3 13:57
mb_zfqvurgb
感谢你的积极参与,期待更多精彩内容!
2026-2-27 23:14
X1aoYU
+1
期待更多优质内容的分享,论坛有你更精彩!
2026-1-31 13:48
东方玻璃
你的分享对大家帮助很大,非常感谢!
2026-1-28 15:24
git_47499test-look
你的帖子非常有用,感谢分享!
2026-1-27 18:48
ngiokweng
+6
期待更多优质内容的分享,论坛有你更精彩!
2026-1-27 16:51
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
7
)
临江仙_
雪 币:
94
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
11
粉丝
0
关注
私信
临江仙_
2
楼
太牛了sw
2026-1-27 14:34
0
东方玻璃
雪 币:
10628
活跃值:
(11035)
能力值:
( LV12,RANK:260 )
在线值:
发帖
12
回帖
119
粉丝
517
关注
私信
东方玻璃
5
3
楼
swdd,yes!
2026-1-28 15:25
0
Shangwendada
雪 币:
4336
活跃值:
(5910)
能力值:
( LV12,RANK:260 )
在线值:
发帖
19
回帖
78
粉丝
223
关注
私信
Shangwendada
4
4
楼
东方玻璃
swdd,yes!
2026-1-28 17:04
0
逆天而行
雪 币:
4722
活跃值:
(7286)
能力值:
( LV6,RANK:90 )
在线值:
发帖
12
回帖
124
粉丝
246
关注
私信
逆天而行
1
5
楼
所以谷歌时严厉禁止 插件化加载的
2026-3-3 13:58
0
Shangwendada
雪 币:
4336
活跃值:
(5910)
能力值:
( LV12,RANK:260 )
在线值:
发帖
19
回帖
78
粉丝
223
关注
私信
Shangwendada
4
6
楼
1
最后于
2026-3-3 14:47 被Shangwendada编辑 ,原因: 1
2026-3-3 14:47
0
Shangwendada
雪 币:
4336
活跃值:
(5910)
能力值:
( LV12,RANK:260 )
在线值:
发帖
19
回帖
78
粉丝
223
关注
私信
Shangwendada
4
7
楼
逆天而行
所以谷歌时严厉禁止 插件化加载的
没招呀 最近已经看到很多这个诈骗案例了
2026-3-3 14:47
0
qingshan666
雪 币:
62
活跃值:
(56)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
1
粉丝
0
关注
私信
qingshan666
8
楼
大神,怎么联系你?有咨询下单
2026-4-17 06:21
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
Shangwendada
4
19
发帖
78
回帖
260
RANK
关注
私信
他的文章
某钓鱼木马APP分析
3887
[原创]记录一次Unity加固的探索与实现
20317
[原创][HWS]animals
11242
[强网杯S9]Qcalc
11633
[原创]记录一次加固逆向分析以及加固步骤详解
10283
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部