逆向中的Hash类算法

简介

Hash 类算法是一种摘要算法,摘要结果是不可逆的。所以一般在逆向中我们通常碰到 Hash 算法要通过它给出的一些信息来进行碰撞爆破。

下面我们首先了解一下常见的 Hash 算法。

算法特征

MD5

MD5(Message-Digest Algorithm 5)是信息学中使用广泛的哈希算法

这个算法具有很多性质:

  1. 压缩性:对于任意长度的输入,输出长度总是相同的
  2. 抗修改性:对原数据的一点点修改都会导致最终结果的最大变化。
  3. 抗碰撞性:已知原数据和 MD5 值很难生成与原数据不同但 MD5 值相同的数据。

可以理解为:生成任意一段数据的 “数字指纹”,对文件或数据的微小改动都会之间导致数字指纹的巨大变化。

Hash 算法常见一般有两种形式的调用:

  • 封装成函数

uint8_t digest[16]; uint8_t input[]="xxxxxx"; ​ MD5_hash(input,sizeof(input)-1,digest);

  • 分三步完成哈希

MD5_CTX ctx; uint8_t digest[16]; uint8_t input[]="xxxxxx"; ​ MD5_Init(&ctx); MD5_Update(&ctx,input,sizeof(input)-1); MD5_Final(&ctx,digest);

openssl源码的函数实现中,可以在靠近开头的位置看到四个固定常量。

A = 0x67452301 B = 0xEFCDAB89 C = 0x98BADCFE D = 0X10325476

识别特征:

  • 四个固定的常量(初始化向量)
  • 不论输入是多长,输出永远是16字节
  • 符合上述两种调用方式之一

SHA1

SHA1 算法与 MD5 类似,只不过 SHA1 有五个常量:

H0 = 0x67452301 H1 = 0xEFCDAB89 H2 = 0X98BADCFE H3 = 0X10325476 H4 = 0XC3D2E1F0

识别特征:

  • 五个固定的常量(初始化向量)
  • 不论输入是多长,输出永远是 20 字节
  • 符合上述两种调用方式之一

SHA256

SHA256 则有八个常量:

H0 = 0x67452301 H1 = 0xEFCDAB89 H2 = 0x98BADCFE H3 = 0x10325476 H4 = 0xC3D2E1F0 H5 = 0x9b05688c H6 = 0x1f83d9ab H7 = 0x5be0cd19

识别特征:

  • 八个固定的常量(初始化向量)
  • 不论输入是多长,输出永远是32字节
  • 符合上述两种调用方式之一

逆向方式

算法分析

通过 ida 的 findcrypt 插件我们可以直接查找到程序中的 Hash 常量来判断加密算法,但是这通常只对自己实现或静态链接openssl库才有作用。如果使用openssl链接库编译动态链接,那么 findcrypt 是找不到的。

也不能说找不到,当动态调试程序的时候使用 findcrypt 插件进行查找可以找到整个openssl链接库的常量。这样我们根本无法分析程序是哪个加密算法。

在使用动态链接的情况下,通常可以通过 ida 函数窗口的函数分析出加密算法。

看到下例中的md5字样,我们可以判断程序为md5加密算法。

网站查询

通过在线网站数据库查询并 “解密” Hash值。

md5:md5在线解密破解,md5解密加密

脚本爆破

通过编写脚本进行碰撞爆破。

通用脚本:

import string import hashlib import itertools ​ #设置目标哈希值 target = bytes.fromhex("")   ​ #设置字符集(可以根据需要修改,以下为大小写字母和数字) charset = (string.ascii_letters + string.digits).encode() ​ #根据选择的哈希算法创建哈希对象 hash_type = 'sha256' ​ if hash_type == 'md5':    hash_func = hashlib.md5 elif hash_type == 'sha1':    hash_func = hashlib.sha1 elif hash_type == 'sha256':    hash_func = hashlib.sha256 else:    print("Unsupported hash type")    exit() ​ for i in itertools.product(charset, repeat=6): #这里的6是字符的长度,可以修改为任何你想要的长度    guess = bytes(i)    hashed = hash_func(guess).digest()    if hashed == target:        print(f"Found match: {guess.decode()}")        break

hashcat

使用 hashcat 这个工具进行碰撞爆破。

常用参数:

  • -m 指定哈希类型
  • -a 指定破解模式
  • -V 查看版本信息
  • -o 将输出结果储存到指定文件
  • --force 忽略警告
  • --show 仅显示破解的 hash 密码和对应的明文
  • --remove 从源文件中删除破解成功的 hash
  • --username 忽略 hash表 中的用户名
  • -b 测试计算机破解速度和相关硬件信息
  • -O 限制密码长度
  • -T 设置线程数
  • -r 使用规则文件
  • -1 自定义字符集 -1 0123asd ?1={0123asd}
  • -2 自定义字符集 -2 0123asd ?2={0123asd}
  • -3 自定义字符集 -3 0123asd ?3={0123asd}
  • -i 启用增量破解模式
  • --increment-min 设置密码最小长度
  • --increment-max 设置密码最大长度

破解模式介绍:

  • 0 straight 字典破解
  • 1 combination 将字典中密码进行组合(1 2>11 22 12 21)
  • 3 brute-force 使用指定掩码破解
  • 6 Hybrid Wordlist + Mask 字典+掩码破解
  • 7 Hybrid Mask + Wordlist 掩码+字典破解

集成的字符集:

  • ?l 代表小写字母
  • ?u 代表大写字母
  • ?d 代表数字
  • ?s 代表特殊字符
  • ?a 代表大小写字母、数字以及特殊字符
  • ?b 0x00-0xff

例题

[SUCTF 2018 招新赛]hash

  • 分析

ida 反编译分析。看到了主函数中的 flag 前缀,下面使用 findcrypt 分析算法。

使用 findcrypt 后我们搜索到了程序中的 MD5 常量,可以判断程序使用的是 MD5 算法。

结合上面信息分析程序代码逻辑。

程序要求输入字符的前 6 个字符为SUCTF{,最后一个字符为}sub_4011A0函数使用了 MD5 的常量,应该是加密函数。最后将结果存到v18,因为下面的代码疑似以两位十六进制打印了v18变量,这可能是存在函数返回值的变量。

下面有一个用于判断的 MD5 值,与其判断的Buffer在与其判断之前。将符合i&1==0条件的字符跟 1 进行了异或,i&1即结果是偶数位的字符,所以我们爆破它之前需要跟 1 再次进行异或。

写出逆向脚本:

md5="bf772f6ed89838b9gb9f7abf3cc09413" md5=list(md5) for i in range(len(md5)): if i&1 == 0: md5[i]=chr(ord(md5[i])^1).join(md5) print(result) #cf673f7ee88828c9fb8f6acf2cb08403

然后我们可以先在 md5 查询网站查询。

查询结果为:birthday

接下来我们继续逆向剩下的代码程序。

将加密后的buffer与 md5 密文进行比较,如果相等则执行第二个if语句部分的代码。

下面我们跟进分析sub_401A90函数。

分析函数代码,首先将取输入的长度。然后以输入长度为界限进行迭代处理。将每个字符根据下标进行i*2+1偏移。i就是字符串的下标。

根据代码逻辑,对比较的字符串进行逆向处理。

enc="`ut9t;" for i in range(len(enc)): n=i*2+1 result+=chr(ord(enc[i])-n) print(result) #_ro2k0

将第一段 flag 和 第二段 flag 拼接起来得:SUCTF{birthday_ro2k0}

2024极客大挑战 blasting_master

  • 分析

分析主函数代码,发现读取了输入并去除换行符。然后由byte_42DE作为标志决定是否执行sub_1464函数,函数参数为我们输入的内容。然后再对指定大小内存进行比较检查对错。

根据以上逻辑我们可以判断程序默认是执行sub_1464函数的,这个函数极可能就是加密函数。而unk_2020以及unk_4060可能就是存储密文的变量以及存储加密结果的变量。dword_4040很明显就是比较长度了。

我们先根据代码逻辑进行函数及变量重命名方便理解。

重命名后:

接下来我们首先使用 findcrypt 进行算法特征查找,但是最后 findcrypt 并没有找到算法特征。

但是我们在函数窗口发现了算法特征,可以看到动态链接函数中的 MD5 字样。我们可以由此判断encrypt加密算法是 MD5 算法。

进入encrypt函数分析,可以看到代码逻辑非常乱。

我们先根据代码逻辑修改变量和函数类型。

修改后的:

这里v3变量获取了我们的输入,之后将v3作为参数传入了sub_13E2参数,并且将dec数组作为第二个参数传入函数。

这里根据前面的逻辑判断,sub_13E2大概就是封装的 MD5 加密函数。dec是存放结果的数组。

下面的逻辑就是对加密结果进行混淆的逻辑了。

跟进sub_13E2函数查看,验证我们的判断是正确的。

我们接下来分析函数中对加密结果进行混淆的逻辑。

以上代码循环执行了十六次,即混淆了十六次。而混淆的数据以及存储的位置每次又都是不同的。我们可以判断这里我们的 md5 加密同样进行了十六次,而我们的 flag 前缀确定的是SYS{,所以我们可以将密文数据分为 16 组进行爆破。

  • exp

import hashlib ​ cipher = [0xB2, 0x50, 0xA0, 0xBC, 0x3A, 0x7F, 0x54, 0x6D, 0x96, 0x07, 0x0F, 0x71, 0x9A, 0x72, 0xEB, 0xA5, 0xA0, 0xB5, 0x71, 0xA4, 0x6A, 0xB8, 0xBA, 0xFA, 0xE4, 0x31, 0xC3, 0x71, 0x54, 0x29, 0xA7, 0x59, 0x20, 0x2B, 0x13, 0x21, 0xBD, 0x67, 0x5F, 0x8D, 0x65, 0x3A, 0x02, 0x27, 0x08, 0x4F, 0x92, 0x9C, 0xB5, 0x7C, 0xDF, 0x69, 0x34, 0xB8, 0x82, 0x2D, 0xF6, 0xCA, 0x7A, 0x65, 0x98, 0x63, 0xDC, 0x51, 0x2A, 0x34, 0x97, 0x4F, 0xF8, 0xBC, 0x23, 0x1F, 0x38, 0xA8, 0xA6, 0x2F, 0xA9, 0x0D, 0x64, 0x4C, 0xAC, 0x2F, 0xF9, 0xF5, 0x2D, 0xB1, 0x91, 0xA8, 0xD5, 0x76, 0xD9, 0x2D, 0xC6, 0xAC, 0x2E, 0x69, 0x32, 0xD5, 0x64, 0x1D, 0xC1, 0x3C, 0xEC, 0xF5, 0x2C, 0x90, 0xED, 0xF4, 0x17, 0x8B, 0x55, 0x4C, 0xE4, 0x6C, 0x3B, 0xB3, 0xDA, 0x29, 0xC0, 0x7B, 0x39, 0xDF, 0x92, 0x73, 0xFC, 0xC9, 0xC2, 0xA8, 0x68, 0x11, 0x22, 0x2B, 0x64, 0x3F, 0x12, 0x9B, 0x95, 0x73, 0x2A, 0x05, 0xD3, 0x3F, 0x2E, 0x33, 0xF1, 0x85, 0xED, 0x07, 0x7B, 0x86, 0x8F, 0x62, 0x2D, 0x79, 0x03, 0xAC, 0x80, 0xCE, 0xF5, 0xB2, 0xA0, 0x0C, 0xF7, 0xE1, 0xC5, 0x0E, 0x63, 0x27, 0xD1, 0x65, 0x23, 0xEA, 0x5A, 0x1C, 0x02, 0x0B, 0x32, 0xBA, 0x1F, 0xE5, 0xC7, 0x22, 0xA5, 0x66, 0x77, 0xEA, 0x5B, 0xE4, 0x64, 0xAB, 0x8B, 0x60, 0xB6, 0xDF, 0x00, 0xDC, 0xF7, 0x6D, 0x93, 0xEC, 0x2F, 0x2F, 0x68, 0x07, 0x50, 0xE0, 0xD1, 0x1A, 0x3F, 0xC6, 0x4E, 0x2E, 0xC6, 0xBB, 0xAE, 0x08, 0x40, 0xD8, 0x5B, 0x11, 0xB5, 0xDC, 0x15, 0x35, 0x7F, 0x63, 0x49, 0x3E, 0x5B, 0x9C, 0x0D, 0xFC, 0x0D, 0xB6, 0x80, 0xB7, 0x2B, 0x00, 0xEF, 0x3C, 0x0C, 0x2F, 0xEB, 0x86, 0x44, 0x57, 0x74, 0x9E, 0x5F, 0x1F, 0x8B, 0xA1, 0xC9, 0x01, 0xF1, 0xD8, 0xF4, 0x92, 0x82, 0x95, 0x6F, 0x85, 0xD2, 0x15, 0x22, 0x1F, 0xF0, 0x9F, 0xD1, 0xAB, 0x51, 0x39, 0x9A, 0xB6, 0xC4, 0xDA, 0xFB, 0x38, 0x8D, 0xE6, 0x8C, 0x57, 0x19, 0x5E, 0x94, 0xDA, 0x57, 0xCC, 0xF0, 0xB9, 0x0A, 0x4A, 0x17, 0x82, 0xFC, 0xC5, 0x4F, 0x4B, 0x5A, 0xA5, 0xF4, 0xE5, 0x3E, 0xFA, 0x3A, 0x0A, 0xF4, 0xB4, 0x8E, 0x7F, 0x25, 0x84, 0x75, 0x90, 0xCD, 0x35, 0x87, 0xEB, 0xC3, 0xCE, 0x81, 0x2B, 0x86, 0xC9, 0x16, 0x7E, 0x85, 0x68, 0x2D, 0xF1, 0xDB, 0x8E, 0x74, 0x15, 0xCF, 0x95, 0x51, 0x07, 0x88, 0x5E, 0x1B, 0xE9, 0x37, 0xC9, 0x5B, 0xBA, 0x61, 0xEB, 0x9F, 0x7B, 0xE4, 0x89, 0x10, 0xF0, 0x6E, 0xCD, 0x75, 0x71, 0xAD, 0x09, 0x74, 0x58, 0x49, 0xA3, 0xF5, 0x33, 0x83, 0x75, 0x22, 0x95, 0x1B, 0xE3, 0x3C, 0x48, 0x05, 0x5C, 0xAD, 0xA8, 0x6B, 0xFD, 0x41, 0xEB, 0xAF, 0xC6, 0x02, 0x28, 0xC6, 0x5E, 0xCF, 0x36, 0xAE, 0x50, 0xCE, 0x93, 0xF2, 0x70, 0x88, 0x9D, 0x3F, 0x4A, 0x9F, 0x86, 0xE7, 0x67, 0x64, 0xB0, 0x02, 0x96, 0x0C, 0xAB, 0x9F, 0xEB, 0x4B, 0x03, 0x44, 0x92, 0xDE, 0x6C, 0xF4, 0xCE, 0x32, 0x4F, 0x4F, 0x38, 0xE2, 0x52, 0x59, 0xCA, 0x95, 0x4A, 0x11, 0xD8, 0x30, 0xA2, 0x7B, 0xD5, 0x3A, 0xE6, 0x11, 0xDA, 0x3A, 0x4A, 0x33, 0x61, 0x39, 0x65, 0x26, 0xD2, 0x78, 0xBC, 0xED, 0xBD, 0xA5, 0x8B, 0x2B, 0x87, 0x4C, 0x95, 0x47, 0x25, 0x02, 0xBA, 0x83, 0x3D, 0xDC, 0xE4, 0x6A, 0xAD, 0x67, 0xDD, 0x22, 0xB1, 0xBD, 0x2B, 0x7C, 0x53, 0x11, 0x3C, 0xD9, 0x23, 0x06, 0x3D, 0x20, 0xBA, 0x28, 0xC8, 0x2D, 0x89, 0x51, 0x57, 0x63, 0x82, 0xA0, 0xC8, 0xA8, 0xDE, 0x29, 0x61, 0xC1, 0x53, 0x51, 0xB0, 0xBC, 0x37, 0x04, 0xEE, 0xC9, 0x35, 0x8A, 0xA8, 0xA2, 0x66, 0xBA, 0x6F, 0x24, 0xB6, 0x3F, 0x62, 0x41, 0x6D, 0x10, 0x46, 0xCB, 0x06, 0x12, 0x39, 0xD9, 0x0E, 0xF9, 0xDC, 0x19, 0xA7, 0x65, 0xB8, 0xC0, 0x40, 0xBE, 0xF6, 0x99, 0x9A, 0xAF, 0x02, 0x16, 0x37, 0x4D, 0xA5, 0x75, 0x4C, 0x42, 0x4B, 0x1A, 0xF0, 0x52, 0xDA, 0x38, 0xF3, 0x6B, 0xA9, 0x1A, 0xDC, 0xFA, 0x80, 0xB0, 0x60, 0xB1, 0xFD, 0x73, 0x7B, 0x78, 0xD9, 0x62, 0x83, 0x26, 0xBF, 0x16, 0x33, 0x71, 0x79, 0x6F, 0x11, 0x2F, 0xE9, 0xA7, 0xBB, 0x46, 0x46, 0xD6, 0x8F, 0xF6, 0x21, 0x7E, 0xFC, 0x68, 0x12, 0x86, 0x6B, 0xFC, 0x51, 0xC9, 0x70, 0x7A, 0x74, 0xBC, 0x8F, 0x6E, 0x0B, 0x86, 0x42, 0x6F, 0x5C, 0xFD, 0xF7, 0x4E, 0x27, 0x71, 0xFE, 0x37, 0xE6, 0xC8, 0x62, 0x47, 0xFC, 0xD5, 0x6C, 0xBA, 0x5C, 0xD9, 0x29, 0x5A, 0x73, 0xAE, 0xC3, 0x8F, 0xF0, 0x46, 0x95, 0x32, 0x42, 0x2D, 0xD0] ​ md5_list = lambda input_str: list(hashlib.md5(input_str.encode()).digest()) ​ cip = [cipher[16 * i: 16 * (i + 1)] for i in range(40)] ​ data1 = 'S'  # 初始化 data1 为 'S' data2 = 'Y'  # 初始化 data2 为 'Y' data3 = 'C'  # 初始化 data3 为 'C' ​ print("SYC",)  # 打印 "SYC" ​ for i in range(40):    # x 从 32 到 127,表示所有字符的 ASCII 码    for x in range(32, 128):        input_str = data1 + data2 + data3 + chr(x)        md5_lis = md5_list(input_str) ​        # 检查 cip[i][j] 是否符合解密条件        if all(cip[i][j] == ((md5_lis[j] ^ (j + 42)) * 7 + (j % 15) * 82) % 256 for j in range(16)):            # 如果所有字节都匹配,打印字符 x,并更新 data1, data2 和 data3            print(chr(x),)            data1 = data2            data2 = data3            data3 = chr(x) ​            if data3 == '}':                exit(0)

Read more

Flutter 三方库 posix 的鸿蒙化适配指南 - 掌控底层系统调用、文件权限管理实战、鸿蒙级系统级工具专家

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 posix 的鸿蒙化适配指南 - 掌控底层系统调用、文件权限管理实战、鸿蒙级系统级工具专家 在鸿蒙跨平台应用开发中,当我们需要实现精密的文件权限操控(如 chmod)、获取系统级用户信息或是管理进程间的信号(Signals)时,高层的 Dart SDK 有时无法提供足够细粒度的控制。如果你需要一种接近 C 语言、直接与鸿蒙内核(Kernel)对话的能力。今天我们要深度解析的 posix——一个旨在为 Dart 提供标准可移植操作系统接口(POSIX)支持的高性能库,正是帮你接管“系统底层主权”的关键插件。 前言 posix 是一套对底层 C 库函数的轻量级封装。它通过 Dart FFI 机制,让你能像写

By Ne0inhk
【Linux篇】Socket编程UDP

【Linux篇】Socket编程UDP

📌 个人主页:孙同学_ 🔧 文章专栏:Liunx 💡 关注我,分享经验,助你少走弯路! 文章目录 * Socket编程UDP * 创建一个套接字 * 为套接字绑定一个端口号 * UDP套接字的初始化部分 * 收消息 * 发消息 * UDP通信流程拆分 * 简单演示: Socket编程UDP UDP是一种无连接、面向数据报、不可靠的传输层协议。具有不建立连接,不保证到达,不保证顺序,不重传,不拥塞控制,速度快,开销小 的特点。 我们想要网络通信,想要UDP的编写,我们想要以网络收发的话首先得把网络文件打开. 创建一个套接字 #include<sys/types.h>/* See NOTES */#include<sys/socket.h>intsocket(int domain,int

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 mutex 为鸿蒙异步任务提供可靠的临界资源互斥锁(并发安全基石)

Flutter for OpenHarmony: Flutter 三方库 mutex 为鸿蒙异步任务提供可靠的临界资源互斥锁(并发安全基石)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 虽然 Dart 运行在单线程的事件循环(Event Loop)中,但在处理复杂的异步业务时,我们依然会面临“竞态条件(Race Conditions)”。例如: 1. 文件写入:两个异步任务同时尝试向同一个鸿蒙沙箱文件写入数据。 2. 状态更新:两个 API 回调几乎同时触发,试图修改同一个全局计数器。 3. 数据库操作:在进行“先查询、后更新”的操作间隙,数据被另一个异步流修改了。 mutex 软件包为 Dart 的异步环境提供了经典的“互斥锁”机制。它能确保在任何特定时刻,只有一个异步 Future 能进入被保护的代码块,是保障鸿蒙应用逻辑原子性的核心工具。 一、异步任务排队模型 mutex 强制让交织在一起的异步请求进行“排队”

By Ne0inhk
Flutter 三方库 twitter_intent 的鸿蒙化适配指南 - 实现一键唤起 X (原 Twitter) 社交意图、支持预填发帖内容与第三方授权跳转

Flutter 三方库 twitter_intent 的鸿蒙化适配指南 - 实现一键唤起 X (原 Twitter) 社交意图、支持预填发帖内容与第三方授权跳转

欢迎加入开源鸿蒙跨平台社区:[https://openharmonycrossplatform.ZEEKLOG.net, https://openharmonycrossplatform.ZEEKLOG.net] Flutter 三方库 twitter_intent 的鸿蒙化适配指南 - 实现一键唤起 X (原 Twitter) 社交意图、支持预填发帖内容与第三方授权跳转 前言 在进行 Flutter for OpenHarmony 的全球化应用开发时,支持社交媒体的快速分享和交互是提升用户活跃度的重要手段。twitter_intent 致力于通过简单的 URL Intent 模式,让应用能瞬间跳转到 X (原 Twitter) 并自动填充推文内容、用户名或搜索词。本文将具体介绍如何在鸿蒙端构建丝滑的社交分享体验。 一、原理解析 / 概念介绍 1.1 基础原理 twitter_intent 利用了移动端的

By Ne0inhk