资讯动态

安卓手势密码暴力枚举:九宫格路径生成与SHA-1哈希碰撞

发布时间:2026/10/26 14:03:58 来源:尧图企业网站定制
简介这份资源面向对Android安全机制与Python脚本实践感兴趣的学习者聚焦于通过Python 2.x还原Android锁屏手势密码这一具体问题。其原理在于系统将手势轨迹转换为十六进制数字串并做SHA1加密结果保存在/data/system/gesture.key中资源围绕该文件的提取与解密展开适合具备一定Python基础、希望理解哈希校验与暴力枚举思路的读者参考。压缩包共12个文件约61KB以6个xml配置文件、1个key样本、1个py主脚本及md说明文档为主辅以图片与工程元数据结构紧凑便于直接运行与阅读。已有1223人学习下载。读者可从中获得可执行的解密脚本、真实gesture.key样本、加密与还原流程的完整演示以及脚本运行耗时等实测数据为理解Android手势密码存储机制和Python枚举实现提供直观参考。1. 手势密码的暴力枚举从哈希到九宫格的那条最短路径安卓锁屏手势密码的存储方式决定了它能不能被离线还原。很多人以为锁屏是一道不可绕过的门实际上在获得设备存储镜像或 root 权限后/data/system/gesture.key里躺着的只是一段 SHA-1 哈希九宫格上任意一条合法路径都能被枚举出来做比对。这个项目就是干这件事的用 Python 把 1 到 9 个点的所有合法手势路径生成出来逐条计算哈希与目标值碰撞。它解决的是取证、安全审计、以及自己忘掉手势后的数据恢复场景适合做移动端安全测试的从业者也适合想理解哈希碰撞边界的新手。核心难点不在哈希算法本身而在九宫格规则带来的路径剪枝——相邻点之间如果跨过中间点那个中间点必须已经被用过否则这条路径在系统里根本不存在。2. 九宫格规则建模为什么 389112 条路径里有一半是废的2.1 手势密码的合法性约束到底有哪些安卓手势密码的规则比大多数人想的要严。九宫格按 3×3 编号从左上角开始顺时针编号 1 到 9横竖斜三个方向都算相邻。一条路径合法的前提是每个点最多用一次长度在 4 到 9 之间且任意两个连续点之间如果存在一个「跨过点」那个跨过点必须已经被访问过。比如从 1 直接到 3中间跨过 2如果 2 还没被用过这条路径在系统里是无效的用户根本画不出来。这个约束是暴力枚举能不能收敛的关键。如果不做剪枝9 个点的全排列是 9! 362880 条加上长度 4 到 8 的排列总数会膨胀到几十万条而且其中大量路径是系统不接受的。做了跨点校验之后合法路径总数收敛到 389112 条这个数字是公开可查的也是这个项目枚举逻辑正确性的一个自检基准。跨点关系可以预先固化成一张表常见做法是用一个字典把「起点-终点」映射到「必须已访问的中间点」# 跨点关系表key 为 (起点, 终点)value 为必须已访问的中间点 # 只列单向即可查询时双向都查 CROSS_POINTS { (1, 3): 2, (1, 7): 4, (1, 9): 5, (2, 8): 5, (3, 7): 5, (3, 9): 6, (4, 6): 5, (7, 9): 8, } def get_cross_point(a, b): 返回 a 到 b 之间必须已访问的中间点没有则返回 None if (a, b) in CROSS_POINTS: return CROSS_POINTS[(a, b)] if (b, a) in CROSS_POINTS: return CROSS_POINTS[(b, a)] return None这段代码的逻辑很直白九宫格里只有 8 组点对存在跨点其余相邻关系都是直接可达的。get_cross_point做了双向查询因为从 3 到 1 和从 1 到 3 跨过的都是 2。参数上没什么可调的这张表是几何关系决定的写死即可。真正要小心的是编号约定——不同资料对九宫格的编号顺序可能不同有的按行优先有的按电话键盘布局一旦编号和哈希来源对不上枚举出来的路径全是错的这是后面避坑章节要展开的点。2.2 用 DFS 生成全部合法路径有了跨点表路径生成就是一个带状态的回溯搜索。状态里要维护三样东西当前路径、已访问点集合、当前长度。每一步尝试所有未访问的点检查跨点约束通过就递归下去长度达到 4 就把当前路径收进结果集。def generate_patterns(min_len4, max_len9): 生成所有合法手势路径返回路径列表每条为点编号的元组 results [] visited [False] * 10 # 下标 1-9 有效0 占位 def dfs(path): if len(path) min_len: results.append(tuple(path)) if len(path) max_len: return for nxt in range(1, 10): if visited[nxt]: continue cross get_cross_point(path[-1], nxt) # 跨点存在且未被访问这条边不合法 if cross is not None and not visited[cross]: continue visited[nxt] True path.append(nxt) dfs(path) path.pop() visited[nxt] False for start in range(1, 10): visited[start] True dfs([start]) visited[start] False return resultsmin_len和max_len默认取 4 和 9对应安卓系统允许的最短和最长手势。如果目标设备的系统版本对最短长度有特殊限制改这两个参数即可但要注意改小之后结果集会变大哈希比对的时间会线性上升。visited数组用 1 到 9 下标0 位空着这样点编号和数组下标直接对应省掉一次减一运算在几十万次递归里这点开销不算大但读起来更顺。跑一遍这个函数len(results)应该等于 389112。这个数字对不上说明跨点表或者编号约定有问题先别往下走。我一般会拿长度 4 的子集单独数一遍合法路径是 1624 条这个数小好核对出问题容易定位。2.3 哈希比对SHA-1 的输入到底是什么gesture.key里存的是手势路径的 SHA-1 十六进制摘要。关键细节在于输入格式系统把路径上的点编号拼成一个字节序列再做哈希不是拼字符串。也就是说路径(1, 2, 3, 6)对应的输入是字节b\x01\x02\x03\x06而不是b1236或b1,2,3,6。这个区别是新手最容易翻车的地方拼错了哈希永远对不上还会误以为是枚举逻辑错了。import hashlib def pattern_to_hash(path): 把路径元组转成 SHA-1 十六进制摘要 raw bytes(path) # 点编号直接作为字节注意不是 ASCII 字符 return hashlib.sha1(raw).hexdigest() def crack(target_hash, patterns): 在候选路径中查找匹配目标哈希的那一条 target target_hash.strip().lower() for path in patterns: if pattern_to_hash(path) target: return path return Nonebytes(path)这一步是核心Python 里对一个整数元组调用bytes()会按字节值展开正好对应系统的存储格式。如果路径里有 9字节就是0x09没问题。crack函数做了大小写归一因为从文件里读出来的哈希可能是大写直接字符串比较会漏。实际跑的时候389112 条路径逐条算 SHA-1单核大概几秒到十几秒取决于机器这个量级不需要上多进程但如果你要批量处理多个镜像把patterns生成一次缓存起来复用能省掉重复的枚举开销。3. 从镜像到明文把 gesture.key 拿到手的几种路径3.1 取证场景下的数据获取方式枚举逻辑再对拿不到gesture.key也是白搭。这个文件在/data/system/目录下普通应用无权读取常见做法有三类。第一类是设备已 root直接用 adb 拉取第二类是有完整的文件系统镜像比如通过恢复模式做的全盘备份挂载后从镜像里提取第三类是在授权前提下通过调试接口读取。三条路的前提条件不同能拿到的数据完整性也不同。adb 路径最直接前提是设备已 root 且 adb 有 root 权限adb root adb pull /data/system/gesture.key ./gesture.keyadb root在部分设备上会失败返回adbd cannot run as root in production builds这是编译类型决定的不是命令写错了。遇到这种情况只能走镜像提取。镜像提取的常见做法是用恢复模式做 userdata 分区备份拿到镜像后用工具挂载成可读文件系统再定位到system/gesture.key。这一步的工具链因设备而异不在这个项目的代码范围内但提取出来的文件格式是一致的——一段 40 字符的十六进制字符串可能带换行。拿到文件后先看一眼内容确认格式cat gesture.key # 输出类似c8f3a2...40 个十六进制字符 wc -c gesture.key # 应该是 40 或 41带换行如果长度不对比如只有 32 字符那可能是别的锁类型PIN 或密码的哈希不是手势别拿手势枚举去碰白费时间。3.2 把枚举和比对串成一条命令拿到哈希之后把前面的枚举和比对接起来就是完整流程。我一般写成一个脚本参数化输入方便复现import sys import hashlib def main(key_file): with open(key_file, r) as f: target f.read().strip().lower() if len(target) ! 40: print(f哈希长度异常: {len(target)}预期 40) return patterns generate_patterns() print(f候选路径数: {len(patterns)}) hit crack(target, patterns) if hit: print(f命中: {hit}) else: print(未命中检查编号约定或哈希格式) if __name__ __main__: main(sys.argv[1])sys.argv[1]传gesture.key的路径。脚本先做长度校验40 字符是 SHA-1 十六进制的固定长度不对就早退避免跑完几十万次才发现输入错了。generate_patterns()每次调用都重新枚举单次场景无所谓批量场景下应该把它挪到循环外面或者落盘缓存。命中输出的是点编号元组比如(1, 2, 3, 6)照着在九宫格上画一遍就能验证。提示这套流程只适用于你自己拥有或获得明确授权的设备。对他人设备做未授权提取和破解性质就变了不在技术讨论范围内。3.3 编号约定不一致时怎么对齐不同来源的资料对九宫格编号的定义可能不同这是复现时最隐蔽的坑。有的按行优先从左到右编号有的按电话键盘布局1 在左下还有的按顺时针螺旋。哈希是对字节序列算的编号一错字节序列就错枚举再多也碰不上。对齐的办法是拿一个已知手势做校准。如果你能在设备上设置一个已知的手势比如 1-2-3-6提取哈希然后用不同编号约定各跑一遍能命中的那个约定就是对的。没有已知手势时只能靠系统版本推断但这个不可靠。我的习惯是先在代码里把编号约定写成一个可切换的配置跑不通就换一种比改一堆硬编码快。# 编号约定切换row_major 为行优先phone 为电话键盘布局 LAYOUT row_major def remap(path): if LAYOUT row_major: return path # 电话键盘1-3 在底行7-9 在顶行做上下翻转 flip {1: 7, 2: 8, 3: 9, 4: 4, 5: 5, 6: 6, 7: 1, 8: 2, 9: 3} return tuple(flip[p] for p in path)remap在比对前对路径做一次映射LAYOUT改一下就能切换。这个设计不优雅但实用校准阶段够用。确认约定之后把映射固化别留在生产脚本里增加复杂度。4. 性能与边界389112 条路径跑得快不快什么时候会失效4.1 单核几秒的量级瓶颈在哪389112 条路径每条算一次 SHA-1单核跑下来通常在 3 到 8 秒之间具体看机器和 Python 版本。瓶颈不在哈希SHA-1 对 9 字节以内的输入极快瓶颈在 Python 层的循环和函数调用开销。pattern_to_hash每条路径调用一次hashlib.sha1的对象创建占了不小比例。想再快一点常见做法是把路径预先生成成字节串列表比对阶段只做哈希不再走元组转换def precompute_bytes(patterns): 把路径元组预转成字节串比对阶段省掉转换 return [bytes(p) for p in patterns] def crack_fast(target_hash, raw_list): target target_hash.strip().lower() for raw in raw_list: if hashlib.sha1(raw).hexdigest() target: return raw return Noneprecompute_bytes把bytes(path)的开销从比对循环里挪出来只做一次。crack_fast返回的是字节串需要的话再转回元组展示。这个优化在单次破解里收益有限但在批量处理几十个镜像时能省下可观的重复枚举时间。再往上就是多进程分片把路径列表切成 N 份并行比对但要注意进程间传大列表本身有开销路径数不到百万级收益不一定为正我一般不上。4.2 什么情况下枚举会失效枚举失效的场景比想象中多。第一种是系统版本较新部分定制系统对gesture.key的存储做了额外处理比如加盐或者换了哈希算法这时候 SHA-1 直接比对必然落空。第二种是设备启用了更严格的锁屏策略手势之外还有别的验证因子单靠手势哈希还原不了完整解锁链路。第三种是文件本身损坏或被截断读出来的哈希长度不对前面已经提过。还有一种容易被忽略的情况用户设置的手势长度小于 4。安卓系统本身不允许但某些定制 ROM 可能放宽了限制如果目标设备是这类系统min_len要相应调小否则枚举集里根本不包含正确答案。反过来如果确认目标只用了 4 位手势把max_len设成 4 能把候选集从 389112 压到 1624速度快两个数量级。这个参数该不该调取决于你对目标设备的了解程度不确定就保持默认。注意枚举集大小和命中率是一对矛盾。缩小范围能提速但范围设错就直接漏解。没有把握时宁可跑全量几秒钟的事不值得为省时间赌一把。5. 避坑清单五条血泪经验5.1 哈希对不上先查字节格式再查枚举现象脚本跑完 389112 条输出「未命中」但哈希长度确认是 40 字符。原因八成是哈希输入格式搞错了把点编号当成了 ASCII 字符拼接比如b1236而不是b\x01\x02\x03\x06。解决打印一条已知路径的bytes(path)看字节值确认是0x01到0x09而不是0x31到0x39。这个错误极其常见因为字符串拼接的写法更符合直觉。5.2 跨点表漏项导致路径数不对现象len(results)跑出来不是 389112偏多或偏少。原因通常是跨点表漏了某一组比如忘了(3, 7)跨 5或者把(1, 9)跨 5 写成了跨别的点。解决拿长度 4 的子集单独核对合法数应该是 1624对不上就逐条检查跨点表。另一个可能是编号约定和跨点表不匹配比如表是按行优先写的枚举却按电话布局跑几何关系全乱。5.3 adb 拉取权限失败被误判为文件不存在现象adb pull报Permission denied或No such file or directory。原因不一定是文件不存在更可能是 adb 没有 root 权限读不了/data/system/。解决先adb root确认返回再adb shell ls -l /data/system/gesture.key看文件是否真的在。如果adb root失败换镜像提取路径别在 adb 上死磕。5.4 把 PIN 或密码哈希当成手势哈希现象文件长度不是 40或者长度对但枚举不中。原因可能是拿错了文件PIN 和密码的存储位置和格式与手势不同哈希算法和输入格式都不一样。解决确认文件路径是gesture.key而不是password.key或pin.key长度必须是 40 字符的十六进制。拿错文件跑手势枚举纯属浪费时间。5.5 批量处理时重复枚举拖慢整体现象处理多个镜像时每个都重新跑一遍generate_patterns()总时间线性叠加。原因是没有把枚举结果缓存复用。解决枚举一次把路径列表或字节串列表存到内存或落盘后续镜像直接加载。389112 条路径的字节串列表内存占用不大几十 MB 量级缓存完全可行。6. 进阶把枚举结果落盘复用以及一个校准小技巧批量场景下枚举只该做一次。把precompute_bytes的结果用 pickle 落盘下次直接加载省掉每次几秒的枚举开销import pickle def save_patterns(path, raw_list): with open(path, wb) as f: pickle.dump(raw_list, f) def load_patterns(path): with open(path, rb) as f: return pickle.load(f) # 首次生成并落盘 raw_list precompute_bytes(generate_patterns()) save_patterns(patterns.pkl, raw_list) # 后续直接加载 raw_list load_patterns(patterns.pkl)pickle的兼容性要注意Python 版本差异大时反序列化可能出问题跨机器复用时最好在目标环境重新生成一次。文件大小在几十 MB传输不是负担。这个缓存在批量取证里收益明显单次场景没必要。校准技巧前面提过这里补一个更省事的版本如果你手头有多个已知手势的哈希比如从测试设备上自己设置的可以写一个小脚本对每种编号约定各跑一遍统计命中数命中数最高的约定就是对的。这比逐个试快也比靠系统版本猜可靠。def calibrate(known_hashes, patterns): known_hashes 为已知手势的哈希列表返回各约定下的命中数 score {} for layout in (row_major, phone): LAYOUT layout hits sum(1 for h in known_hashes if crack(h, patterns)) score[layout] hits return scoreknown_hashes至少准备两三条一条容易碰巧命中多条才能区分约定。calibrate返回的字典里命中数明显高的那个就是正确约定。这个脚本我一般只在第一次接触新设备型号时跑确认之后把约定写死后面不再校准。从那以后我每次拿到新的gesture.key都强制先跑一遍长度校验和编号约定校准再进枚举宁可多花两分钟也不愿在几十万条路径跑完之后才发现输入格式错了。希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑