看代码啊
玉玉公主
-
领创api和stu_id获取方法、动态密码完整poc -
9月15日 ky讲话摘要(转自空间)。:烤鸭女士今日五班演讲金句如下
“你知不知道能够上课是多么不容易,我们好不容易才给你们争取到不放假的机会”
“下课了难道就能发出声音了吗?”
“你是班主任就能在自己的班上讲话了吗?” -
这个世界需要一个掀起浪潮的人水!!!
-
论坛终于可以访问了所以你能看看我的帖子吗
领创api和stu_id获取方法、动态密码完整poc
领创动态密码算法解析 -
历史区老物:图书馆志愿经历其二还有某些地方不合时宜的书
-
历史区老物:图书馆志愿经历其二还有某些地方不合时宜的书
-
历史区老物:图书馆志愿经历其二还有某些地方不合时宜的书
-
历史区老物:图书馆志愿经历其二还有某些地方不合时宜的书
-
历史区老物:图书馆志愿经历其二还有某些地方不合时宜的书
-
领创api和stu_id获取方法、动态密码完整poc完整代码已经在Github更新,参见此链接
由于时间原因暂不做讲解,有疑问可以提出,我尽量答复 -
一张神秘贴纸这种东西用英文写感觉没啥用啊,能看懂的基本不会被这些“主义”桎梏
-
一张神秘贴纸反****,反****,反****,反****,奇怪,
---数据删除--- -
领创动态密码算法解析@会说话的三角龙 不是。stu id要通过api获取
-
领创动态密码算法解析领创动态密码算法解析
最近发现有人似乎存在忘记密码等存在相关问题,我干脆吧22年底研究的东西放出来吧,反正这东西我早就不搞了
0. 声明
本报告仅用于技术交流,严禁用于非法用途。
0.0 目录
1. 算法介绍
该算法用于生成领创动态密码。动态密码可用于跳过手势密码、退出登录、锁机解锁。
2. 算法原理
加盐md5
3. 算法解析
预备材料:
- apk安装包
- 这里选用5.03的安装包,实际上5和6的算法是相同的,但jadx在反编译6时可能会出现失败只给伪代码的情况,虽然不影响分析,但不美观。
- jadx
- 使用搜索引擎的能力
3.1 反编译
丢到jadx里面,开启反混淆
3.2 分析
3.2.1 代码定位
按照逆向常规
尤其是破jie,反编译后第一步就是定位代码位置。定位方法很多,一般来说,字符串定位是最常用且最简单的,简单步骤此处不赘述。3.2.2 代码分析
3.2.2.1 顺藤摸瓜
public boolean m4493a(String str) { int m3546b = f2681c.m3546b("dynamic_pass", 1); String m4491c = m4491c(); String m4495a = m4495a(); if (m3546b == 0) { if (str.equals(m4491c) || MD5Util.m3060a(str).equals(m4495a)) { return true; } } else if (m3546b == 1 && str.equals(m4491c)) { return true; } return false; } public String m4495a() { this.f2683e = f2681c.m3544b("password2", ""); return !TextUtils.isEmpty(this.f2683e) ? this.f2683e : "unknown"; }
m4493a()判断输入的密码是否正确的函数。
这里我们不难发现,返回true有两个条件:- 当策略未启用动态密码时,使用固定的管理员密码(我们的是huananfz1!)
- 启用动态密码,则先通过函数m4491c计算动态密码,然后与输入的密码进行比较
3.2.2.2 外围代码分析
算法其实很简单
private String m4491c() { String m4492b = m4492b(); if (m4492b.length() >= 6) { return m4492b.substring(0, 6); } return m4492b; }
我们可以看到,m4491c只是截取了算完后的六个字符作为最终结果。这里的判断理论上始终为true,因为m4492b是从md5得来的,具我们经过下面的分析后就会明白。
public String m4492b() { String m2965a = LTKDeviceUtil.m2965a(f2680b); if (!TextUtils.isEmpty(m2965a) && !"unknown".equals(m2965a)) { this.f2682d = SettingUtil.m3539a(f2680b).m3538a(m2965a); } return !TextUtils.isEmpty(this.f2682d) ? this.f2682d : "unknown"; } public static String m2965a(Context context) { if ((LTKDeviceTypeUtil.m3011i() || LTKDeviceTypeUtil.m3021d()) && Build.VERSION.SDK_INT >= 29) { return m2947g(context).toLowerCase(); } String m2951e = m2951e(context); if (!m2951e.contains("unknown")) { new LTKSpfUtil(context, "linslib").m2858a("wifi_mac", m2951e); return m2951e; } String m2857b = new LTKSpfUtil(context, "linslib").m2857b("wifi_mac", "unknown"); if (m2857b.equals("unknown")) { String m2960b = m2960b(context); return m2960b.equals("unknown") ? m2954d(context) : m2960b; } return m2857b; }
我们先来看m2965a的来龙去脉。如果是安卓10+,,且设备类型为华为等,那么这里会选用sn(设备序列号),否则用wifi mac地址。
我们不管那么多,反正都一样。现在假定返回的是sn,进入核心代码。
3.2.2.3 核心代码分析
public String m3538a(String str) { Log.i("CalculatePwd", "pwd is start"); String str2 = new SimpleDateFormat("yyyyMMdd").format(new Date()) + str + "40E06F51-30D0-D6AD-7F7D-008AD0ADC570"; String m5067X = UserInfoUtil.m5026f().m5067X(); if (!TextUtils.isEmpty(m5067X)) { str2 = str2 + m5067X; } if (!TextUtils.isEmpty(str2)) { String m3537b = m3537b(str2); if (m3537b.length() > 8) { String m3536c = m3536c(m3537b.substring(m3537b.length() - 8, m3537b.length())); Log.i("CalculatePwd", "pwd is start"); return m3536c; } } return ""; } /*UserInfoUtil*/ public String m5067X() { if (TextUtils.isEmpty(this.f2139G)) { this.f2139G = f2132H.m3544b("student_id", ""); } return this.f2139G; } public String m3537b(String str) { Log.i("CalculatePwd", "toMd5 is start"); /* 算md5的,省略 */ }
我们发现,这里首先将时间字符串、给定的str(即sn)和一串特殊字符连接起来,然后再从UserInfoUtil中获取student_id,加入字符串并计算md5。
student id在登录时调api获取并保存到本地,这里暂不做讲解,等下周再开一个帖子来说
显然,这里的m3537b肯定大于8个字符,所以这里会截取后8个字符转换为long
public static String m3536c(String str) { Log.i("CalculatePwd", "hexToLong is start"); String valueOf = String.valueOf(Long.parseLong(str, 16)); if (!TextUtils.isEmpty(valueOf) && valueOf.length() > 8) { return valueOf.substring(valueOf.length() - 8, valueOf.length()); } return valueOf; }
一样的,转换为long之后如果太长,也截取后8个字符。
这样我们就得到了8位数字,再回到前面,这8位数的前6位就是最终的密码。
3.2.3 梳理
- 集齐设备信息、日期、student_id
- 加盐计算md5
- 截取,转数字,再截取
是不是很简单?接下来我们试着自己写出完整的计算算法。
3.3 算法复现
知道了原理,写起来是很轻松的
代码已经开源在Github,点此链接查看,
这里也放上完整代码/* Author: SuchAnIdi0t *** !!NOTICE!! * These codes are opensourced under MIT License. * Modifications and redistribution should be in compliance with the License. * PoC only, use at your own risk. For more information, please refer to https://github.com/SuchAnIdi0t/linspirer-dynamic-password */ package main import ( "crypto/md5" "fmt" "log" "strconv" "strings" "time" ) func ca(str1 string) string { str2, _ := strconv.ParseInt(str1, 16, 64) v := strconv.Itoa(int(str2)) if len(v) <= 8 { return v } else { return v[len(v)-8:] } } func calc(swdid string, stu_id string) string { str3 := time.Now().Format("20060102") + swdid + "40E06F51-30D0-D6AD-7F7D-008AD0ADC570" + stu_id log.Println(str3) h := md5.Sum([]byte(str3)) b := fmt.Sprintf("%x", h) log.Println(b) if len(b) > 8 { return ca(b[len(b)-8:]) } else { return "err" } } func main() { var swdid, stu_id string fmt.Print("swdid:") fmt.Scanln(&swdid) swdid = strings.ToLower(swdid) fmt.Print("stu_id:") fmt.Scanln(&stu_id) log.Printf("Result: %s", calc(swdid, stu_id)[:6]) }
4. 总结
动态密码的实现原理并不复杂,逆向也毫无阻碍,但其中理解涉及到的算法和逻辑还是需要一定耐心和基本能力。
有任何问题欢迎在此处探讨,点赞过5,下周讲如何获取student_id
- apk安装包
-
我的世界15周年庆典java+基岩半价仅44.5
15周年披风上线
tiktok twitch联名披风需要在对应平台领取
twitch在有表明奖励的直播间观看15分钟以上即可,tiktok注册比较麻烦,但是领取方法大致相同。 -
高三4班迎来化竞真神rt,转自qq空间
-
CS2更新日志(2024.5.1-5.7)5.9
MISC
- Reduced the opacity of teammate names near the crosshair.
- Made the death experience during bot takeover match the normal death experience.
- Fixed several bugs with looping through bot takeover spectator targets.
- Fixed several bugs with scoreboard buttons not working as designed.
- Fixed a bug where the in-air kill feed icon didn't work with certain weapons (thanks aquaismissing).
- Fixed a bug where the HUD would be offset after watching a demo (thanks again aquaismissing).
-
没什么人啊蹲
-
CS2更新日志(2024.5.1-5.7)