Soul 隐私图片去除禁止截屏 · MT 管理器修改教程
适用对象:
[soul_6.37.0.apk](http://soul_6.37.0.apk)(包名[cn.soulapp.android](http://cn.soulapp.android),versionCode26091510)修改效果:隐私保护预览页等原本禁止截屏的页面,恢复为可正常截屏 / 录屏
改动量:只需修改 1 处(隐私保护预览页
PrivacyProtectPreviewActivity)
一、修改位置总览
| # | 文件(相对 smali 目录) | 所在 dex | 作用 | 是否必须改 |
|---|---|---|---|---|
| 1 | cn/soulapp/android/component/chat/snapchat/ [PrivacyProtectPreviewActivity.smali](http://PrivacyProtectPreviewActivity.smali) |
classes12 |
隐私保护预览页(聊天中发送的隐私图片 / 视频) | ✅ 必须改(本教程核心) |
💡 提示:只改第 1 处就够了。
文件位置:
[classes12.dex](http://classes12.dex)→cn/soulapp/android/component/chat/snapchat/PrivacyProtectPreviewActivity
二、操作步骤(核心:修改隐私保护预览页)
2.1 反编译
-
1. 打开 MT 管理器 → 文件管理器 → 找到
[soul_6.37.0.apk](http://soul_6.37.0.apk) -
2. 查看 → classes12.dex → 用 Dex编辑器++ 打开
2.2 定位文件
-
1. 在 Dex编辑器++ 中打开
[classes12.dex](http://classes12.dex) -
2. 搜索
0x2000,类型选择 代码 -
3. 下滑定位到
cn/soulapp/android/component/chat/snapchat/目录 -
4. 在
PrivacyProtectPreviewActivity中找到如下片段:const/16 v2, 0x2000 -
2.3 修改(只改 1 处)
修改前:
.line 34const/16 v2, 0x2000.line 36if-eqz v1, :cond_2d
修改后: 将 const/16 v2, 0x2000 替换为 const/4 v2, 0x0,即:
.line 34const/4 v2, 0x0.line 36if-eqz v1, :cond_2d
🔍 说明:寄存器
v2后面被两个分支引用 ——
• 当
banScreenshotAndSave == true时执行setFlags(v2, v2)• 否则执行
clearFlags(v2)将立即数改为
0后,两个分支均变为空操作,无需删除任何代码,逻辑依然保持完整。
2.4 保存、打包、签名
-
1. 修改完成后返回,保存该 dex 文件
-
2. 在功能菜单中执行 去签名校验(⚠️ 不做这一步的话,安装后会闪退)
-
3. 重新打包 APK 并签名
⚠️ 安装前务必先卸载旧版 Soul —— 签名证书不同,无法覆盖安装。
2.5 验证
在聊天中发送一张带「隐私保护」标记的图片 / 视频 → 点开预览 → 此时可以正常截屏,即修改成功 ✅。
Soul 去除开屏广告 / 全部广告位 / 私聊限制弹窗 · MT 管理器修改教程
适用对象:
soul_6.37.0.apk(包名cn.soulapp.android,versionCode26091510)修改效果:启动不再展示开屏广告;信息流、插屏、激励视频、原生等所有广告位不再加载广告;带「火热图标」的高热度会话点进去不再弹「送花提升回复率」拦截弹窗,可直接正常私聊
改动量:共 3 处(另有 1 处可选)
一、修改位置总览
| # | 文件(相对 smali 目录) | 所在 dex | 作用 | 是否必须改 |
|---|---|---|---|---|
| 1 | cn/soulapp/android/component/startup/main/MainScheduler$onCreate$6 |
classes15 |
启动调度:决定要不要展示开屏广告 | ✅ 必须改 |
| 2 | cn/soulapp/android/ad/core/requseter/AdRequester |
classes11 |
广告请求统一入口(信息流 / 插屏 / 激励 / 原生) | ✅ 必须改 |
| 3 | cn/soulapp/android/component/chat/style/BaseConversationStyleProcess |
classes12 |
私聊页限额拦截弹窗(送花提升回复率) | ✅ 必须改 |
| 4 | cn/soulapp/android/lib/common/bean/ChatLimitModel |
classes16 |
「会话是否受限」的判定(可选:连火热图标一起处理) | ⬜ 可选 |
💡 提示:1、2、3 三处是核心,改完即可达到「无广告 + 无限制弹窗」。
第 4 处只有在你想把「私聊」按钮旁边的火热图标也去掉时才需要改;想保留图标就不要动它。
二、操作步骤
2.1 反编译
- 1. 打开 MT 管理器 → 文件管理器 → 找到
soul_6.37.0.apk - 2. 查看 → 选中
classes11.dex、classes12.dex、classes15.dex(可多选)→ 用 Dex编辑器++ 打开
💡 提示:也可以只打开一个 dex,按类名搜索定位;MT 的 Dex编辑器++ 会显示类所在的 dex。
2.2 第一部分:去除开屏广告
所在 dex:classes15.dex → cn/soulapp/android/component/startup/main/MainScheduler$onCreate$6
- 1. 定位到
MainScheduler$onCreate$6,打开invoke()V方法 - 2. 搜索
display_ad,可以看到下面这段:
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
:cond_27
if-eqz v0, :cond_37
.line 3
sget-object v0, Lcn/soulapp/android/client/component/middle/platform/utils/push/NotifierClickUtils;->a:Lcn/soulapp/android/client/component/middle/platform/utils/push/NotifierClickUtils;
invoke-virtual {v0}, Lcn/soulapp/android/client/component/middle/platform/utils/push/NotifierClickUtils;->h()Z
move-result v0
if-eqz v0, :cond_37
.line 4
iget-object v0, p0, Lcn/soulapp/android/component/startup/main/MainScheduler$onCreate$6;->this$0:Lcn/soulapp/android/component/startup/main/MainScheduler;
invoke-static {v0}, Lcn/soulapp/android/component/startup/main/MainScheduler;->e(Lcn/soulapp/android/component/startup/main/MainScheduler;)V
goto :goto_3c
.line 5
:cond_37
iget-object v0, p0, Lcn/soulapp/android/component/startup/main/MainScheduler$onCreate$6;->this$0:Lcn/soulapp/android/component/startup/main/MainScheduler;
invoke-static {v0}, Lcn/soulapp/android/component/startup/main/MainScheduler;->c(Lcn/soulapp/android/component/startup/main/MainScheduler;)V
:goto_3c
return-void
修改前:
if-eqz v0, :cond_37
修改后: 把这一条改成无条件跳转,即改为:
goto :cond_37
🔍 说明:上面的
:cond_37分支调用的是MainScheduler.c()(→s()→ 直接做主页初始化,不展示开屏);下面的:cond_37之后那段调用的是MainScheduler.e()(→doShowAd(),展示开屏广告)。把
if-eqz v0, :cond_37改成goto :cond_37后,判断结果被忽略,无条件走”不展示开屏”分支,启动直接进主页。原广告分支变成不可达代码,dex 允许存在,无需删除。补充:
NotifierClickUtils.h()在<clinit>里被置为true,所以正常冷启动原本是满足展示条件的 —— 这就是不改就一直有开屏的原因。
2.3 第二部分:去除其他广告(信息流 / 插屏 / 激励 / 原生)
所在 dex:classes11.dex → cn/soulapp/android/ad/core/requseter/AdRequester
- 1. 定位到
AdRequester,
打开
1
e0(Lcn/soulapp/android/ad/bean/ReqInfo;Lcn/soulapp/android/ad/core/services/plaforms/listener/SoulAdRequestListener;Lcn/soulapp/android/ad/core/services/plaforms/listener/ColdTimingAdRequestListener;)V
方法
- 2. 把整个方法体替换为下面这段:
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
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
.method public final declared-synchronized e0(Lcn/soulapp/android/ad/bean/ReqInfo;Lcn/soulapp/android/ad/core/services/plaforms/listener/SoulAdRequestListener;Lcn/soulapp/android/ad/core/services/plaforms/listener/ColdTimingAdRequestListener;)V
.registers 14
monitor-enter p0
:try_start_0
const/4 v0, 0x3
new-array v1, v0, [Ljava/lang/Object;
const/4 v8, 0x0
aput-object p1, v1, v8
const/4 v9, 0x1
aput-object p2, v1, v9
const/4 v2, 0x2
aput-object p3, v1, v2
sget-object v3, Lcn/soulapp/android/ad/core/requseter/AdRequester;->changeQuickRedirect:Lcn/soul/android/plugin/ChangeQuickRedirect;
const/4 v4, 0x0
const/4 v5, 0x2
new-array v6, v0, [Ljava/lang/Class;
const-class v7, Lcn/soulapp/android/ad/bean/ReqInfo;
aput-object v7, v6, v8
const-class v7, Lcn/soulapp/android/ad/core/services/plaforms/listener/SoulAdRequestListener;
aput-object v7, v6, v9
const-class v7, Lcn/soulapp/android/ad/core/services/plaforms/listener/ColdTimingAdRequestListener;
aput-object v7, v6, v2
sget-object v7, Ljava/lang/Void;->TYPE:Ljava/lang/Class;
move-object v2, p0
invoke-static/range {v1 .. v7}, Lcn/soul/android/lib/hotfix/PatchProxy;->proxy([Ljava/lang/Object;Ljava/lang/Object;Lcn/soul/android/plugin/ChangeQuickRedirect;ZI[Ljava/lang/Class;Ljava/lang/Class;)Lcn/soul/android/lib/hotfix/PatchProxyResult;
move-result-object v1
iget-boolean v1, v1, Lcn/soul/android/lib/hotfix/PatchProxyResult;->isSupported:Z
:try_end_0
.catchall {:try_start_0 .. :try_end_0} :catchall_0
if-eqz v1, :cond_0
monitor-exit p0
return-void
:cond_0
:try_start_1
iput-object p1, p0, Lcn/soulapp/android/ad/core/requseter/AdRequester;->a:Lcn/soulapp/android/ad/bean/ReqInfo;
invoke-static {}, Ljava/lang/System;->currentTimeMillis()J
move-result-wide v1
invoke-virtual {p1, v1, v2}, Lcn/soulapp/android/ad/bean/ReqInfo;->setReqTime(J)Lcn/soulapp/android/ad/bean/ReqInfo;
iput-object p2, p0, Lcn/soulapp/android/ad/core/requseter/AdRequester;->b:Lcn/soulapp/android/ad/core/services/plaforms/listener/SoulAdRequestListener;
iput-object p3, p0, Lcn/soulapp/android/ad/core/requseter/AdRequester;->c:Lcn/soulapp/android/ad/core/services/plaforms/listener/ColdTimingAdRequestListener;
new-instance v0, Lsb/o;
invoke-virtual {p1}, Lcn/soulapp/android/ad/bean/ReqInfo;->getReqId()Ljava/lang/String;
move-result-object v1
invoke-virtual {p1}, Lcn/soulapp/android/ad/bean/ReqInfo;->getAdSlot()Lfa/a;
move-result-object v2
invoke-virtual {v2}, Lfa/a;->k()I
move-result v2
invoke-direct {v0, v1, v2}, Lsb/o;-><init>(Ljava/lang/String;I)V
iput-object v0, p0, Lcn/soulapp/android/ad/core/requseter/AdRequester;->g:Lcn/soulapp/android/ad/core/strategy/IRequestStrategy;
invoke-direct {p0}, Lcn/soulapp/android/ad/core/requseter/AdRequester;->y()V
const/4 v0, -0x1
const-string v1, "ads_disabled"
invoke-virtual {p0, p1, v0, v1}, Lcn/soulapp/android/ad/core/requseter/AdRequester;->onRequestFailed(Lcn/soulapp/android/ad/bean/ReqInfo;ILjava/lang/String;)V
:try_end_1
.catchall {:try_start_1 .. :try_end_1} :catchall_0
monitor-exit p0
return-void
:catchall_0
move-exception v0
monitor-exit p0
throw v0
.end method
🔍 说明:
e0(...)是 Soul 自建聚合广告 SDK 的统一请求入口,信息流、插屏、激励视频、原生等所有广告位最终都走这里(穿山甲com.bytedance.sdk.openadsdk和优量汇com.qq.e只是它下面的 adapter,本身不直接对外)。改成直接调用
onRequestFailed(reqInfo, -1, "ads_disabled")后,等于每次请求都回报”没有广告填充”。各广告位会按”无广告”的正常分支收起或隐藏,不会卡在加载中。⚠️ 前面三行不能删:
g(策略对象Lsb/o)和y()(写入ReqInfo.plSlotInfo)是后续onHandleResult()必需的前置状态 ——onHandleResult里会无条件读取getPlSlotInfo()和this.g,缺了会空指针崩溃。注意:MT 重新汇编后标签名可能被重编号(如
cond_0变成cond_2c),属正常现象,不影响逻辑。
2.4 第三部分:去除私聊限制弹窗(送花提升回复率)
所在 dex:classes12.dex → cn/soulapp/android/component/chat/style/BaseConversationStyleProcess
- 1. 定位到
BaseConversationStyleProcess,打开checkLimitOpt(Luj/c0;Lcn/soulapp/imlib/Conversation;Ljava/lang/String;)Z方法 - 2. 搜索
limit_model,可以看到下面这段:
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
30
31
32
.line 95
invoke-virtual {v0, v2}, Landroid/content/Intent;->getSerializableExtra(Ljava/lang/String;)Ljava/io/Serializable;
.line 98
move-result-object v0
.line 99
goto :goto_64
.line 100
:cond_63
move-object v0, v1
.line 101
:goto_64
instance-of v2, v0, Lcn/soulapp/android/lib/common/bean/ChatLimitModel;
.line 103
if-eqz v2, :cond_6b
.line 105
move-object v1, v0
.line 106
check-cast v1, Lcn/soulapp/android/lib/common/bean/ChatLimitModel;
.line 108
:cond_6b
move-object v2, v1
.line 109
if-eqz v2, :cond_ad
修改前:
instance-of v2, v0, Lcn/soulapp/android/lib/common/bean/ChatLimitModel;
修改后: 把这一条替换为:
const/4 v2, 0x0
🔍 说明:
v1初始为null,只有instance-of判断成立时才会被check-cast成ChatLimitModel。把instance-of的结果固定为0后,if-eqz v2, :cond_6b必定跳到:cond_6b,此时v1仍是null→v2也是null→if-eqz v2, :cond_ad必定跳到:cond_ad,也就是未受限分支。语义上完全等价于「Activity 的 Intent 里从来没有
limit_model这个字段」,和普通不受限会话走完全相同的后续流程(照常执行ChatUtils.s()和p1.l1()),不会少做任何正常初始化。改动极小,且不动任何寄存器类型,不会引入校验问题。这样就不会再构建
LimitGiftDialogV2(那个左上角有 X、点 X 会Activity.finish()把聊天页关掉的弹窗),但「私聊」按钮旁的火热图标是另一条分支控制的,不受影响。
2.5 保存、打包、签名
- 1. 修改完成后返回,保存相关 dex 文件
- 2. 在功能菜单中执行 去签名校验(⚠️ 不做这一步的话,安装后会闪退)
- 3. 重新打包 APK 并签名
⚠️ 安装前务必先卸载旧版 Soul —— 签名证书不同,无法覆盖安装。
2.6 验证
- 1. 冷启动 App → 不再出现开屏广告,直接进入主页 ✅
- 2. 进 广场 / 消息页滚动信息流 → 广告位空白或直接收起,不再出现广告 ✅
- 3. 在会话列表找到带火热图标的会话点进去 → 不再弹「送花提升回复率」弹窗,可直接打字聊天,返回也不会被踢出聊天页 ✅
三、可选:连「火热图标」一起去掉
所在 dex:classes16.dex → cn/soulapp/android/lib/common/bean/ChatLimitModel
那个火热图标(MetaPageUserHomeFragment.applyChatLimitHotIcon)的显示条件是 ChatLimitModel.isLimit() == true。想连它一起去掉,就再改第 4 处:
- 1. 定位到
ChatLimitModel,打开isLimit()Z方法 - 2. 把整个方法体替换为:
1
2
3
4
5
6
7
.method public isLimit()Z
.registers 2
const/4 v0, 0x0
return v0
.end method
🔍 说明:
ChatLimitModel的字段limit只被isLimit()读取,全 App 有 24 处调用isLimit()(私聊入口校验、路由拦截器、聊天页样式处理、新版配额体系、广场帖子底部按钮、通知卡片等),全部是限额相关判定。恒返回false相当于「这个会话永远不受限」,会一次性把所有付费拦截(含新版ChatQuota的「解锁私聊机会」卡片)都压掉。⚠️ 代价:同时也会让「私聊」按钮上那颗由
isLimit()驱动的火热图标不再显示。所以:
- 想保留火热图标 → 只改 1、2、3 三处,不要动
isLimit()(推荐,本教程默认方案)- 想连图标一起清掉 → 额外做这一处
四、关于版本
- • 本教程基于 Soul 6.37.0 版本(versionCode
26091510)。 - • 不同版本中,上述类的所在 dex 可能不同(如
classes11/classes12/classes15/classes16等)。定位方式相同:按类名搜索即可,MT 的 Dex编辑器++ 会显示该类实际所在的 dex。 - • 混淆后的类名可能变化,但下列特征很稳定,可作为定位锚点:
- 开屏:搜字符串
display_ad,落在MainScheduler$onCreate$6一类里 - 广告入口:类
cn/soulapp/android/ad/core/requseter/AdRequester,方法名e0 - 私聊弹窗:搜字符串
limit_model,落在BaseConversationStyleProcess.checkLimitOpt里
- 开屏:搜字符串
⚠️ 重要注意事项
-
- 安装修改版前必须先卸载原版本,聊天记录将会丢失,请提前备份重要数据。
- 2. 第 2 处改动会关闭全部广告请求,因此「看广告领 Soul 币 / 看广告解锁」这类功能会一直提示无广告可用,属预期结果。
- 3. 本教程仅用于学习 Android 反编译原理,请遵守相关法律法规,勿用于商业用途或侵犯他人权益。
关注微信公众号【素简好物】
- 回复:soul
- 获取安装文件