文章目录[隐藏]
密码学学习记录 13 / 15
- 密码学学习记录|hash md5 算法
- 密码学学习记录|hash md5 算法练习样本一
- 密码学学习记录|hash md5 算法练习样本二
- 密码学学习记录|hash sha1 算法
- 密码学学习记录|hash sha1 算法练习样本一
- 密码学学习记录|hash sha256 算法
- 密码学学习记录|hash sha256 算法练习样本一
- 密码学学习记录|hash sha512 算法
- 密码学学习记录|hash hmac 算法
- 密码学学习记录|des 加解密算法
- 密码学学习记录|des - 3des 算法
- 密码学学习记录|aes 加解密算法
- 密码学学习记录|aes dfa 练习样本一
- 密码学学习记录|aes dfa 练习样本二
- 密码学学习记录|aes dfa 练习样本三
前言
dfa 攻击相关资料,可参考之前的文章 从黑盒攻击模型到白盒攻击模型
样本来自龙哥星球,需要获取可加星球。上述链接有二维码
unidbg
祖传操作,先把架子搭起来
package com.qinless.yirendaijiekuan.v639;
import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.memory.Memory;
import java.io.File;
import java.io.IOException;
public class AesWBTest extends AbstractJni {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
public String apkPath = "";
AesWBTest() {
emulator = AndroidEmulatorBuilder.for32Bit().build();
final Memory memory = emulator.getMemory();
memory.setLibraryResolver(new AndroidResolver(23));
vm = emulator.createDalvikVM(new File(apkPath));
vm.setJni(this);
vm.setVerbose(true);
DalvikModule dm = vm.loadLibrary("aeswb", true);
module = dm.getModule();
dm.callJNI_OnLoad(emulator);
}
public static void main(String[] args) {
AesWBTest aesWB = new AesWBTest();
aesWB.destroy();
}
private void destroy() {
try {
emulator.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行成功,啥也没输出,猜测都是静态注册的 so
函数,打开瞅一瞅
so
打开导出窗口,搜索 java
好家伙,这是标准函数,哪咱们就选择一个最简单的函数分析
public void callEncryptEcb() {
DvmObject<?> dvmObject = vm.resolveClass("com/sec/MessageUtil").newObject(null);
StringObject ret = dvmObject.callJniMethodObject(
emulator, "encryptecb(Ljava/lang/String;)Ljava/lang/String;",
new StringObject(vm, "abcdefg")
);
System.out.println("result: " + QLUtils.bytesToHexString(Base64.decodeBase64(ret.toString())));
}
再次运行,结果成功出来了,这就是 aes
的加密结果
看了前面文章的小伙伴大概都明白了,想要进行 dfa 攻击,需要通过倒数 第二轮 来注入错误的数据,实现分差攻击,所以咱们需要先找到轮数,继续分析 so
encryptecb
函数跟进来,会到这里,这里大概就是轮数了,刚好循环 10 次
public void inlineHook() {
emulator.getBackend().hook_add_new(new CodeHook() {
@Override
public void hook(Backend backend, long address, int size, Object user) {
if (address == (module.base + 0x16BE)) {
Arm32RegisterContext ctx = emulator.getContext();
System.out.println(ctx.getR1Int());
}
}
@Override
public void onAttach(UnHook unHook) {
}
@Override
public void detach() {
}
}, module.base + 0x16BE, module.base + 0x16BE, null);
}
使用 inlinehook
看一下,刚好是运行 10 次
看一下 sub_165C
函数,大概率是 aes
具体的查表逻辑,这里就不用管了,主要分析 sub_165C
函数的参数即可
每次循环都会调用这个函数,咱们直接在第 9
次的时候,修改下这个函数的参数,即可实现分差攻击
public int randInt(int min, int max) {
Random rand = new Random();
return rand.nextInt((max - min) + 1) + min;
}
public void dfaAttack(final long offset1) {
emulator.attach().addBreakPoint(module.base + 0x16B8 + 1, new BreakPointCallback() {
int count = 0;
UnidbgPointer pointer;
@Override
public boolean onHit(Emulator<?> emulator, long address) {
count += 1;
RegisterContext registerContext = emulator.getContext();
pointer = registerContext.getPointerArg(0);
if (count % 9 == 0) {
pointer.setByte(offset1, (byte) randInt(0, 0xff));
}
return true;
}
});
}
public static void main(String[] args) {
long[] ints = new long[]{
1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10,
11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 16, 16
};
for (int i = 0; i < ints.length; i++) {
AesWBTest aesWB = new AesWBTest();
aesWB.dfaAttack(ints[i]);
aesWB.callEncryptEcb();
aesWB.destroy();
}
}
这里每个字节都修改两次,运行把所有的结果都保存起来,使用 python phoenixAES
跑一下
Tips: pip install phoenixAES
运行,成功计算出 k10
密钥
在使用 stark 项目运行,成功计算出主密钥
最后使用 cyberchef
验证,加密结果相同
菜鸟
2022-04-08你的 bytesToHexString 是标准的 还是去壳获取的
会爬山的小脑虎
2022-04-08@菜鸟 自己写的哦
7777
2022-04-06bytesToHexString 是标准的还是 去壳得到的
会爬山的小脑虎
2022-04-06@7777 自己写的
66
2022-04-06你的 bytesToHexString 是标准的 还是去壳获取的
会爬山的小脑虎
2022-04-06@66 自己写的
66
2022-04-07@会爬山的小脑虎 看到了 之前代码里有
66
2022-04-07@会爬山的小脑虎 感谢