FinalShell 3.0.10 逆向破解
本帖最后由 Ax王者 于 2021-10-3 18:57 编辑高级版功能(不断增加中)
1. 网络监控可选择接口,同时监控多个网络接口速度.
2. 打包传输,自动压缩解压,适合传输大量文件,文件夹和文本文件.
3. 高级网络监控,监控每个进程监听的端口,以及网络
4. 高级进程管理,详细显示进程信息.连接状态.
5. 无限制的终端命令历史,路径历史,可快速输入命令,切换路径.
这是 FinalShell 高级版的功能。
我打算直接静态破解,毕竟这款软件的静态混淆没有那么的强。
首先进行命名反混淆。
(反混淆后启动软件发现是秒启动,原生版本都需要很长时间去加载,难不成是直接跳过了证书验证?)
然后我们用 JbyteMod-Reborn 加载这个反混淆后的文件。
直接搜索“高级版”看看会有些什么?
结果显而易见,升级高级版对应的是免费版本,“高级版”字符串则对应的是付费版本了。
我们直接跳转到 “MainPanel” 对主页面进行分析。
这里会有两个对字符串字段的定义,我们直接搜索“高级版”所对应的字段的调用记录。
发现这里会有三个对这个字段的调用,我们随便进去一个,来查询一下它的具体条件是怎么样?
private void OO00O0OOOO0O00O0OO0O0OOO0OOO0OOOO0OOO0OO0OO0O00O0O000O0O0OO0OO0OO0O0O0O0OOO0OO0O00000OOOOO0OO0OO0OOO() {
boolean pro = myssh.App.OO0OO0000OOO0OO0O0O0000O000OO00O0O0OO00OO00O0O000OOO00O0O00O00OOO0O0O000O0O0O0OOO000OO000OOOOO0O0O().O0000OOO0000O0OO0OO0O0O0OO00O000O0OO0O0OO0OOO0OOOOOO0000OOO00O00O0O0OOOOOO0OO0000OO0OOO00OOOOOOO0().O00OOOO00O00OO0OOOO00O00000OOOO0OOOO0000000O0OOOO00OO0000O00O00OO0OOOOOO0O0OO0O00OOOOOOO0O0O0O0000OO();
if (pro) {
this.O000O0O0OOO0O0O000000OO0OOO00OO000OO00O0OOO0OOOOOOOO0OOOOOOO000O0OOO0OO00O0O000OO00OOOO00O000O.setText(this.O0OOOOOO000OO0O0O0OOOO000O0O000OO0OOOOOO0OO00OOO00OOO0O0000OO000OOO0O00OO00O0O0OO00O00O00000OO0OOOO);
} else {
this.O000O0O0OOO0O0O000000OO0OOO00OO000OO00O0OOO0OOOOOOOO0OOOOOOO000O0OOO0OO00O0O000OO00OOOO00O000O.setText(this.OOOO00O0OOO00OOOO0OO0OOO0OOO0O000OOOO0000O0000OO00OOO00O0OO0OO0OO0O0OO0OO0O0OOOOO0O00OOOOOOOO000O0);
}
this.O0OOO0000OO00O0OOOOO0OO0OO00O0OO0O00O00OOOO000OOOO0OO0O00O00OOOO0000O0O000OOOO0O00000O00O0OOO0O.O00OOOO00O00OO0OOOO00O00000OOOO0OOOO0000000O0OOOO00OO0000O00O00OO0OOOOOO0O0OO0O00OOOOOOO0O0O0O0000OO(pro);
OO0O00OO000O0O0OO00OO00OOOOO0OOOOO0OO000O0OOOO0OOO0000O000O0OOO0OOOO0O00000OOOOO0OO0000O0O0OO000OO.OO0O00OO00OOOOOO0000O0O000O0O0000OOOO00OO000OO0000OO000000O00O0O000OO00O0O000O00O0O00OOOOOO0OOO0().O00OOOO00O00OO0OOOO00O00000OOOO0OOOO0000000O0OOOO00OO0000O00O00OO0OOOOOO0O0OO0O00OOOOOOO0O0O0O0000OO(pro);
OO0O00OO00OOOOOO0000O0O000O0O0000OOOO00OO000OO0000OO000000O00O0O000OO00O0O000O00O0O00OOOOOO0OOO0.OOOO000O00OO0O0000O0000OO0O0O00OOOO00OO00OOO000000O0OOO0OOOOOOOOOOOOO000OO0OOOO000O00OO000OOOOOOO000().O00OOOO00O00OO0OOOO00O00000OOOO0OOOO0000000O0OOOO00OO0000O00O00OO0OOOOOO0O0OO0O00OOOOOOO0O0O0O0000OO(pro);
}
通过CFR反编译器反编译出来的代码,我们可以轻松的知道他是如何判断用户是否为“Pro”用户了。
我们直接跳转到它赋值 布尔值 “pro” 所调用的方法。
我们直接进入了类“ControlClient”,看来这就是主要的验证类了。
我们要开始动手了?
首先我注意到的是这两个方法:
private boolean method1() {
boolean success = false;
this.code= 0;
this.O000OOOO00OO0O0O0O0OO0OO0OO000O0O00000OOO0O0OO0O00O00OOOOO00O00OOOO0O000OO00OOO00OO0OOOOO0O0O0000O = false;
this.O000000000O0000OOOO0O0O000O0000OOOO0OO0O0O00OOO00OO00O000O0OO0OOO0000000OO000O0OOO00OOOO00000OO0OOOO = false;
JSONObject requestMeaaage = new JSONObject();
requestMeaaage.put("username", (Object)App.OO0OO0000OOO0OO0O0O0000O000OO00O0O0OO00OO00O0O000OOO00O0O00O00OOO0O0O000O0O0O0OOO000OO000OOOOO0O0O().O000OOOO00OO0O0O0O0OO0OO0OO000O0O00000OOO0O0OO0O00O00OOOOO00O00OOOO0O000OO00OOO00OO0OOOOO0O0O0000O().O00OOO00O00OOOOO0OO0OOOO00O00OO0OO00OOOOO0O000OO00O0OO000O000O0OOO0OOOO000OOO0000OO000OOOOOOOOOOO00());
requestMeaaage.put("password", (Object)App.OO0OO0000OOO0OO0O0O0000O000OO00O0O0OO00OO00O0O000OOO00O0O00O00OOO0O0O000O0O0O0OOO000OO000OOOOO0O0O().O000OOOO00OO0O0O0O0OO0OO0OO000O0O00000OOO0O0OO0O00O00OOOOO00O00OOOO0O000OO00OOO00OO0OOOOO0O0O0000O().OOO00OO00OOOO00OO0O00O0O00OOO0OOO000O00OOOO00OOOOOO000O0OO0O0OO0OO000O00O0OOOOOO00OO00OO00OOOO0OO000());
requestMeaaage.put("fix", (Object)Tools.OO0O00OO00OOOOOO0000O0O000O0O0000OOOO00OO000OO0000OO000000O00O0O000OO00O0O000O00O0O00OOOOOO0OOO0((int)this.O000O0OOOOO00O0O00O0OO00O000O00O00000O0OO00000OOOO0O0OOOOO0O0O0OOOOO00OOO000OOO0000O0O0OOO0O0OO00OO0.nextInt(1024)));
try {
String msg;
JSONObject responJson = this.O00OOOO00O00OO0OOOO00O00000OOOO0OOOO0000000O0OOOO00OO0000O00O00OO0OOOOOO0O0OO0O00OOOOOOO0O0O0O0000OO(requestMeaaage, "http://www.hostbuf.com/fs.c", this.OOO00O00OO00O0O000000O0OOOOOO0OOOO0O000OO000OO0OO0O0O00O0O0O00OO000O00OO000OOO00OO0O0O00O0O00OO0000);
this.code = responJson.getIntValue("code");
LoginDialog.OO0O000000O0OO000000O0O0000O0OO0O00OOO000OOOOOOO00OO0O00O0OO000O00OO00OOO000O00OOOOO0O0OOO0O0O0O0 = msg = responJson.getString("msg");
this.O000OOOO00OO0O0O0O0OO0OO0OO000O0O00000OOO0O0OO0O00O00OOOOO00O00OOOO0O000OO00OOO00OO0OOOOO0O0O0000O = true;
while (this.O000OOOO00OO0O0O0O0OO0OO0OO000O0O00000OOO0O0OO0O00O00OOOOO00O00OOOO0O000OO00OOO00OO0OOOOO0O0O0000O) {
Thread.sleep((long)100L);
}
}
catch (Exception e) {
e.printStackTrace();
}
return success;
}
public boolean OOO0OOOOO00O0OO00O000OOOO0OO00OOO00000OO0000000OOOOOO00O0OOO00O0O00O0OOOOOO0OOO0O000OOOO0000OO0O000O() {
return this.O000OOOO00OO0O0O0O0OO0OO0OO000O0O00000OOO0O0OO0O00O00OOOOO00O00OOOO0O000OO00OOO00OO0OOOOO0O0O0000O;
}
void method2() {
if (this.code== 1) {
if (this.O0O0O0000OO00O0O0000OO0OOO00O0OO00OO0OO0OOO0000OO00O00O0OOOO00OOOO00OO00O00O000OO00OOOO0000O000O != null) {
this.O0O0O0000OO00O0O0000OO0OOO00O0OO00OO0OO0OOO0000OO00O00O0OOOO00OOOO00OO00O00O000OO00OOOO0000O000O.O00OOOO00O00OO0OOOO00O00000OOOO0OOOO0000000O0OOOO00OO0000O00O00OO0OOOOOO0O0OO0O00OOOOOOO0O0O0O0000OO(true);
}
} else {
this.O0O0O0000OO00O0O0000OO0OOO00O0OO00OO0OO0OOO0000OO00O00O0OOOO00OOOO00OO00O00O000OO00OOOO0000O000O.O00OOOO00O00OO0OOOO00O00000OOOO0OOOO0000000O0OOOO00OO0000O00O00OO0OOOOOO0O0OO0O00OOOOOOO0O0O0O0000OO(false);
}
this.O000000000O0000OOOO0O0O000O0000OOOO0OO0O0O00OOO00OO00O000O0OO0OOO0000000OO000O0OOO00OOOO00000OO0OOOO = true;
this.O000OOOO00OO0O0O0O0OO0OO0OO000O0O00000OOO0O0OO0O00O00OOOOO00O00OOOO0O000OO00OOO00OO0OOOOO0O0O0000O = false;
}
第二个方法会首先判断第一个方法所取得的 code,如果为1,那么就继续。
然后会判断一个interface是否为null。
随后就会把
第一个方法会获取responJson作为信息返回,是在登陆窗口弹出的以及显示出的信息。
具体思路已经理顺,我们直接对其进行修改!
首先patch掉它这个类中用来获取请求的方法。
public com.alibaba.fastjson.JSONObject O00OOOO00O00OO0OOOO00O00000OOOO0OOOO0000000O0OOOO00OO0000O00O00OO0OOOOOO0O0OO0O00OOOOOOO0O0O0O0000OO(com.alibaba.fastjson.JSONObject requestMeaaage, java.lang.String url, org.apache.http.client.HttpClient httpclient) throws java.lang.Exception {
com.alibaba.fastjson.JSONObject responeMessage = null;
boolean success = false;
for (int n = 0;
n < this.O0O000OOOOO0000O0000O000O00O000O0O0OO0O00OO0000OO00OO0OOO0O0O0OO00OO00O0OOO0OOOOOO0OOO0O00000OO0OO; ++n) {
org.apache.http.client.methods.HttpPost post = new org.apache.http.client.methods.HttpPost(url);
java.io.DataInputStream dis = null;
try {
byte[] requestData = requestMeaaage.toString().getBytes("utf-8");
requestData = myssh.OOO00O00OO00O0O000000O0OOOOOO0OOOO0O000OO000OO0OO0O0O00O0O0O00OO000O00OO000OOO00OO0O0O00O0O00OO0000.DesUtilPro.O00OOOO00O00OO0OOOO00O00000OOOO0OOOO0000000O0OOOO00OO0000O00O00OO0OOOOOO0O0OO0O00OOOOOOO0O0O0O0000OO((byte[])requestData);
org.apache.http.entity.ByteArrayEntity entity = new org.apache.http.entity.ByteArrayEntity(requestData);
post.setEntity((org.apache.http.HttpEntity)entity);
org.apache.http.HttpResponse respone = httpclient.execute((org.apache.http.client.methods.HttpUriRequest)post);
org.apache.http.HttpEntity responeEntity = respone.getEntity();
int responeLength = (int)responeEntity.getContentLength();
java.io.InputStream is = responeEntity.getContent();
dis = new java.io.DataInputStream(is);
byte[] responeData = new byte;
dis.readFully(responeData);
responeData = myssh.OOO00O00OO00O0O000000O0OOOOOO0OOOO0O000OO000OO0OO0O0O00O0O0O00OO000O00OO000OOO00OO0O0O00O0O00OO0000.DesUtilPro.OO0O00OO00OOOOOO0000O0O000O0O0000OOOO00OO000OO0000OO000000O00O0O000OO00O0O000O00O0O00OOOOOO0OOO0((byte[])responeData);
java.lang.String string = new java.lang.String(responeData, "utf-8");
responeMessage = com.alibaba.fastjson.JSONObject.parseObject((java.lang.String)string);
success = true;
break;
}
catch (java.lang.Exception e) {
e.printStackTrace();
try {
java.lang.Thread.sleep((long)100L);
}
catch (java.lang.InterruptedException e1) {
e1.printStackTrace();
}
continue;
}
finally {
if (dis != null) {
try {
dis.close();
}
catch (java.io.IOException e) {
e.printStackTrace();
}
}
}
}
if (!success) {
throw new java.lang.Exception("request failed!");
}
return responeMessage;
}
直接return null;
然后开始patch上面所说的“method1”
1.Patch 方法返回的布尔值,直接return true;
2.Patch Code检测和返回的Message
3.Patch “method2” 内的一切条件判断
好了,我们现在启动一下试试看。
出现了JVM虚拟机字节码校验错误,我们上面修改的代码是不存在的问题的
所以不知道哪里出现了问题,直接注入代码反射修改虚拟机,强制跳过验证。
启动。
看来是启动成功了,接下来测试一下破解是否成功。
看来是成功了?
投入实际应用测试一下。
成功了!
总结一下,
这个软件的验证没有我认为的那么困难,可能主要是因为这个软件本来我感觉就挺良心的,没有要商业化的意思。
破解副本仅供学习参考,请勿用于商业用途。
若您喜欢这个软件,请支持正版,也就35块钱,资助一下人家。
另外,给点分?
新版
FinalShell 3.9.2.2 离线激活 key-gen - 『原创发布区』 - 吾爱破解 - LCG - LSG |安卓破解|病毒分析|www.52pojie.cn
https://www.aliyundrive.com/s/cFew9qVL8pQ
文中所用的静态修改工具可以在Github内搜索到,是一个Fork版本,由于自己没能力更新多少,就不在这里放出连接了。 支持技术上的学习,有条件支持正版软件也是极好的! ecareyu 发表于 2020-5-29 10:59
无脑问下,在逆向过程中,是否发现有挖矿木马的存在?我觉得如果没有的话,最好替作者澄清一下。
我用了一年多了,确实遇到过一次CPU爆满的情况,java占用99%。当时作者很久没更新,现在作者重新拾起,不知道修复没有,具体的忘了。结论就是多开不要长期挂起,每天重启一次软件,到现在没遇到第二次。 有用免费版,唉~!不经常使用,想着用到就支持一下开发者!开发不易!!! 感谢,非常好! 感谢楼主分享技术:lol 挺好的一个软件,免费也很好用的 谢谢楼主,学习中 评分路过 感谢您的分享,非常有用,谢谢。 值得学习,谢谢分享~