可能因为我的水平有限,我所能想到的加密方式似乎都没有实际用处。
比如,某个业务场景是付费用户可以创建无限的素材,而非付费用户只能创建不高于某数量的素材。
虽然我已经用了各种方式来加密这个“某数量”的值,不过不管前面写的多绕,但是最终还是会回归到一个 if
逻辑判断。
比如,针对上述场景,我是这样写的:
val a = "RWM="
val b = a.decodeBase64()?.string(Charset.forName("utf8")) ?: "${Char(0)}${Char(0)}"
val realNum = 10
if (realNum <= b.toCharArray()[1].code - b.toCharArray()[0].code) {
println("pass")
}
else {
println("you need pay first")
}
但是反编译下来是这样的:
(节选)
if (realNum <= var8 - var3) {
var4 = "pass";
System.out.println(var4);
} else {
var4 = "you need pay first";
System.out.println(var4);
}
不管你前面写的有多复杂,最终还是会回归到简单的 if
逻辑判断,别人修改时只需要删除或者直接改一下这个判断就能轻松破解了。
判断是否付费同理,不管前面写的多复杂,最终会回归到简单的逻辑判断。
所以我现在加密完全依赖于加固,但是加固也只能防的住“脚本小子”一键破解,如果是有大牛专门破解的话,也很容易被脱壳。
虽然防住“脚本小子”已经满足我的需求了,但是我还是想知道,应该怎么在不加固的情况下避免被“一键”破解呢?
据我所知,某个业内有名的 APP ,就没有使用加固,但是就是至今没被破解,甚至作者还专门搞了个悬赏,让别人区破解它。据我了解的是,至今没人破解成功。
补充:
哈哈哈,我可真实谢谢这个编译器了。
按照评论区大佬们的建议,试了一下多嵌套一些判断逻辑,然而:
如图,前面一堆都是我加的额外的判断条件,笑死我了,这个编译器管你前面嵌套了多复杂,反正最终还是如我正文所说的,只剩一个 if 判断了
1
heguangyu5 2022-07-01 09:43:23 +08:00
分享一下我的思路,关键有两点:
1. 定位到 if 判断需要一定时间 2. 要有办法在代码中加入足够多的 if 判断 |
2
gaobing 2022-07-01 09:47:15 +08:00
单机版 APP 吗?可以结合服务端进行设置。
|
3
TOUJOURSER 2022-07-01 09:49:47 +08:00
某个业内有名的 APP 指的是?
|
4
kile 2022-07-01 09:51:17 +08:00
怎么可能才一个 if
多个不同的值共同做加解密,互相做校验,一旦发现校验不通过,直接崩,每句语句都单独写判断。不要整合到一个方法里面。能写多少写多少 |
5
equationl OP @gaobing 正如我所说的,由于 APP 类型限制,没法把这部分内容放到服务器。
虽然我现在确实也做了利用服务器的在线校验,但是最终还是会回到我正文中说的问题,最后还是靠本地的简单逻辑判断。 但是我这个 APP 又没法把需要加密的代码放到服务端来执行,最终本地只负责接收服务端的计算结果... |
6
lerp 2022-07-01 09:52:43 +08:00
把业务逻辑也写进 c++里,在 c++里再进行签名校验。还有就是更新频率快一点,让他破解的速度跟不上你更新的速度。
|
7
equationl OP @heguangyu5 我现在就是这样做的,但是这样也只能做到增大“脚本小子”一键破解的难度,只要它稍微多捋一捋,还是能捋清
|
9
xiangyuecn 2022-07-01 09:55:03 +08:00
有没有那么一种可能,只要你代码写的足够复杂,别人就会懒得去破解😂
一个 if 不够,那就整一堆 if ,for 、while 都可以作为 if 的替代品 要是破解了一个地方进来了,里面发现校验不对,直接整点声音、震动、闪屏 物理攻击😂 |
10
equationl OP @TOUJOURSER MT 管理器
|
11
Mitt 2022-07-01 09:57:08 +08:00
没有什么破解不了,只取决于你的价值,其实以前有很多反破解做的很好的外挂说自己不可破解甚至还悬赏,基本都是因为太嚣张第一天就被破解了然后卷着尾巴跑了的,还是应该把附加价值算进去,比如售后好一点之类的,别人愿意买正版那被破解了也无所谓,最怕的就是把防破解做到极致了,客户都跑了
|
12
equationl OP @Mitt 这确实,我也没打算防这些大牛,我只想防“脚本小子”的一键破解,这些人太烦了,基本都是一些刚入门的学生,用别人写好的脚本破解了就沾沾自喜,到处发,真的大牛一般即使破解了也不会像他们这样到处发到处宣传
|
13
YouKnowIt 2022-07-01 10:31:57 +08:00
上定制版的 dex vmp 可以保护住,防防脚本小子没问题的
|
14
dearmymy 2022-07-01 10:40:09 +08:00
只能说有很多小技巧增加难度。针对你这种简单方法是。
两套校验逻辑 1 ,java 业务层判断,这种 if 比较容易被破解,而且需要提示给正常用户。 2 ,搞套 c++库逻辑,起个线程静默判断,发现 c++判断无效,但是前端逻判断有效,说明前端判断逻辑被爆破。c++线程里设置个变量,然其他 c++调用地方故意搞崩溃,不要给前端任何提示。 这样就符合你不需要太多加固,又比较简单拦住一帮新手了。 |
15
ZhiyuanLin 2022-07-01 10:43:19 +08:00
另一个思路是给个简单破解的口子,破解了会在生成素材加不可见水印+上传制作者信息到服务器。
然后律师函走起。 |
16
libook 2022-07-01 10:48:07 +08:00
加固通常对源代码没有侵入,个人认为能用加固解决的问题就尽量不要去污染源代码。
|
17
heguangyu5 2022-07-01 10:50:57 +08:00
@equationl 我没有实际做过破解,多问一句,如果在代码里加上几十万处 if 后,if 代码不完全一致,是有办法批量处理的吗?
|
18
yaocai321 2022-07-01 10:53:15 +08:00 2
曾经破解过一个 App ,他的反破解还挺有意思的。
先搞个类,里面的校验代码挺正常的,实际上是迷惑你的。 运行时,会利用双亲委派的机制,替换掉这个类。 这个用来替换的类,会将字节码加密成字符串存在某个地方。 是不是有点意思? |
19
equationl OP @heguangyu5 全自动不行,但是可以半自动,只要分析出你的逻辑,几十万,还是几十亿不都是批量替换的事嘛
|
20
equationl OP @libook 这个确实也是,但是加固有个问题就是现在加固几乎没有支持加固 aab 的,但是 play 只允许上传 aab 了。只能二选一。
ps:我知道 360 支持加固 aab ,但是价格太高了,不是我能承受的起的 |
21
0o0O0o0O0o 2022-07-01 11:25:38 +08:00 via iPhone
我觉得只要程序逻辑运行在本地能被破解者接触到,就没有防破解的可能。就算是鉴权后下发逻辑,也只能防第一次付费。后端加水印,破解者破解扩散后被发现了破解者可能也无所谓,大不了你封号拉黑呗,反正已经搞到手了。说到底,还是攻防双方成本的博弈。
MT 它不用知名厂商的加固产品你也不能说它没用加固,而且它的保护方案也被它定义成一种产品呢。 实在要说建议,那就建议学习一下 dex to c 、ollvm 、vmp 等相关知识,再在已有轮子基础上来做自定义保护。 |
22
shyrock 2022-07-01 13:43:45 +08:00
如果你所有的代码执行环境都在对方的手里,那意味着对方可以找到最近的分支最小改动绕过你的防护。整个业务逻辑全链路最简单的地方会成为破解的缺口,防不胜防。
但是如果你的部分代码不在客户端,应该就很难破解了。 |
23
CantSee 2022-07-01 14:10:03 +08:00
代码混淆?
|
25
zlowly 2022-07-01 14:40:36 +08:00
如果不考虑离线情况,即用户总是联网使用付费功能,则可以考虑不对称加密方式。密钥在服务器上,客户端有公钥,素材的全体或部分用密钥加密后才保存在客户端。平时客户端用公钥解密来使用,这样就无法简单的绕过某个判断来使用完整功能了。
|
26
equationl OP |
29
newmlp 2022-07-01 14:47:31 +08:00
Java 不是可以动态执行字节码吗,把一些逻辑放在服务端,然后下发的客户端执行
|
31
clino 2022-07-01 14:57:36 +08:00
“最终还是会回归到简单的 if 逻辑判断,别人修改时只需要删除或者直接改一下这个判断就能轻松破解了”
能不能让程序在删除或者修改 if 以后就不能正常工作呢? 比如说在判断里塞入一些不起眼的操作,如果没有这些操作,不能生成正常的素材,这种不正常是没法直接跟踪程序执行能够直接判断的,而是隐含在复杂的素材生成算法里面,这样破解的人就难以了解前后的因果关系 |
33
clino 2022-07-01 15:10:13 +08:00
我考虑的大概方向就是,不要把你的“不能工作”用那么明显的方式表现出来,比如说直接退出程序这种,而是隐含在功能的产出里,这种功能的产出应该是比较复杂有门槛的,否则你的产品也没什么保护的需要了,所以考虑你的功能里实现最复杂的部分,找地方加料让功能间接地不能正常工作,比如生成的素材有非常多的噪点,但是跟踪执行根本不知道噪点是哪里引入的,因为要搞懂就要了解里面的算法,但是算法没代码又非常难
|
34
i3x 2022-07-01 15:11:10 +08:00 via Android
@equationl 360 这个也不是解不了。虚拟机 dump 之类的都过时了。。。。暂时我还没发现安全的加固。只是难度不同罢了。我是个萌新,但是我目前木有发现解决不了的,解决不了的发给群里的大佬,几分钟就解决了,他们都有现成的环境。。。
冲你的目标,对付脚本小子来说,你自建一个混淆机制,不要用市场现有的,让他找不到脚本,没有真正的技术就解决不了了。 相关的防护我做的是比较多了。。。java php js 之类的代码保护,对于这些人来说,反倒是最不安全的自创逻辑混淆最安全。反而是你用标准的付费的高端的加密器,让他们很容易找到解密工具。先混淆到他们无法利用,在这个里边加几步授权,让这种人几乎完全无法利用就够了。 虽然对大佬来说写几个脚本就解密掉了。但是有一些逻辑也因为变量名字被替换,也是难以理解的,大佬对这种东西也不感兴趣,看一下结构自己都知道怎么写了,对破解这种东西根本就不感兴趣了。 至于你说混淆没有用,是因为你的逻辑太简单。你碰到一个授权不一定要在一个地方判断,很多地方判断搞好几十个逻辑,有的逻辑可能导致整个程序不可用,而有的逻辑会导致一部分比较恶心的东西。比如说有的地方校验不通过会随机性崩溃。程序不要做必然性的逻辑。还有存储这些判断逻辑,也不一定要存储一个固定的常量。可存储很多的值,有的数字是对的,但是是是而非。比如说你要存最大允许 10 个素材,你可以在一个地方出一个常量 10 。但是你根本就不调用这玩意儿,你调用的是 2*5 ,或者 9+1 。至于这个加减乘除怎么做你也不一定要存常量。可以从其它地方获得这个数字,就更难搜索定位了。有一些部分的逻辑自建类似于为加固的东西,用对称加密保护住。还有的功能做在网络上。虽然本地有逻辑,但是网络逻辑也作为额外的吊销手段。 当然只要你的东西足够有价值脚本小子没准就通过你的项目,成为大牛。。。 |
37
timethinker 2022-07-01 15:25:51 +08:00
加固就行了,除非是特别特别需要防止别人破解的 APP ,这个时候才应该考虑在源代码级别上面进行特殊的逻辑处理,另外也可以花钱买专业的加固工具,权衡利弊选择适合的就可以啦。操作系统都能被破解,iOS 都能被越狱,更别说 APP 了。抱着学习的态度去尝试了解这一行也是有价值收获的。
|
38
equationl OP @timethinker 目前是这样做的,只是 play 更新了政策,只能上传 aab 了,现在没有免费加固 aab 的服务了,收费加固的价钱不是我能承受的起的
|
39
cszchen 2022-07-01 15:32:44 +08:00 via iPhone
连温道斯和苹果系统都能被破解。
|
40
timethinker 2022-07-01 15:56:31 +08:00
我理解的你这个 APP 的激活是不是类似于签发密钥然后激活的单机授权模式,也就是用户在没有联网的情况下也能正常付费激活,比如私钥签名,然后 APP 校验,没问题的话就读取里面的授权信息,激活就算成功了?
如果破解者能够把你的授权部分修改掉,伪造出授权成功的信息通过校验,那么你的 APP 就会认为这确实是已经付费的正常用户。除此之外你在没有授权时做的任何校验都失去了存在的意义。 因此在你做各种校验之前,最好先考虑一下哪一部分才是最关键的。 |
41
equationl OP @timethinker 对,确实就是你说的这种情况,但是有一点不同的是,我会在调用付费功能时重新和服务器对比校验密钥
|
42
zhaokun 2022-07-01 16:41:25 +08:00
没写过 app ,JVM 层面可以做一个代码解密后加载到内存,磁盘上存的是加密后的,不知道 app 有没有这类实现
|
44
wy315700 2022-07-01 16:47:15 +08:00
当年 win32 环境下各种加壳和脱壳的对抗。
加壳加花加校验。让人看不懂也改不了。 |
45
653513754 2022-07-01 16:56:49 +08:00
用 switch 写多点分支 [![jQqON4.png]( https://s1.ax1x.com/2022/07/01/jQqON4.png)]( https://imgtu.com/i/jQqON4)
|
46
maokabc 2022-07-01 17:02:06 +08:00 via Android
把核心代码 native 化就能防止这类工具反编译,可以试试这个 https://github.com/maoabc/nmmp
|
47
xkang66 2022-07-01 17:02:30 +08:00
@equationl 放脚本小子简单啊, 免费方法:验证方法写在 so 里面,native 化,并且使用 C++ RTTI 以及 inline 属性,然后加上 ollvm 防护。付费方法:选择知名加固厂商,进行 VMP 保护
|
49
kytrun 2022-07-01 17:06:46 +08:00 3
|
50
nicevar 2022-07-01 17:10:08 +08:00
简单处理,你后面那个场景可以埋多个雷的,水平一般的也只会修改那个判断,你稍微玩点花的,对方可能就没那个耐心了,我个人的安卓收费软件一般是服务器多个协议校验+本地一个 c/c++库加解密逻辑,同时用两种不同的方式加密交换数据,能挡住绝大多数人了,当然挡不住一些功力深厚的,但是这类人大多也不会闲着去破解我的小应用,浪费他们的时间。
|
53
d1g1tal0cean 2022-07-01 17:40:28 +08:00
@kytrun 神人啊
|
54
cat9life 2022-07-01 17:48:55 +08:00
不用云端验证就没有别的好办法,加固是一条
|
55
TimhLiu 2022-07-01 17:51:31 +08:00 via Android
MT 管理器的作者有提供防逆向的付费服务,管理器左侧菜单"应用保护",可以看看
https://blog.mt2.cn/app-protect/ |
56
delectate 2022-07-01 20:10:01 +08:00
1 、云端下发代码,内存运行,但是 kms 的方法可破;
2 、本地 uuid 生成 key ,保存的素材包含 key 和 uuid 。其他人打开时首先验证 key 和 uuid ,不合法则拒绝打开,或者低清版,或者随即崩溃; 3 、验证的逻辑,不要写得那么简单,要让 key 的应用更广泛。比如保存文件的名称,配置文件的路径,某些变量的值,都要用到 key 和时间戳的余数,如果 key 不合法,那么 key 和时间戳的余数也不合法,那么程序随机崩溃; 4 、本地导出素材隐写术加水印; 5 、守护进程检测程序是否被修改( md5 、关键位置的汇编是否改动),是否有修改工具在运行(这个仅限 pc 平台,app 不确定),内存中 dump 的验证注册信息的代码段是否被修改; 6 、参考一下 denuvo 和 drm ; 7 、使用认证硬件,比如加密狗。这个适合专业软件; 8 、ai 数据分析,比如用户的操作是否合理(常见于反挂,比如 a 坐标到 b 坐标,时间小于 10s 就是有问题) |
57
cheng6563 2022-07-01 22:12:55 +08:00
我记得有混淆器把命名全替成 00OOoo 之类的
|
58
jhdxr 2022-07-01 22:20:49 +08:00
只考虑如何干掉 if:
//伪代码 func_table = [normal_flow, error_flow] call func_table[to_int(condition)] 狗头.jpg |
59
ZeroDu 2022-07-01 22:41:21 +08:00
mt 那么厉害吗?有没有大佬大概讲一下
|
60
DiamondYuan 2022-07-01 22:51:05 +08:00
https://balsamiq.com/company/how-we-run-our-business/business-strategy/software-piracy/
可以看看这篇 对于犯罪分子:你不能以技术为由打败他们,算了。如果人们想破解软件,他们会(代码混淆、call-home 方案与否。这些人在早餐前编写反编译器)。处理此问题的方法是在您的站点上拥有一份不错的最终用户许可协议以及使用条款文档. 确保每个下载链接上都写着“我同意”,并且基本上给人的印象是您已经完成了功课,有律师,而不是等待被利用的菜鸟。哦,如果时机成熟,一定要找一位好律师来帮助你。如果您发现有人以不同的品牌销售您的相同产品,我相信您律师的措辞强硬的信可能会有很长的路要走……再说一次,谁知道呢。但正如我稍后将解释的那样,这并不重要。 对于黑客:同样,不要试图用疯狂的加密方案打败他们,因为他们比你更好:你认为对编码有害的是他们的热情。我的做法是:尽量不要树敌,不要给他们挑战。如果你被认为是“一家不错的公司”,那么你成为黑客攻击目标的可能性就会降低(我不知道有多少 Windows 病毒是因为 MS 多年来对 Linux 的傲慢和攻击性言论造成的)。这在很大程度上是为什么我将这么多许可证授予非营利组织和各种行善者。此外,如果该软件一开始很便宜,有免费版本,而且许可证密钥看起来很容易破解,为什么还要费心破解它呢?我相信这些都是导致只有 16 人在 Google 上搜索“balsamiq 系列”的原因 |
61
XiLingHost 2022-07-01 22:56:08 +08:00
不要让程序崩溃,把你的授权包含在业务逻辑里,授权即使不正确依旧可以正常得到错误的结果,错误和正确的结果应当是无法简单区分的
|
62
InDom 2022-07-01 23:00:15 +08:00
说白了,这个 if 一定会存在,但能不能尽可能难的定位到(发现问题的时候,已经离执行这个 if 很远了。)。
先从别的地方获取到 你判断需要的数据准备好,但不使用。 另一个地方,在不同的时间线上,获取这个数据,不要判断,转化为特定的,与授权无关的几个值之一。 比如 is_main = "info" / debug / hello / index 。。。中间隔着非常多的代码 然后,['info' => fun_yes1, 'debug' => fun_no0, 'hello' => 'fun_no', 'index' => fun_yes5]('test_body', args1...) 这个地方,做了一次分流,如此多来几次,代码里面没有出现 if ,但其实在无形之中,做了很多次,判断。 同样的思路,可以用 for (i in 5|7|1|3|2|6|4 ){switch {case 2: code1;case 5: code2,i++ ;... } } 这个 fun 里面 已经在执行 正确的 或者错误的功能代码了。 跟踪代码的时候,基本不会关注这里,因为这里的代码 执行看起来都很正常。 然后到了某个地方,突然发现,这个地方没有 if 。就代码里面硬写死了 中断, 那就要回去找,是什么地方 引导流程到这里来的。 回头去找代码执行来源,就很头疼了,因为不知道从什么地方开始的,只能从头一点点跟踪... 如果想找到合心的代码,就必须从头到位,一直不拉的全部梳理一遍,才能找到核心。 |
63
gen900 2022-07-02 06:35:38 +08:00 via iPhone
我以前试过用抛异常代替 if 判断。比如 运行时间 /a ,合法用户 a=0 ,然后在合适位置 catch 这个异常走正常流程。没有逆向过不清楚这样是不是也回归到 if 判断
|
64
plumk 2022-07-02 15:53:57 +08:00
核心代码写在动态库,生成 md5 ,在运行的时候某个不起眼的地方检查动态库 md5 ?
|
65
dousha99 2022-07-03 13:29:33 +08:00
可以参考 Rare 的做法:写很多相互校验的逻辑,然后加壳混淆再加密,最后留一个一眼就能看出来的 `if`. 这个一眼就能看出来的 `if` 实际上是一个诱饵,破解之后可以被后面隐藏的校验逻辑所感知。当然做戏要做全套,验证出这个诱饵被破解之后,先不要着急弹没有授权的框框,而是让程序先能用一段时间;之后就开始莫名奇妙地崩溃、卡死、删存档。表面上是破解了,实际上完全没有破解,而且用户体验会特别特别差。尤其是生产力工具,卡死带删档基本上是绝杀。
大部分脚本小子和经验不足的破解者可能在干掉这个诱饵之后就打包发布了,殊不知发布的版本是完全没法用的。虽然说最终那些隐藏的相互校验的逻辑也会被大神破解掉,但是这个正版验证的目的已经达到了:让程序尽可能晚被破解。 |
66
HiLyy 2022-07-05 04:39:55 +08:00
可以这么写:
user_input_code = 123 real_code = 31456 key = abs ( real_code - user_input_code ) # 如果用户输入的注册码正确,key 为 0 ,否则为一正整数 ...... # 在生成素材的时候 for i in range ( 0 ... key ): # 在素材中添加随机噪音 除非用户能完全阅读代码,否则应该没人会想到要给 key 赋 0 吧。如果再绕一点,可以这样: user_input_code = 123 real_code = 31456 key = real_code - user_input_code # 如果用户输入的注册码正确,key 为 100 ,否则为其它数 ...... # 在生成素材的时候 for i in range ( 0 ... abs ( key - 100 ) ): # 在素材中添加随机噪音 在这种情况下,应该很难破解了吧? |
67
HiLyy 2022-07-05 04:42:50 +08:00
不知道为什么上面的缩进给没了,再试一下
可以这么写: user_input_code = 123 real_code = 31456 key = abs ( real_code - user_input_code ) # 如果用户输入的注册码正确,key 为 0 ,否则为一正整数 ...... # 在生成素材的时候 for i in range ( 0 ... key ): # 在素材中添加随机噪音 除非用户能完全阅读代码,否则应该没人会想到要给 key 赋 0 吧。如果再绕一点,可以这样: user_input_code = 123 real_code = 31456 key = real_code - user_input_code # 如果用户输入的注册码正确,key 为 100 ,否则为其它数 ...... # 在生成素材的时候 for i in range ( 0 ... abs ( key - 100 ) ): # 在素材中添加随机噪音 在这种情况下,应该很难破解了吧? |