Chr_小屋 - data
type: channel
link: https://blog.chrxw.com/tag/data/
items: 【油猴脚本教程】P2 初识油猴脚本 & 油猴脚本的基本构成
items: 安卓Frida RPC的一些简单应用
items: 小黑盒逆向分析笔记（五）【未完成】
items: 【树莓派】简约令牌时钟
items: 利用ASF自动添加Steam上的免费游戏

【油猴脚本教程】P2 初识油猴脚本 & 油猴脚本的基本构成
type: item
date: 2022-08-17T00:00:00+08:00
description: P2 初识油猴脚本 教程索引 视频链接 光讲JS感觉不是特别直观，用两个不涉及网络请求的脚本进行讲解 第一个脚本 - Remove Live.js 脚本链接： https://github.com/chr233/GM_Scripts/blob/master/Bilibili/Remove_Live.js 脚本功能： 提供一个开关，用于移除直播播放器，并且带有记忆功能 源码： // ==UserScript== // @name Remove_Live // @name:zh-CN 去除B站直播间播放器 // @namespace https://blog.chrxw.com // @version 1.1 // @description 去除B站多余的直播播放器（滑稽） // @author Chr_ // @include https://live.bilibili.com/* // @license AGPL-3.0 // @icon https://blog.chrxw.com/favicon.ico // ==/UserScript== (() => { "use strict"; //去除播放器的开关 let VEnable = localStorage.getItem("VEnable") === "true"; if (VEnable) { setTimeout(() => { document.getElementById("live-player").remove(); }, 3000); } let btnArea = document.querySelector(".right-ctnr"); let btn = document.createElement("button"); btn.textContent = VEnable ? "恢复播放器" : "移除播放器"; btn.addEventListener("click", () => { VEnable = !VEnable; localStorage.setItem("VEnable", VEnable); btn.textContent = VEnable ? "恢复播放器" : "移除播放器"; if (VEnable) { document.getElementById("live-player").remove(); } else { location.reload(); } }); btnArea.insertBefore(btn, btnArea.children[0]); })(); 第二个脚本 - SteamDB CN.js 脚本链接： https://github.com/chr233/GM_Scripts/blob/master/SteamDB/SteamDB_CN.js 脚本功能： 汉化SteamDB 源码： // ==UserScript== // @name:zh-CN SteamDB汉化 // @name SteamDB_CN // @namespace https://blog.chrxw.com // @supportURL https://blog.chrxw.com/scripts.html // @contributionURL https://afdian.net/@chr233 // @version 1.32 // @description SteamDB汉化插件 // @description:zh-cn SteamDB汉化插件 // @author Chr_ // @match https://steamdb.info/* // @license AGPL-3.0 // @icon https://blog.chrxw.com/favicon.ico // @resource data https://cf.chrxw.com/SteamDB/SteamDB_CN.json // @grant GM_addStyle // @grant GM_getResourceText // @grant GM_registerMenuCommand // ==/UserScript== (() => { "use strict"; //是否打开调试模式（使用自定义汉化规则而不是在线规则） const DEBUG = localStorage["dbg_mode"] == "开"; //是否在控制台输出未汉化文本 const OUTPUT = localStorage["out_word"] == "开"; GM_registerMenuCommand(`调试汉化文本: 【${DEBUG ? "开" : "关…

安卓Frida RPC的一些简单应用
type: item
date: 2021-11-19T19:35:00+08:00
description: 前言 本文仅供学习探讨之用，如果侵犯您的权益请联系我删除。 前几天研究某APP未果，中间稍微研究了一些Frida的RPC机制，做一下笔记。 Frida RPC简介 Frida RPC可以将APP中的函数暴露给加载器，方便在外部调用，相比于recv和send，使用起来更加方便。 示例hook脚本 rpc.exports = { add(a, b) { //同步函数 return a + b; }, sub(a, b) { //异步函数 return new Promise(resolve => { setTimeout(() => { resolve(a - b); }, 100); }); } }; 示例Python加载器 import codecs import frida def on_message(message, data): if message['type'] == 'send': print(message['payload']) elif message['type'] == 'error': print(message['stack']) session = frida.attach('iTunes') with codecs.open('./agent.js', 'r', 'utf-8') as f: source = f.read() script = session.create_script(source) script.on('message', on_message) script.load() #调用rpc暴露出来的add和sub方法 print(script.exports.add(2, 3)) print(script.exports.sub(5, 3)) session.detach() Frida RPC使用演示 Java层 Java函数的Hook又分为静态方法和实例方法，静态类直接用Java.use('类名')获取以后直接就可以调用，而实例方法需要先用Java.choose('类名')，在堆中查找到类的实例以后获取该实例，才能调用。 静态方法 实例方法 So层 SO层如果是静态方法也跟Java层的类似，只不过需要获取上下文对象，并作为第一个参数传入。 JNI方法 演示代码 Hook代码 console.log("脚本载入成功"); Java.perform(function () { rpc.exports = { staticmethod() { var n1 = Java.use('com.max.xiaoheihe.utils.n1'); return n1.E(); }, classmethod() { var result = undefined; Java.choose('com.max.xiaoheihe.bean.account.AccountDetailObj', { onMatch: function (instance) { result = instance.getUserid(); }, onComplete: function () { if (result === undefined) { return 'not found'; } } }); return result; }, jnimethod(urlpath, timestamp, nonce) { var currentApp = Java.use('android.app.ActivityThread').currentApplication(); var context = currentApp.getApplicationContext(); var NDKTools = Java.use('com.max.xiaoheihe.utils.NDKTools'); return NDKTools.encode(context, urlpath, timestamp, nonce); }, }; }); 加载器 import frida device = frida.get_usb_device() process = device.attach('小黑盒') with open('demo.js', 'r', encoding='utf-8') as f: script = process.create_script(js.read()) script.load() print('static method: ', script.exports.staticmethod()) print('class method: ', script.exports.classmethod()) prin…

小黑盒逆向分析笔记（五）【未完成】
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作为下…

利用ASF自动添加Steam上的免费游戏
type: item
date: 2021-03-12T23:00:52+08:00
description: 前言 最近开始关注Steam游戏的下架情报，付费游戏下架还可能买得到激活码，免费游戏下架绝大多数情况都没有办法收藏了，于是就有了这个能够自动添加免费游戏的项目 使用方法 链接 本项目链接：Github ASF配置 添加免费游戏通过ASF实现，所有首先需要配置ASF，打开ASF的IPC功能 只要在ASF.json中，加一条"IPC": true,即可 脚本安装 & 配置 首先克隆仓库 git clone https://github.com/chr233/free_game_hunter cd free_game_hunter pip3 install -r requirements.txt mv example.config.toml config.toml 在本地会存在一个data.db，会缓存机器人拥有的游戏和steam商店上的免费游戏，然后会保存统计数据，请不要自行修改data.db 然后修改config.toml，设置机器人名称即可 [bot] # 机器人设置 # 机器人名称,不能设置为asf bot_name = '1' # 多个机器人可以设置写成这样 # bot_name = ['1','2','3'] [asf] # asf设置 # ipc地址 ipc_addr = 'http://localhost:1242' # ipc连接密码,没有密码的话留空 ipc_password = '' [db] # 数据库配置 # 数据库名称 db_name = 'data.db' 运行脚本 使用Python3运行 python3 run.py 自动运行 我使用crontab实现定时执行（因为Steam限制，一个账号每小时最多添加50个Sub，所以一小时执行一次就行了） # 每个小时执行一次 0 * * * * /home/xxx/free_game_hunter/start.sh > /dev/null 2>&1 & start.sh内容： # 需要先切换到工作目录才可以，路径自行修改 cd /home/xxx/free_game_hunter python3 run.py 运行效果