Chr_小屋 - 算法
type: channel
link: https://blog.chrxw.com/tag/算法/
items: 小黑盒逆向分析笔记（六）完结
items: 小黑盒逆向分析笔记（五）【未完成】
items: 【树莓派】简约令牌时钟
items: 小黑盒逆向分析笔记（四）

小黑盒逆向分析笔记（六）完结
type: item
date: 2023-04-03T12:10:00+08:00
description: 前言 本文仅供学习探讨之用，如果侵犯您的权益请联系我删除。 上文 [post cid="714"/] [post cid="1347"/] [post cid="1365"/] [post cid="1383"/] [post cid="1634"/] 前情提要 已经毕业半年多了，有关于小黑盒的开发也搁置很久了，感觉也没有那个精力继续开发了，记录一下最近的算法研究，就当给大学生活做个小结。 @> 本文适用于小黑盒 1.3.262，小黑盒历史版本参考我的仓库 @> 全文评论后可见

小黑盒逆向分析笔记（五）【未完成】
type: item
date: 2021-11-19T13:28:00+08:00
description: 前言 本文仅供学习探讨之用，如果侵犯您的权益请联系我删除。 上文 [post cid="714"/] [post cid="1347"/] [post cid="1365"/] [post cid="1383"/] 前情提要 距离上一篇文章已经过去一年多了，前两天心血来潮找了最新版本研究了一下，熬了两天夜也没整明白，不打算继续研究下去了，做个笔记记录下分析过程。 @> 本文适用于小黑盒 1.3.191，小黑盒历史版本参考我的仓库 @> 全文评论后可见

【树莓派】简约令牌时钟
type: item
date: 2021-04-04T01:17:00+08:00
description: 前言 总共花了4天休息时间做的小玩意，支持显示通用TOTP令牌(6位数字)和STEAM令牌(5位)，同时也是个桌面时钟。 源码获取：GitHub 令牌原理 i> 简单的介绍一下两个令牌的原理，不感兴趣的可以参考下一节 原理 令牌类似于短信验证码，是一种一次性密码OTP，核心原理很简单，服务器和用户事先约定好一个Secret，然后用户通过特殊的算法（一般用Secret + 时间戳或者计数器作为参数），得到一串字符，服务器也通过同样的算法得到一串字符，如果两者相同，那么就证明了我是我。 常见的令牌大多是基于时间的令牌TOTP，也就是用当前的时间戳作为输入，只要保证和服务端时间差不超过令牌有效期（常见的实现是30秒）就能确保通过验证。优点是令牌客户端可以不需要联网，缺点是可能因为时间差导致验证失败。 另一种实现是基于HMAC的令牌HOTP，这种令牌需要和服务器约定一个计数器，客户端刷新一次令牌，就告诉服务器，让两边的计数器同步，这样用户和服务器得到的结果才能一样，才能通过验证。缺点显而易见，就是令牌客户端需要联网。 STEAM的令牌系统类似于TOTP，也是基于时间戳的，只是算法略有区别，最明显的区别是普通TOTP计算得到的是6位数字，STEAM令牌是5位数字或字母。 x> Secret一定要保管好，千万不要向他人透露。 i> 完整代码位于utils/twofa.py TOTP详解 伪代码 #. 演示用的中间结果用【】 表示 0. 服务器和用户事先约定了一个密钥Secret，记为K【MNUHE6DXGEZDGNBVGY======】(Base32编码，也有的实现采用Base64编码） 1. 取当前时间戳/30，取整后记为C【53914423】 2. 使用C作为消息，K作为密钥，哈希方式一般为Sha-1，计算HMAC哈希值H【deb27e0ae7e70fc2d2527e6ed231967e26503f72】(16进制) 3. 取H最后一个字节的低4位，用作偏移，记为O【2】(即16进制C的最后一位) 4. 取H的第O到O+4字节【7e0ae7e7】，丢弃最高位，变为32位无符号整数，记为I【7e0ae7e7】(最高位本来就是0，所以不变) 5. 把I转成10进制数字【17602563747】，最低6位就是结果【643943】 Python实现 import hmac import hashlib from base64 import b32decode from time import time import struct def get_totp_auth_code(secret: str, t: int = None): if not t: t = int(time() / 30) key = b32decode(secret) msg = struct.pack(">Q", t) mac = hmac.new(key, msg, hashlib.sha1).digest() offset = mac[-1] & 0x0f binary = struct.unpack('>L', mac[offset:offset+4])[0] & 0x7fffffff code = str(binary)[-6:].zfill(6) return code # 演示DEMO print(get_totp_auth_code('MNUHE6DXGEZDGNBVGY======',53914423)) # 输出结果为 643943 Steam令牌详解 伪代码 #. 演示用的中间结果用【】 表示 0. 服务器和用户事先约定了一个密钥Secret，记为K【Y2hyeHcxMjM0NTY=】(Base64编码） 1. 取当前时间戳/30，先取整【53914423】，然后转换成8字节，前方补0，记为C【000000000336ab37】(16进制) 2. 使用C作为消息，K作为密钥，哈希方式一般为Sha-1，计算HMAC哈希值H【deb27e0ae7e70fc2d2527e6ed231967e26503f72】(16进制) 3. 取H最后一个字节的低4位，用作偏移，记为O【2】(即16进制C的最后一位) 4. 取H的第O到O+4字节【7e0ae7e7】，丢弃最高位，变为32位无符号整数，记为I【7e0ae7e7】(最高位本来就是0，所以不变)，为了方便计算转成10进制为【2114643943】 5. 创建一个字母表G【23456789BCDFGHJKMNPQRTVWXY】(去掉了一些容易混淆的字符) 6. 用I【2114643943】对26取余，记为M【9】，然后将I整除26，重复5次，得到5个M【9,13,7,12,25】(10进制) 7. 把5个M作为下…

小黑盒逆向分析笔记（四）
type: item
date: 2020-08-21T21:45:00+08:00
description: 前言 本文仅供学习探讨之用，如果侵犯您的权益请联系我删除。 上文 [post cid="714"/] [post cid="1347"/] [post cid="1365"/] 前情提要 就在我写完上一篇文章后没过一个礼拜，小黑盒就又改 hkey 计算方法了，断断续续花了一礼拜终于搞清楚它的算法。 在这里分享一下逆向的过程。 @> 本文适用于小黑盒 1.3.121 ~ 1.3.124 ，小黑盒历史版本参考我的仓库 Smali 分析 使用Jadx打开安装包，找到hkey的计算函数 可以看到两个版本的 hkey 计算方式略有区别，最明显的就是替换的字符改了 修改以后还是提示参数错误，肯定是 encode 函数也改了，接下来换用IDA So 分析 先贴一下IDA生成的反汇编代码，也不用看的很仔细 int __fastcall encode(int *a1, int a2, int a3, int a4, int a5) { int *v5; // r9 int v6; // r11 _DWORD *v7; // r4 char *v8; // r5 char *v9; // r0 size_t v10; // r0 int v11; // r10 int v12; // r0 int v13; // r0 _DWORD *v14; // r2 const char *v15; // r1 char *v16; // r10 const char *v17; // r11 size_t v18; // r4 size_t v19; // r4 signed int v20; // r8 char *v21; // r6 char *v22; // r0 int v23; // r12 int i; // lr int v25; // r5 int v26; // r0 unsigned int v27; // r4 int v28; // r3 unsigned int v29; // r1 int v30; // r8 const char *v31; // r4 int j; // r1 int k; // r0 int v34; // r0 size_t v35; // r5 size_t v36; // r6 char *v37; // r5 char *v38; // r0 int v39; // r1 int v40; // r12 int l; // lr int v42; // r6 int v43; // r0 unsigned int v44; // r4 int v45; // r3 unsigned int v46; // r2 int result; // r0 int v48; // [sp+0h] [bp-48h] int v49; // [sp+4h] [bp-44h] int *v50; // [sp+8h] [bp-40h] char *nptr; // [sp+Ch] [bp-3Ch] int v52; // [sp+10h] [bp-38h] _DWORD *v53; // [sp+14h] [bp-34h] char s; // [sp+19h] [bp-2Fh] int v55; // [sp+24h] [bp-24h] v5 = a1; v6 = a4; v7 = &_stack_chk_guard; if ( j_check_signature(a1) != 1 ) goto LABEL_28; v53 = &_stack_chk_guard; v8 = (char *)&v48 - ((strlen(KEYTWO[0]) + 62) & 0xFFFFFFF8); v9 = strcpy(v8, KEYTWO[0]); v10 = strlen(v9); _aeabi_memcpy(&v8[v10], "/xDu7EuCQfjaRk4TBKXrnhrlnkz@%$^&*(-_-)hahaha(-_-)_time=", 56); v7 = 0; v11 = (*(int (__fastcall **)(int *, int, _DWORD))(*v5 + 676))(v5, v6, 0); v12 = *v5; v52 = a5; v13 = (*(int (__fastcall **)(int *, int, _DWORD))(v12 + 676))(v5, a5, 0); if ( v11 ) { v14 = v53; if ( !v13 ) goto LABEL_26; v49 = v6; v50 = &v48; v15 = (const char *)v11; v16 = (cha…