autojs网络验证源码以及防破解思路分析

autojs实现网络验证,只要你稍微有点编程基础,代码和实现逻辑是非常简单的,先上一个我写过的代码,供大家参考。

先来看下界面:

autojs网络验证源码以及防破解思路分析

autojs网络验证源码以及防破解思路分析

 

 

autojs网络验证源码以及防破解思路分析

网络验证代码

我使用的单码登陆模式,即用户不需要注册账号,仅凭激活码就可以使用。

我把网络验证部分单独放在一个js文件中,这样方便管理和日后维护,在需要的时候用require调用,我的网络验证js文件名为ey.js,代码如下:

 1 var ey = {};
 2 ey.data = {
 3     url: "https://w.eydata.net/",
 4     ver: "20.08.10",
 5     jhm: "",
 6     statusCode: "",
 7 }
  //出于安全考虑,下面的部分字符串我打了马赛克 8 ey.httpCode = { 9 login: "764******c2cb3", 10 checkStatus: "2a2******56adac", 11 logOut: "8d7******39d", 12 getVerData: "f91******797e",//获取程序版本数据 (下一个版本的更新地址) 13 getBulletin: "35d******e556",//获取公告 14 getAppData: "b91******9811",//获取程序数据 (关键数据) 15 checkAppVersion: "28a******3075"//是否为最新版本, 1为最新,0不是最新 16 } 17 ey.httpBody = function (data) { 18 return { 19 login: { "SingleCode": data.jhm, "Ver": data.ver, "Mac": "" }, 20 checkStatus: { "StatusCode": data.statusCode, "UserName": data.jhm }, 21 logOut: { "StatusCode": data.statusCode, "UserName": data.jhm }, 22 getVerData: { "StatusCode": data.statusCode, "UserName": data.jhm, "Version": data.ver }, 23 getBulletin: {}, 24 getAppData: { "StatusCode": data.statusCode, "UserName": data.jhm }, 25 checkAppVersion: { "ver": data.ver } 26 } 27 } 28 29 ey.http = function (code, body) { 30 let fullUrl = ey.data.url + code; 31 let res = http.post(fullUrl, body, { 32 headers: { 33 'Content-Type': "application/x-www-form-urlencoded" 34 } 35 36 }); 37 let html = res.body.string(); 38 //log(html); 39 return html; 40 } 41 ey.err = function (ret) { 42 switch (ret) { 43 case "-1": 44 return "网络链接失败"; 45 case "-6": 46 return "还未登录"; 47 case "-15": 48 return "频繁调用,请等待10分钟后再做尝试"; 49 case "-110": 50 return "用户使用时间已到期"; 51 case "-115": 52 return "用户已被禁用."; 53 case "-129": 54 return "用户被开发者禁止使用,请咨询开发者是否被拉到黑名单"; 55 case "-133": 56 return "用户状态码错误"; 57 case "-134": 58 return "用户状态码不存在"; 59 case "-203": 60 return "程序版本号错误"; 61 case "-401": 62 return "无效的激活码"; 63 case "-421": 64 return "单码卡密超过最大登录数,如果确定已经下线,请等60分钟后重试"; 65 default: 66 return ret; 67 } 68 } 69 70 module.exports = ey;

登录过程代码

 1 function 登录() {
 2     //登录开始时,设置激活按钮文本为 激活中 ,并且把激活按钮设置为 禁止,防止用户误操作
 3     ui.run(function () {
 4         ui.isActive.setText("激活中...");
 5         ui.btn_jihuo.setEnabled(false);
 6     });
 7     //调用网络验证js文件ey.js
 8     var ey = require("./ey.js");
 9     ey.data.jhm = ui.jihuoma.getText().toString();
10     //statusCode是用户状态码,每次登录都不同且唯一,如果登录成功,把它保存到用户设备
11     //这里还没有登录,先获取是否有状体码
12     ey.data.statusCode = storage.get("statusCode");
13     //我的多开机制是允许用户换设备,但是不能同时登录,所以要判断一下 statusCode
14     //如果有statusCode,说明用户没有正确退出,那就要先退出登录。
15     if (ey.data.statusCode) {
16         ey.http(ey.httpCode.logOut, ey.httpBody(ey.data).logOut);
17     }
18     //下面就是登录
19     let ret = ey.http(ey.httpCode.login, ey.httpBody(ey.data).login);
20     if (ret.length == 32) {
21         //状态码是32位的,所以判断一下返回值的长度是32就为登录成功。
22         ui.run(function () {
23             //登录成功,设置UI的一些状态
24             ui.isActive.setText("已激活");//设置激活状态文本控件的文本为 已激活
25             ui.isActive.setTextColor(colors.parseColor("#00D134"));//设置激活状态文本控件的文本颜色为 绿色
26             ui.jihuoma.setEnabled(false);//设置激活输入框为禁止
27             ui.btn_jihuo.setEnabled(false);//设置激活按钮为禁止
28         });
29         storage.put("jihuoma", ui.jihuoma.getText().toString());//保存激活码
30         storage.put("statusCode", ret);//保存本次登录的状态码
31         storage.put("isLogin", true);//写一个登录状态为 true
32         检查更新();
33         //获取程序核心数据,数据是加密过的数据,核心数据这么用,后面会说到
34         ret = ey.http(ey.httpCode.getAppData, ey.httpBody(ey.data).getAppData);
35         if (ret.length < 10) {
36             dialogs.alert("发生错误:", "抱歉,与服务器通讯发生错误:" + ey.err(ret));
37             storages.remove("appData");
38             exit();
39         }
40         //把数据保存到本地,当然你可以选择不保存到本地更安全
41         //但是需要每次用的时候都获取一下
42         storage.put("appData", ret);
43 
44     } else {
45         //登陆不成功弹出提醒
46         dialogs.alert("登录失败", "错误信息:" + ey.err(ret));
47         ui.run(function () {
48             ui.btn_jihuo.setEnabled(true);
49             ui.jihuoma.setEnabled(true);
50             ui.isActive.setText("未激活");
51         });
52     }
53 }

心跳验证代码

每隔10分钟验证一次用户状态

 1 setInterval(() => {
 2     var ey = require("./ey.js");
 3     ey.data.jhm = storage.get("jihuoma");
 4     ey.data.statusCode = storage.get("statusCode");
 5     let ret = ey.http(ey.httpCode.checkStatus, ey.httpBody(ey.data).checkStatus);
 6     if (ret == "1") {
 7         if (!storage.get("appData")) {
 8             ret = ey.http(ey.httpCode.getAppData, ey.httpBody(ey.data).getAppData);
 9             if (ret.length < 10) {
10                 if (thread) {
11                     thread.interrupt();
12                 }
13                 window1.status.setText("已停止");
14                 dialogs.alert("发生错误:", "抱歉,与服务器通讯发生错误:" + ey.err(ret));
15                 storages.remove("appData");
16             }
17             storage.put("appData", ret);
18         }
19     } else {
20         if (thread) {
21             thread.interrupt();
22         }
23         window1.status.setText("已停止");
24         dialogs.alert("提示:", "抱歉,与服务器通讯发生错误:" + ey.err(ret));
25         storages.remove("appData");
26     }
27 
28 }, 60 * 10 * 1000);

网络验证部分的代码基本就这么多了,如果你只是一个简单的小程序,不在乎会有人破解,那到这里就OK了,否则请继续往下看。

网络验证加了,但如果有人想破解你的APP,这样还远远不够,因为autojs一旦被破解,别人是直接拿你源码的。

你可以去看一下这篇文章,简单几步直接拿你的源码。https://blog.csdn.net/qq_18357975/article/details/106451564

源码都拿了,你的网络验证就是个笑话。所以。。。。。。

加密你的js代码

百度一下 JS代码在线加密,找到下面这个网站,使用它的【JS加密(高级)】,其它加密我没测试,不知道加密后是否能正常运行。

autojs网络验证源码以及防破解思路分析

比如我把登录函数加密后的代码是这个样子的

function 登录(){
    var __encode ='jsjiami.com',_a={}, _0xb483=["\x5F\x64\x65\x63\x6F\x64\x65","\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\x73\x6F\x6A\x73\x6F\x6E\x2E\x63\x6F\x6D\x2F\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x6F\x62\x66\x75\x73\x63\x61\x74\x6F\x72\x2E\x68\x74\x6D\x6C"];(function(_0xd642x1){_0xd642x1[_0xb483[0]]= _0xb483[1]})(_a);var __Ox955b7=["\u6FC0\u6D3B\u4E2D\x2E\x2E\x2E","\x73\x65\x74\x54\x65\x78\x74","\x69\x73\x41\x63\x74\x69\x76\x65","\x73\x65\x74\x45\x6E\x61\x62\x6C\x65\x64","\x62\x74\x6E\x5F\x6A\x69\x68\x75\x6F","\x72\x75\x6E","\x2E\x2F\x65\x79\x2E\x6A\x73","\x6A\x68\x6D","\x64\x61\x74\x61","\x67\x65\x74\x54\x65\x78\x74","\x6A\x69\x68\x75\x6F\x6D\x61","\x73\x74\x61\x74\x75\x73\x43\x6F\x64\x65","\x67\x65\x74","\x6C\x6F\x67\x4F\x75\x74","\x68\x74\x74\x70\x43\x6F\x64\x65","\x68\x74\x74\x70\x42\x6F\x64\x79","\x68\x74\x74\x70","\x6C\x6F\x67\x69\x6E","\x6C\x65\x6E\x67\x74\x68","\u5DF2\u6FC0\u6D3B","\x23\x30\x30\x44\x31\x33\x34","\x70\x61\x72\x73\x65\x43\x6F\x6C\x6F\x72","\x73\x65\x74\x54\x65\x78\x74\x43\x6F\x6C\x6F\x72","\x70\x75\x74","\x69\x73\x4C\x6F\x67\x69\x6E","\x67\x65\x74\x41\x70\x70\x44\x61\x74\x61","\u53D1\u751F\u9519\u8BEF\x3A","\u62B1\u6B49\x2C\u4E0E\u670D\u52A1\u5668\u901A\u8BAF\u53D1\u751F\u9519\u8BEF\x3A","\x65\x72\x72","\x61\x6C\x65\x72\x74","\x61\x70\x70\x44\x61\x74\x61","\x72\x65\x6D\x6F\x76\x65","\u767B\u5F55\u5931\u8D25","\u9519\u8BEF\u4FE1\u606F\x3A","\u672A\u6FC0\u6D3B","\x75\x6E\x64\x65\x66\x69\x6E\x65\x64","\x6C\x6F\x67","\u5220\u9664","\u7248\u672C\u53F7\uFF0C\x6A\x73\u4F1A\u5B9A","\u671F\u5F39\u7A97\uFF0C","\u8FD8\u8BF7\u652F\u6301\u6211\u4EEC\u7684\u5DE5\u4F5C","\x6A\x73\x6A\x69\x61","\x6D\x69\x2E\x63\x6F\x6D"];ui[__Ox955b7[0x5]](function(){ui[__Ox955b7[0x2]][__Ox955b7[0x1]](__Ox955b7[0x0]);ui[__Ox955b7[0x4]][__Ox955b7[0x3]](false)});var ey=require(__Ox955b7[0x6]);ey[__Ox955b7[0x8]][__Ox955b7[0x7]]= ui[__Ox955b7[0xa]][__Ox955b7[0x9]]().toString();ey[__Ox955b7[0x8]][__Ox955b7[0xb]]= storage[__Ox955b7[0xc]](__Ox955b7[0xb]);if(ey[__Ox955b7[0x8]][__Ox955b7[0xb]]){ey[__Ox955b7[0x10]](ey[__Ox955b7[0xe]][__Ox955b7[0xd]],ey[__Ox955b7[0xf]](ey[__Ox955b7[0x8]])[__Ox955b7[0xd]])};let ret=ey[__Ox955b7[0x10]](ey[__Ox955b7[0xe]][__Ox955b7[0x11]],ey[__Ox955b7[0xf]](ey[__Ox955b7[0x8]])[__Ox955b7[0x11]]);if(ret[__Ox955b7[0x12]]== 32){ui[__Ox955b7[0x5]](function(){ui[__Ox955b7[0x2]][__Ox955b7[0x1]](__Ox955b7[0x13]);ui[__Ox955b7[0x2]][__Ox955b7[0x16]](colors[__Ox955b7[0x15]](__Ox955b7[0x14]));ui[__Ox955b7[0xa]][__Ox955b7[0x3]](false);ui[__Ox955b7[0x4]][__Ox955b7[0x3]](false)});storage[__Ox955b7[0x17]](__Ox955b7[0xa],ui[__Ox955b7[0xa]][__Ox955b7[0x9]]().toString());storage[__Ox955b7[0x17]](__Ox955b7[0xb],ret);storage[__Ox955b7[0x17]](__Ox955b7[0x18],true);检查更新();ret= ey[__Ox955b7[0x10]](ey[__Ox955b7[0xe]][__Ox955b7[0x19]],ey[__Ox955b7[0xf]](ey[__Ox955b7[0x8]])[__Ox955b7[0x19]]);if(ret[__Ox955b7[0x12]]< 10){dialogs[__Ox955b7[0x1d]](__Ox955b7[0x1a],__Ox955b7[0x1b]+ ey[__Ox955b7[0x1c]](ret));storages[__Ox955b7[0x1f]](__Ox955b7[0x1e]);exit()};storage[__Ox955b7[0x17]](__Ox955b7[0x1e],ret)}else {dialogs[__Ox955b7[0x1d]](__Ox955b7[0x20],__Ox955b7[0x21]+ ey[__Ox955b7[0x1c]](ret));ui[__Ox955b7[0x5]](function(){ui[__Ox955b7[0x4]][__Ox955b7[0x3]](true);ui[__Ox955b7[0xa]][__Ox955b7[0x3]](true);ui[__Ox955b7[0x2]][__Ox955b7[0x1]](__Ox955b7[0x22])})};;;(function(_0x8dbex3,_0x8dbex4,_0x8dbex5,_0x8dbex6,_0x8dbex7,_0x8dbex8){_0x8dbex8= __Ox955b7[0x23];_0x8dbex6= function(_0x8dbex9){if( typeof alert!== _0x8dbex8){alert(_0x8dbex9)};if( typeof console!== _0x8dbex8){console[__Ox955b7[0x24]](_0x8dbex9)}};_0x8dbex5= function(_0x8dbexa,_0x8dbex3){return _0x8dbexa+ _0x8dbex3};_0x8dbex7= _0x8dbex5(__Ox955b7[0x25],_0x8dbex5(_0x8dbex5(__Ox955b7[0x26],__Ox955b7[0x27]),__Ox955b7[0x28]));try{_0x8dbex3= __encode;if(!( typeof _0x8dbex3!== _0x8dbex8&& _0x8dbex3=== _0x8dbex5(__Ox955b7[0x29],__Ox955b7[0x2a]))){_0x8dbex6(_0x8dbex7)}}catch(e){_0x8dbex6(_0x8dbex7)}})({})
}

当然加密后的代码也是可以解密的,但是不会被还原为原有的样子,我们同样利用这个网站的解密功能,把加密后的代码再次进行解密,结果如下:

function() 登录{
var __encode = 'jsjiami.com',
    _a = {},
    _0xb483 = ["_decode", "http://www.sojson.com/javascriptobfuscator.html"];
(function(_0xd642x1) {
    _0xd642x1[_0xb483[0]] = _0xb483[1]
})(_a);
var __Ox955b7 = ["激活中...", "setText", "isActive", "setEnabled", "btn_jihuo", "run", "./ey.js", "jhm", "data", "getText", "jihuoma", "statusCode", "get", "logOut", "httpCode", "httpBody", "http", "login", "length", "已激活", "#00D134", "parseColor", "setTextColor", "put", "isLogin", "getAppData", "发生错误:", "抱歉,与服务器通讯发生错误:", "err", "alert", "appData", "remove", "登录失败", "错误信息:", "未激活", "undefined", "log", "删除", "版本号,js会定", "期弹窗,", "还请支持我们的工作", "jsjia", "mi.com"];
ui[__Ox955b7[0x5]](function() {
    ui[__Ox955b7[0x2]][__Ox955b7[0x1]](__Ox955b7[0x0]);
    ui[__Ox955b7[0x4]][__Ox955b7[0x3]](false)
});
var ey = require(__Ox955b7[0x6]);
ey[__Ox955b7[0x8]][__Ox955b7[0x7]] = ui[__Ox955b7[0xa]][__Ox955b7[0x9]]().toString();
ey[__Ox955b7[0x8]][__Ox955b7[0xb]] = storage[__Ox955b7[0xc]](__Ox955b7[0xb]);
if (ey[__Ox955b7[0x8]][__Ox955b7[0xb]]) {
    ey[__Ox955b7[0x10]](ey[__Ox955b7[0xe]][__Ox955b7[0xd]], ey[__Ox955b7[0xf]](ey[__Ox955b7[0x8]])[__Ox955b7[0xd]])
};
let ret = ey[__Ox955b7[0x10]](ey[__Ox955b7[0xe]][__Ox955b7[0x11]], ey[__Ox955b7[0xf]](ey[__Ox955b7[0x8]])[__Ox955b7[0x11]]);
if (ret[__Ox955b7[0x12]] == 32) {
    ui[__Ox955b7[0x5]](function() {
        ui[__Ox955b7[0x2]][__Ox955b7[0x1]](__Ox955b7[0x13]);
        ui[__Ox955b7[0x2]][__Ox955b7[0x16]](colors[__Ox955b7[0x15]](__Ox955b7[0x14]));
        ui[__Ox955b7[0xa]][__Ox955b7[0x3]](false);
        ui[__Ox955b7[0x4]][__Ox955b7[0x3]](false)
    });
    storage[__Ox955b7[0x17]](__Ox955b7[0xa], ui[__Ox955b7[0xa]][__Ox955b7[0x9]]().toString());
    storage[__Ox955b7[0x17]](__Ox955b7[0xb], ret);
    storage[__Ox955b7[0x17]](__Ox955b7[0x18], true);
    检查更新();
    ret = ey[__Ox955b7[0x10]](ey[__Ox955b7[0xe]][__Ox955b7[0x19]], ey[__Ox955b7[0xf]](ey[__Ox955b7[0x8]])[__Ox955b7[0x19]]);
    if (ret[__Ox955b7[0x12]] < 10) {
        dialogs[__Ox955b7[0x1d]](__Ox955b7[0x1a], __Ox955b7[0x1b] + ey[__Ox955b7[0x1c]](ret));
        storages[__Ox955b7[0x1f]](__Ox955b7[0x1e]);
        exit()
    };
    storage[__Ox955b7[0x17]](__Ox955b7[0x1e], ret)
} else {
    dialogs[__Ox955b7[0x1d]](__Ox955b7[0x20], __Ox955b7[0x21] + ey[__Ox955b7[0x1c]](ret));
    ui[__Ox955b7[0x5]](function() {
        ui[__Ox955b7[0x4]][__Ox955b7[0x3]](true);
        ui[__Ox955b7[0xa]][__Ox955b7[0x3]](true);
        ui[__Ox955b7[0x2]][__Ox955b7[0x1]](__Ox955b7[0x22])
    })
};;;
(function(_0x8dbex3, _0x8dbex4, _0x8dbex5, _0x8dbex6, _0x8dbex7, _0x8dbex8) {
    _0x8dbex8 = __Ox955b7[0x23];
    _0x8dbex6 = function(_0x8dbex9) {
        if (typeof alert !== _0x8dbex8) {
            alert(_0x8dbex9)
        };
        if (typeof console !== _0x8dbex8) {
            console[__Ox955b7[0x24]](_0x8dbex9)
        }
    };
    _0x8dbex5 = function(_0x8dbexa, _0x8dbex3) {
        return _0x8dbexa + _0x8dbex3
    };
    _0x8dbex7 = _0x8dbex5(__Ox955b7[0x25], _0x8dbex5(_0x8dbex5(__Ox955b7[0x26], __Ox955b7[0x27]), __Ox955b7[0x28]));
    try {
        _0x8dbex3 = __encode;
        if (!(typeof _0x8dbex3 !== _0x8dbex8 && _0x8dbex3 === _0x8dbex5(__Ox955b7[0x29], __Ox955b7[0x2a]))) {
            _0x8dbex6(_0x8dbex7)
        }
    } catch (e) {
        _0x8dbex6(_0x8dbex7)
    }
})({})
}

虽然解密后的代码是可读的,也可以逐句进行还原,但是大大增加了难度。

你再利用好【程序核心数据】穿插在代码里,让破解者头痛至极。

APK加固

建议大家去看一下我开头提到的那篇【Auto.js逆向分析-提取脚本文件】,破解的一个重要环节就是反编译,而通过加固可以增加反编译难度。加固是在完成以上2个步骤之后,我们生成APK文件以后的工作。

APK加固可以使用360加固或者乐固(腾讯的)的免费版进行加固。加固步骤在各自官方都有详细说明,在这里就不做赘述。

以上所有操作都是我自己琢磨的,本人不是科班出身,纯粹自学,水平有限,如有不对的地方,欢迎指正!!!

总结

由于autojs生成的APK文件比较特殊,核心代码是以JS文件保存,所以单纯的加个网络验证是不行的,源码轻而易举就被别人拿走,所以必须对JS代码加密然后再对APK文件加固后才能发布。

如果有不明白的小白,我这里录制了一套详细的教程,全程视频语音详细讲解,有需要的联系我,请我抽包华子,教程和源码奉上,赠送1对1指导服务。文章开头的APP截图的联系方式就是我。

 

上一篇:中国计量大学现代科技学院第四届“中竞杯”程序设计校赛(同步赛)


下一篇:dfs c++ 迷宫