分析
可以看到符号并没有抹除.
我们进入 AES_ECB_encrypt 看看
可以看到 sub_20B14 被调用了三次, 这显然是 轮密钥加 的操作.
第一轮 与 key0 做异或
第2到10轮 与 key1-9 做异或
第11轮 与 key10 做异或
进入到 sub_20BAC, 看看实现
再看看 byte_16CD5
这明显是一个查表的操作, 而且 0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F 明显是AES 的 S盒, 专用于字节替换.
再来看看 sub_20C3C 的实现.
这看起来似乎是循环左移的操作.
那 sub_20D2C 大概率是 列混淆的操作了.
a1 大概率是 state.
我们试试在倒数第二轮修改state 的某个值.
可以看到 i== 10 就退出了, 我们期望 i==9 时候, 修改 state 的值.
这里介绍一个ida插件, 可以生成 frida 代码
1 | function hookwbShiftRows(){ |
函数名没来得及改, hook循环左移, 但不影响, 我们实际hook的是列混淆函数
一共是九次列混淆, 最后一轮计算中并没有列混淆的操作. 所有当 i==9 时, 注入一个字节. 在哪里注入? 在arg[0] + offset 的位置注入无符号字节, 0-255之间
我们怎么来主动 call 呢?
答曰 : 从java 层或者 native层主动call
我这里从native层没有找到好的时机, 因为 aes的第一个入参是 &ctx, 这是个结构体. 所以我尝试从 java层多次call
1 | Java.perform(function () { |
下面给出完整的 dfa 注入错误的代码
1 |
|
然后执行 frida 注入
将结果复制到文件 aes_dfa_result.txt 中, 第一行添加正确的加密的结果.
1 | import phoenixAES |
跑一下 看看结果
可以看到 最后一轮也就是第11轮的密钥已经出来了, 197DD69D902A88D60447D21405FC374C
已知AES ECB 的某一轮密钥, 可以推导出其他任意一轮密钥,(共11轮密钥), 包括初始密钥, 也可以推导出.
我们使用 Stark 工具来推导出任意一轮密钥
1 | git clone https://github.com/SideChannelMarvels/Stark.git |
1 | 从0开始计数. 10代表第11轮 |
看看结果
cyberchef 看看
可以看到 AES ECB 的密钥已经出来了. 算法并没有魔改
而且这个样本使用的就是标准的 tiny-AES-c 的算法
感兴趣可以自己研究研究
1 | git clone https://github.com/kokke/tiny-AES-c.git |