破解闪讯详细原理
前言
博主曾在大二下学期的时候写了一个闪讯无线助手(Android端)用来破解闪讯,拨号路由器发出无线。当时在闪讯吧发了一个相关帖子免费提供给大家使用,同时也力所能及的回答吧友的问题。之后将闪讯无线助手在豌豆荚和应用宝上架。当初写好时候就想着之后要写一篇具体实现教程同时开源,也答应了很多人会开源。本来想着在学校的时候就把这件事完成,没想到之后事情比较多,被什么实习、工作、毕业一直拖着。一晃眼现在已经毕业了,再不开始搞估计就鸽了,趁着刚毕业还不怎么忙的时候先把这件事搞定。ps:之前实习的时候比较忙,qq和贴吧上问的问题我都忘了回,在这说声不好意思。
我还是简单的讲一下破解闪讯路由拨号是什么意思,其实就是让路由器拨号闪讯账号发出无线,实现多台设备上网。
敲黑板划重点 闪讯无线助手源码github地址
闪讯无线助手豌豆荚地址
闪讯无线助手应用宝地址
ps:上面是闪讯无线助手Android端源码,我也写了一个闪讯无线助手ios端,不过第一次写ios代码写的十分混乱,如果不介意这些想要ios端的代码话可以在下面留言。
闪讯破解原理
破解整体过程
众所周知路由器有一个管理后台比如TPLINK的192.168.1.1,在管理后台页面中我们可以找到一个拨号界面,正常情况下我们都是输入宽带账号密码来拨号路由。现在要拨号闪讯,直接输入闪讯账号和密码是拨号不成功的。因为闪讯拨号的时候会对闪讯账号进行加密处理,要使用加密之后的闪讯账号进行拨号。闪讯账号加密是根据当前时间动态加密的,手动在路由器拨号界面输入加密之后的闪讯账号时间上会来不及,因而要通过软件进行模拟拨号。
现在来总结下破解过程,第一步先将闪讯账号进行相应的加密,第二步将加密之后的闪讯账号和闪讯密码通过软件模拟路由器拨号进行拨号。整体流程如下图所示
闪讯账号加密实现
闪讯账号加密整体流程如下图所示
这里给出Java版的闪讯账号加密代码,其他语言版本的大家可以自己发挥。ps:要object-c版的可以在下面留言
/**
* 获取加密后的闪讯账号
*
* @return
*/
public String getRealName(String username, long lasttime) {
if (TextUtils.isEmpty(username)) {
return null;
}
mUserName = username;
mLastTime = lasttime;
Calendar calendar = Calendar.getInstance();
long mTime1c;
long mTime1convert;
byte[] ss = new byte[]{0, 0, 0, 0};// unsigned char byte
byte[] ss2 = new byte[]{0, 0, 0, 0};
String strS1 = "";
String mFormatsring = "";
String mMd5 = "";
String mMd5use = "";
{
long t;
t = calendar.getTimeInMillis() / 1000;// 得到系统时间
t *= 0x66666667;
t >>= 0x20;
t >>= 0x01;
mTime1c = (long) t;
}
if (mTime1c <= mLastTime) {
mTime1c = mLastTime + 1;
}
mLastTime = mTime1c;
{
long t;
t = mTime1c;
ss2[3] = (byte) (t & 0xFF);
ss2[2] = (byte) ((t & 0xFF00) / 0x100);
ss2[1] = (byte) ((t & 0xFF0000) / 0x10000);
ss2[0] = (byte) ((t & 0xFF000000) / 0x1000000);
{
try {
strS1 = new String(ss2, "ISO-8859-1");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
{
int t, t1, t2, t3;
t = (int) mTime1c;
t1 = t;
t2 = t;
t3 = t;
t3 = t3 << 0x10;
t1 = t1 & 0x0FF00;
t1 = t1 | t3;
t3 = t;
t3 = t3 & 0x0FF0000;
t2 = t2 >> 0x10;
t3 = t3 | t2;
t1 = t1 << 0x08;
t3 = t3 >> 0x08;
t1 = t1 | t3;
mTime1convert = t1;
}
{
long t;
t = mTime1convert;
ss[3] = (byte) (t & 0xFF);
ss[2] = (byte) ((t & 0xFF00) / 0x100);
ss[1] = (byte) ((t & 0xFF0000) / 0x10000);
ss[0] = (byte) ((t & 0xFF000000) / 0x1000000);
}
/**
* sun ss byte !负数
*/
int ssInt[] = new int[]{0, 0, 0, 0};
{
ssInt[3] = (int) (ss[3] & 0xff);
ssInt[2] = (int) (ss[2] & 0xff);
ssInt[1] = (int) (ss[1] & 0xff);
ssInt[0] = (int) (ss[0] & 0xff);
}
byte[] pp = new byte[]{0, 0, 0, 0};
{
int i = 0, j = 0, k = 0;
for (i = 0; i < 0x20; i++) {
j = i / 0x8;
k = 3 - (i % 0x4);
pp[k] *= 0x2;
if (ssInt[j] % 2 == 1) {
pp[k]++;
}
ssInt[j] /= 2;
}
}
/**
* sun pp byte !负数
*/
int ppInt[] = new int[]{0, 0, 0, 0};
{
ppInt[3] = (int) (pp[3] & 0xff);
ppInt[2] = (int) (pp[2] & 0xff);
ppInt[1] = (int) (pp[1] & 0xff);
ppInt[0] = (int) (pp[0] & 0xff);
}
/**
* 格式符计算,mFormatsring为结果
*/
byte[] pf = new byte[]{0, 0, 0, 0, 0, 0};
{
short t1, t2;
t1 = (short) ppInt[3];
t1 /= 0x4;
pf[0] = (byte) t1;
t1 = (short) ppInt[3];
t1 = (short) (t1 & 0x3);
t1 *= 0x10;
pf[1] = (byte) t1;
t2 = (short) ppInt[2];
t2 /= 0x10;
t2 = (short) (t2 | t1);
pf[1] = (byte) t2;
t1 = (short) ppInt[2];
t1 = (short) (t1 & 0x0F);
t1 *= 0x04;
pf[2] = (byte) t1;
t2 = (short) ppInt[1];
t2 /= 0x40;
t2 = (short) (t2 | t1);
pf[2] = (byte) t2;
t1 = (short) ppInt[1];
t1 = (short) (t1 & 0x3F);
pf[3] = (byte) t1;
t2 = (short) ppInt[0];
t2 /= 0x04;
pf[4] = (byte) t2;
t1 = (short) ppInt[0];
t1 = (short) (t1 & 0x03);
t1 *= 0x10;
pf[5] = (byte) t1;
}
{
int i;
for (i = 0; i < 6; i++) {
pf[i] += 0x20;
if ((pf[i]) >= 0x40) {
pf[i]++;
}
}
}
{
for (int i = 0; i < 6; i++) {
// mFormatsring += pf[i];
mFormatsring += (char) ((int) (pf[i] & 0xff));
}
}
String strInput;
strInput = strS1 + mUserName.split("@")[0] + RADIUS;
try {
mMd5 = getMD5(strInput, "ISO-8859-1");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
mMd5use = mMd5.substring(0, 2);
mReallyUserName = mFormatsring + mMd5use.toLowerCase() + mUserName;
return mReallyUserName;
}
路由器模拟拨号
模拟路由器拨号主要是通过对路由器管理员平台的拨号页面(tplink的拨号界面一般是http://192.168.1.1/userRpm/PPPoECfgRpm.htm)抓包,然后发送模拟请求来完成,mac用户可以使用charles来抓包,Windows用户可以使用ie自带的开发者工具来抓包。
在这里说下几个注意点:1.路由器管理后台登录是抓不了包的,因为路由器管理员登录是通过http basic authorization认证的,简单的来讲就是将管理员账号和密码拼接起来然后进行BASE64加密,将加密之后的结果放到http请求头中。2.要设置http referer请求头为路由器管理后台地址,因为路由器拨号的时候会通过referer来判断当前请求之前的页面是否为路由器管理后台,不设置的话会返回You have no authority to access this device!。因而通过js或微信小程序来模拟拨号都是行不通的(博主两个都尝试过了-_-),js和微信小程序都不允许用户设置请求的referer,大家也可以找找有没有其他方法突破这个限制。
模拟路由拨号整体流程如下图所示
模拟路由拨号请求代码
/**
* 拨号
*
* @param routerName
* @param routerPwd
* @param shanXunName
* @param shanXunPwd
* @return
*/
public int dial(String routerName, String routerPwd, String shanXunName, String shanXunPwd) {
if (TextUtils.isEmpty(routerName) || TextUtils.isEmpty(routerPwd) || TextUtils.isEmpty(shanXunName) || TextUtils.isEmpty(shanXunPwd)) {
sErrCode = -1;
return NetConstant.ERR;
}
int r;
shanXunName = "\r\n" + SXRealName.getInstance().getRealName(shanXunName, 0);
String acc = str2HexStr(shanXunName);
String str = routerName + ":" + routerPwd;
String authorization = new Base64().encode(str.getBytes());
String path = "http://192.168.1.1/userRpm/PPPoECfgRpm.htm?wan=0&wantype=2&acc=" + acc +
"&psw=" + shanXunPwd + "&confirm=" + shanXunPwd + "&specialDial=100&SecType=1&sta_ip=0.0.0.0&sta_mask=0.0.0.0&linktype=4&waittime2=0&Connect=%C1%AC+%BD%D3";
try {
URL url = new URL(path);
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestProperty("Authorization", "Basic " + authorization);
con.setRequestMethod("GET");
con.setRequestProperty("Host", "192.168.1.1");
con.setRequestProperty("Referer", "http://192.168.1.1/userRpm/PPPoECfgRpm.htm?wan=0&wantype=2&acc=" + acc + "&psw=" + shanXunPwd +
"&confirm=" + shanXunPwd + "&SecType=1&sta_ip=0.0.0.0&sta_mask=0.0.0.0&linktype=4&waittime2=0&Disconnect=%B6%CF+%CF%DF");
con.setRequestProperty("Cookie", "Authorization=Basic " + authorization);
String result = getHttpResponse(con);
if (result == null) {
sErrCode = -2;
return NetConstant.ERR;
} else if ("401".equals(result)) {
return NetConstant.ROUTER_LOGIN_FAIL;
}
String[] strs = result.split("Hello123World");
if (strs.length >= 2) {
strs = strs[1].split(",");
if (strs.length >= 19) {
r = Integer.valueOf(strs[18].trim());
Log.d(TAG, "dial: " + strs[18].trim());
} else {
sErrCode = -3;
return NetConstant.ERR;
}
} else {
sErrCode = -4;
return NetConstant.ERR;
}
} catch (Exception e) {
e.printStackTrace();
sErrCode = -5;
r = NetConstant.ERR;
}
// Log.d(TAG, "dial: " + shanXunName);
return r;
}
Emmmmmm 两个关键的部分都说了,有什么补充我会直接在这篇教程上加的,源码在github地址,大家可以自己动手看看,有什么问题可以给我留言哈!
如果教程博客、源码对你有所帮助可以给个star。
更多推荐
闪讯无线助手-Android端闪讯破解路由器拨号实现原理
发布评论