SM4-DFA攻击

admin 2025年1月21日19:13:40评论8 views字数 9998阅读33分19秒阅读模式

案例:byd hy

SM4密码算法详解:分组长度与加密流程-CSDN博客

https://github.com/py-gmssl/py-gmssl 可以看源码

https://github.com/guojuntang/sm4_dfa dfa攻击

(六)国密SM4算法 - 知乎 (zhihu.com)

分析SO

这里我没看java层,从so的java_开始查看,根据经验可得

Java_com_bangcle_comapiprotect_CheckCodeUtil_checkcode 入口
这里是sm4的算法入口,bcda123fcd4d2019 这里为什么是iv?
bangcle_QSM4_cbc_encrypt(v112, v125, v128, &v789, "bcda123fcd4d2019"16LL, v135, 131076LL, 1);// bcda123fcd4d2019=iv 

iv 探究

上面代码追踪到如下:

v18 = bangcle_CRYPTO_cbc128_encrypt(p, a3, item_count, bcda123fcd4d2019, &v23, off_142FA0);
进去之后发现就是用来异或的,iv的作用就是这个。 这里我做了patch,所以最后的iv=0000000000000000000000000000000
//这里要分为16个字符长度,每个字符跟bcda123fcd4d2019的相应位置异或。得到sm4加密之前的明文输入,1234得到的是SQWU=>?joh8h><=5
SM4-DFA攻击

sm4加密探究

进入a6之后,可以看看算法如下,怎么看a6? 这里看看x4的值。或者off_142FA0点进去看看就行

SM4-DFA攻击a6=bangcle_WB_QSM4_encrypt(__int64 a1, __int64 a2, __int64 *a3) 最终的方法

根据前序的知识,可以得出非常标准的算法。

SM4-DFA攻击

如图,注入位置,后续就开始注入。使用unidbg 代码如下:

package com;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Emulator;
import com.github.unidbg.Module;
import com.github.unidbg.arm.context.RegisterContext;
import com.github.unidbg.debugger.BreakPointCallback;
import com.github.unidbg.file.FileResult;
import com.github.unidbg.file.IOResolver;
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.linux.android.dvm.api.SystemService;
import com.github.unidbg.linux.file.SimpleFileIO;
import com.github.unidbg.memory.Memory;
import com.github.unidbg.pointer.UnidbgPointer;
import com.github.unidbg.utils.Inspector;
import com.github.unidbg.virtualmodule.android.AndroidModule;
import com.sun.jna.Pointer;
import keystone.Keystone;
import keystone.KeystoneArchitecture;
import keystone.KeystoneMode;
import unicorn.Arm64Const;

import java.io.*;
import java.util.ArrayList;
import java.util.Random;

/*
 */

public class bydhy extends AbstractJni implements IOResolver {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;

    @Override
    public FileResult resolve(Emulator emulator, String pathname, int oflags) {
        System.out.println("get path:" + pathname);
        if ("/proc/self/maps".equals(pathname) || ("/proc/" + emulator.getPid() + "/maps").equals(pathname)) {
            return FileResult.success(new SimpleFileIO(oflags, new File("unidbg-android/src/test/resources/byd/bydmpas"), pathname));
        }
        return null;
    }

    bydhy() {
// 创建模拟器实例
        emulator = AndroidEmulatorBuilder.for64Bit().setProcessName("com.byd.sea").build();
// 获取模拟器的内存操作接口
        final Memory memory = emulator.getMemory();
// 设置系统类库解析
        memory.setLibraryResolver(new AndroidResolver(23));
        emulator.getSyscallHandler().addIOResolver(this);
// 创建Android虚拟机,传入APK,Unidbg可以替我们做部分签名校验的工作
        vm = emulator.createDalvikVM(new File("unidbg-android/src/test/resources/byd/bydhy.apk"));
// 设置JNI
        vm.setJni(this);
// 打印日志
        vm.setVerbose(true);
        new AndroidModule(emulator, vm).register(memory);
        ;
// 加载目标SO
// DalvikModule dm = vm.loadLibrary("encrypt", true);
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/resources/byd/libencrypt_bydhy.so"), true);
//获取本SO模块的句柄,后续需要用它
        module = dm.getModule();
// 调用JNI OnLoad
        dm.callJNI_OnLoad(emulator);
    }

    ;

    @Override
    public DvmObject<?> callStaticObjectMethod(BaseVM vm, DvmClass dvmClass, String signature, VarArg varArg) {
        switch (signature) {
            case "android/app/ActivityThread->currentActivityThread()Landroid/app/ActivityThread;": {
                return dvmClass.newObject(null);
            }
            case "android/os/SystemProperties->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;": {
                String arg0 = varArg.getObjectArg(0).getValue().toString();
                String arg1 = varArg.getObjectArg(1).getValue().toString();
                System.out.println(arg0 + "====" + arg1);
                if (arg0.equals("ro.serialno")) {
                    return new StringObject(vm, "unknown");
                }
                return new StringObject(vm, "");
            }
        }
        return super.callStaticObjectMethod(vm, dvmClass, signature, varArg);
    }

    @Override
    public DvmObject<?> callObjectMethod(BaseVM vm, DvmObject<?> dvmObject, String signature, VarArg varArg) {
        switch (signature) {
            case "android/app/ActivityThread->getSystemContext()Landroid/app/ContextImpl;": {
                return vm.resolveClass("android/app/ContextImpl").newObject(null);
            }
            case "android/app/ContextImpl->getPackageManager()Landroid/content/pm/PackageManager;": {
                DvmClass clazz = vm.resolveClass("android/content/pm/PackageManager");
                return clazz.newObject(signature);
            }
            case "android/app/ContextImpl->getSystemService(Ljava/lang/String;)Ljava/lang/Object;": {
                StringObject serviceName = varArg.getObjectArg(0);
                assert serviceName != null;
                System.out.println(serviceName.toString());
                return new SystemService(vm, serviceName.getValue());
            }
            case "android/net/wifi/WifiManager->getConnectionInfo()Landroid/net/wifi/WifiInfo;": {
                return vm.resolveClass("android/net/wifi/WifiInfo").newObject(null);
            }
            case "android/net/wifi/WifiInfo->getMacAddress()Ljava/lang/String;": {
                return new StringObject(vm, "da:c4:13:ef:95:aa");
            }
        }
        return super.callObjectMethod(vm, dvmObject, signature, varArg);
    }

    @Override
    public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {

        switch (signature) {
            case "android/os/Build->MODEL:Ljava/lang/String;":
                return new StringObject(vm, "google");
            case "android/os/Build->MANUFACTURER:Ljava/lang/String;":
                return new StringObject(vm, "google1");
            case "android/os/Build$VERSION->SDK:Ljava/lang/String;":
                return new StringObject(vm, "20.1");
        }
        return super.getStaticObjectField(vm, dvmClass, signature);

    }


    public String checkcode() throws FileNotFoundException {
//        emulator.attach().addBreakPoint(module.base + 0x29E6C); //28轮
//        emulator.attach().addBreakPoint(module.base + 0x29E9C); //29轮
//        emulator.attach().addBreakPoint(module.base + 0x29ECC); //30轮
//        emulator.attach().addBreakPoint(module.base + 0x29efc); //31轮
//        emulator.attach().addBreakPoint(module.base + 0x2580C); //结果下断

//        先把这里nop掉,好分析 不进行nop的值=XsdchC8eiT4MgMiS0PMHtQ== nop之后HzsW71C+Z3r+/Y1tWAsffg== 这里是iv
        UnidbgPointer pointer = UnidbgPointer.pointer(emulator, module.base + 0x29384); //这里要分为16个字符长度,每个字符跟bcda123fcd4d2019的相应位置异或。得到sm4加密之前的明文输入,1234得到的是SQWU=>?joh8h><=5
        Keystone keystone = new Keystone(KeystoneArchitecture.Arm64, KeystoneMode.LittleEndian);
        String s = "nop";
        byte[] machineCode = keystone.assemble(s).getMachineCode();
        pointer.write(machineCode);
        pointer = UnidbgPointer.pointer(emulator, module.base + 0x29388);
        keystone = new Keystone(KeystoneArchitecture.Arm64, KeystoneMode.LittleEndian);
        machineCode = keystone.assemble(s).getMachineCode();
        pointer.write(machineCode);
        pointer = UnidbgPointer.pointer(emulator, module.base + 0x2938C);
        keystone = new Keystone(KeystoneArchitecture.Arm64, KeystoneMode.LittleEndian);
        machineCode = keystone.assemble(s).getMachineCode();
        pointer.write(machineCode);
//
//arg listm
        ArrayList<Object> params = new ArrayList<>(10);
//jnienv
        params.add(vm.getJNIEnv());
//jclazz
        params.add(0);
        //str1 参数//用4个字符刚好就是
        StringObject str1 = new StringObject(vm, "F1234");
        params.add(vm.addLocalObject(str1));
//int 参数2
        params.add(0);

        //str2 参数3
        StringObject str2 = new StringObject(vm, "1715591022250");
        params.add(vm.addLocalObject(str2));
        Number number = module.callFunction(emulator, 0x1DDE0, params.toArray());
        StringObject res = vm.getObject(number.intValue());
        return "";
    }

    public static void main(String[] args) throws FileNotFoundException {
        bydhy b = new bydhy();
        System.out.println(b.checkcode());
    }
}

这里F1234作为明文输入,patch iv之后的结果为1f3b16ef 50be677a fefd8d6d 580b1f7e

分别输入改x0就行,手动改,拿28轮举例,最终的结果。

SM4-DFA攻击其他轮次依旧这样,最后的结果如下。

SM4-DFA攻击phoenixSM4 之后为:

import phoenixSM4

with open('tracefile''wb') as t:
    t.write("""1f3b16ef5****d8d6d580b1f7e
c0d87ff54****fd8d6d580b1f7e
a2cd206a4f222e95fefd8d6d580b1f7e
bc2d8a7c4e****d8d6d580b1f7e 
9e43147076bf48e5b2880a15580b1f7e
5e7c8624****b0b12580b1f7e 
16682a9fa8e47260b38b0b15580b1f7e 
f9c2ca932****c29ea249ee4 
b8025b071088252c133e42ebeb279fe3
d3e683f053****36b7ea249ee3 
397d454b8f19d16f2fd06790e9b61b33
87cc59fa32****4314ddaa83f
65cc8506759bc518c1ad173c64882580
"
"".encode('utf8'))

phoenixSM4.crack_file('tracefile')
结果:
Round key 32 found:
21****
Round key 31 found:
B****4
Round key 30 found:
68****96
Round key 29 found:
EDF3A9FA
./sm4_keyschedule EDF3A9FA **** B2D12B44 **** 32   
Key: 
****9A4A5585 ****142A2B9E
K00: ****
CCE066D5 27D246F9 A65A0942
K04: 
**** B7DE1D60 A35DF2E1 A62C2EDD
K08: 
****493CE50E **** 09D66C42
K12: 
****42782EB5 3104CB06 8C7525F0
K16: 
****2C376B48 FB588F56 6A317921
K20: 
9**** D32E709F BBBABC1F FCF3E7F8
K24: 
**** E9A12B08 **** 90BFEA02
K28: 
7**** B2E1AC9 1763BA27 EF90DD2E
K32: **** 
682E0F96 B2D12B44 21E6B235

修改端序
./sm4_keyschedule FAA9F3ED 
****442BD1B2 ****32                                  
Key: 
****89ABCDEF ****89ABCDEF
K00: **** DF01FEBF 
665ED4F0 3BDBEF33
K04: **** 
41662B61 C15C6744 527431C6
K08: 
****494B8F3E **** E47F3D90
K12: 
****47CEAA78 76D78969 0CF3CC88
K16: ****
354D88D4 666F8E03 A3993068
K20: 
****0A6A9446 **** 73C658F6
K24: ****
7F58ED7B C6ED22D9 E8FB6FFF
K28: 
**** C81955CD 0B24484F 0D3FE944
K32: **** 
960F2E68 442BD1B2 35B2E621

注意点

这里最后的结果我也没找到差异,[原创]白盒SM4的DFA方案-Android安全-看雪-安全社区|安全招聘|kanxue.com 这边文章提示了我,第32轮注入的话影响4字节,我选的位置是没有进行轮秘钥处理的,所以这位置不可取,后续也只有一个反序,所以忽略。大佬也说了有轮密钥端序的问题,最后处理下就行。

SM4-DFA攻击

原文始发于微信公众号(小白技术社):SM4-DFA攻击

免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2025年1月21日19:13:40
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   SM4-DFA攻击https://cn-sec.com/archives/3656560.html
                  免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉.

发表评论

匿名网友 填写信息