密码学学习记录 14 / 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.zhaolianjinrong.v630;
import com.androidFrameWork.QLUtils;
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;
import java.util.Objects;
public class HoneyBeeAesTest extends AbstractJni {
private final AndroidEmulator emulator;
private final VM vm;
private final Module module;
public DvmObject<?> dvmObject;
public String apkPath = "/Volumes/T7/android/android-file/zhaolianjinrong-6.3.0.apk";
HoneyBeeAesTest() {
emulator = AndroidEmulatorBuilder.for32Bit().setProcessName("com.zl.fqbao").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("honeybee", true);
module = dm.getModule();
dm.callJNI_OnLoad(emulator);
dvmObject = vm.resolveClass("com/mucfc/honeybee/CollectorManager").newObject(null);
}
public static void main(String[] args) {
HoneyBeeAesTest honeyBeeAesTest = new HoneyBeeAesTest();
honeyBeeAesTest.destroy();
}
private void destroy() {
try {
emulator.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行成功,发现没啥输出,打开 so
分析一波
导出窗口搜索 java
关键词,根据符号名称可以清楚的看到第一个是加密函数,第二个是初始化函数,下面使用 unidbg
调用一下
得益于符号没去,可以直接看到函数的参数类型
public void callInitialize() {
dvmObject.callJniMethodObject(
emulator, "initialize(Landroid/content/Context;)V;",
vm.resolveClass("android/content/Context").newObject(null)
);
}
public void callEncryptPhoneNum() {
DvmObject<?> ret = dvmObject.callJniMethodObject(
emulator, "encryptPhoneNum(Ljava/lang/String;I)[B;",
new StringObject(vm, "bacde"), 5
);
System.out.println("result : " + QLUtils.bytesToHexString((byte[]) ret.getValue()));
}
代码跑起来,发现结果不太对,有点长,有猫腻,so
分析一波
so
进入 encryptPhoneNum
函数分析
通过 unidbg
的日志可以看出来,最后是调用 SetByteArrayRegion
获取结果的
在这之前调用了 memcpy
函数,拼接 j_mask_phone_number, j_encrypt_phone_number
这两个函数的结果,点进去看一下
j_mask_phone_number
这个函数是一些 hash
算法,貌似跟 aes
没关系,略过
j_encrypt_phone_number
这个函数就很明显了,是 aes
算法
public void consoleDebugger() {
Debugger debugger = emulator.attach(DebuggerType.CONSOLE);
debugger.addBreakPoint(module.base + 0x958C);
debugger.addBreakPoint(module.base + 0x95A6);
}
下面在使用 unidbg console debugger
动态调试看一下,先下两个断点
这两个函数的第二个参数是用来保存返回数据的,所以直接保存 r1
的地址,使用 n
单步执行后,在查看 r1
的数据
可以看到最后的结果是 hash + aes
拼接起来的,所以咱们只需要关注 j_encrypt_phone_number aes
算法即可,下面来分析下
aes
继续分析 j_encrypt_phone_number
函数
这里就是循环调用了,每 16
个字节数据为一组,这也是 aes
的特征,这里虽然能看到 key
但我们不用去管他,毕竟是练习 aes dfa
,继续跟下去
这个样本的符号很友好,完全没去除,所以可以直接看到,密钥编排函数,跟加密函数
点进加密函数,这就能看到 aes
的核心逻辑了,是个标准的 aes
,咱们就按照龙哥文章里说的逻辑,在箭头这里注入故障值
public int randInt(int min, int max) {
Random rand = new Random();
return rand.nextInt((max - min) + 1) + min;
}
public void dfaAttack(final long off) {
emulator.attach().addBreakPoint(module.base + 0xCF34 + 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(off, (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 (long anInt : ints) {
HoneyBeeAesTest honeyBeeAesTest = new HoneyBeeAesTest();
honeyBeeAesTest.dfaAttack(anInt);
honeyBeeAesTest.callInitialize();
honeyBeeAesTest.callEncryptPhoneNum();
honeyBeeAesTest.destroy();
}
}
同样的逻辑,每个字节注入两次
运行保存所有的错误密文,使用 phoenixAES
跑一下
轮密钥,跟主密钥也是成功计算出来了
最后
这里面有个坑博主没有说,大家自行踩一下