首页
社区
课程
招聘
[原创]白盒SM4的DFA方案
发表于: 2025-1-19 21:39 41714

[原创]白盒SM4的DFA方案

2025-1-19 21:39
41714

声明:本文章中所有内容仅供学习交流使用,不用于其他任何目的,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除!

介绍:随着越来越多的人都会白盒AES的DFA,现在一些厂商为了安全性开始使用白盒SM4,那就让我们来看看吧~

包名:Y29tLmJ5ZC5kZW56YWRpbGluaw==

版本号:3.1.8

定位也比较简单,相信屏幕前的彦祖都可以找到,这里直接给出Frida的脚本了

然后我们使用Unidbg去模拟执行,代码如下

arg3其实很明显就是IV了,看不出来也没关系,So里面可以看得出来,这里要用readFile的形式来读取它这个白盒的表,表太大了不能直接定义为字符串所以我采用了这种方式

然后运行,发现读取了maps,补一下,不补的话跑不起来

然后继续跑,会发现报错了,bangcle crypto tool error code : -1,NullPointerException这个空指针异常是我们没跑完肯定没有结果,然后我们又去读取的结果(getValue)所以报错了

现在来看看这个报错哪来的,因为这个样本没有什么混淆、字符串加密,所以字符串加密就可以直接搜索出来了

大概意思就是v12有问题,不为0,导致的错误,我们进去看看sub_509C这个函数

可以通过断点在几个可疑的地方下一下断点看看到底执行了没有,也可以使用traceCode,我这里就直接说了,其实是v17 = (*((*a1)->reserved0 + 168))(*a1, a6)这里出问题了,根据Unidbg这里是GetStringUTFLength,然后判断长度是不是奇数,是的话就直接-1,-1基本表示失败、有问题,这里读取到的表长度是0x40009,是奇数,这个表按理来说是偶数才对,并且我们看了一下表的长度应该是0x40008才对,不知道什么原因读错了,patch一下就好了,代码如下

然后代码就跑通了,这里要注意如果我们将明文改成几个字节跑出来的结果是错的,所以我上面没有修改明文,估计是在哪里判断了长度,无伤大雅我们继续分析,根据之前的符号已知是CBC模式的,并且IV我们也猜到了可能是其中一个参数,接下来我们就点进伪代码分析一下

最后会跟到这个函数然后就没办法跟下去了,没办法直接根据伪代码来找到白盒SM4的具体位置,造成这个原因是因为IDA的反编译出问题了,其实就在这个函数里面,但是伪代码没翻译出来,对应如下

我是怎么发现的呢一开始?我一开始直接偷懒了,搜索SM4直接定位到了bangcle_WB_QSM4_encrypt

Hook了一下发现确实是这个函数并且代码的实现也确实是查表的实现,并且找不到Key,那就是白盒SM4了

那如果符号搜索不到呢我们又要怎么定位?

我们还可以使用traceCode和traceFunctionCall来进行定位,traceCode我们都比较熟悉,但是在这个样本里面不是很好用,因为粒度太细了要trace很久,感兴趣的可以试试看,也可以只trace一会然后停下来,然后聚焦bangcle_internal_crypto这个函数内部的一个控制流走向,因为我们前面就是跟到这里跟不下去了

然后我们还可以使用Unidbg封装好的一个traceFunctionCall,粒度稍微粗糙一些但是够用了,这里就不多介绍了,因为我们的明文分组是刚好19个,根据标准的填充还会再填充一个分组,所以应该是20,但是这个traceFunctionCall还是有点问题的,有的时候trace不全,即次数会出现遗漏,这里仅提供一个思路~

最后一个0x30ec就是我们刚刚字符串定位到的函数了

接下来我们先来简单介绍一下白盒SM4的DFA差分故障攻击,详细的数学原理感兴趣的可以参考:浅析SM4中的DFA attack-安全KER - 安全资讯平台,这里就不多赘述了

工具准备:SideChannelMarvels/JeanGrey: Tools to perform differential fault analysis attacks (DFA).

SideChannelMarvels/Stark: Repository of small utilities related to key recovery

我们可以先来看一份SM4的DFA攻击代码(参考:guojuntang/sm4_dfa: differential fault analysis attacks (DFA) against SM4)然后来总结一下注入故障的位置与时机

涉及的数学原理其实是比较复杂的,这里我就直接总结一下:

注入攻击的轮次时机:29-30-31-32,分别拿到对应的轮密钥

注入攻击的故障要求:第29轮>13个字节的差分(基本16个字节的差分),第30轮129个字节的差分,第32轮5个字节的差分,这些是质量最高的差分范围

当然这些也只是理论,实战的时候你会发现其实会有些变化

简单介绍完方案以后我们来开始注入攻击吧,从最后一组分组的最后一轮开始,这里输出了前后的value确保注入故障成功

正确的结果是3764c30d86577eab5ad61cc1cc7355f7,注入故障以后是47d601cf86577eab5ad61cc1cc7355f7,观察了一下故障字节是四个字节,不符合差分要求,那就先继续往上一轮注入看看,只需要修改一下偏移,我选择的是0x3764,故障密文是74f4709ae76f27ca5ad61cc1cc7355f7,故障字节8个字节,符合要求,在这个位置继续注入一次,每轮拿到两个故障密文,新的故障密文是b3801e6bdc3a139c5ad61cc1cc7355f7,也是8个字节的故障符合要求

然后继续往第30轮注入,我选择的是0x3734,故障密文分别是2a4da31a9398bd3977fb7b8bcc7355f7、f707f9ef53227db06aea10f1cc7355f7,故障密文都是12个字节,符合要求

继续往第29轮注入,我选的是0x3704,故障密文分别是e1320d54029a01d0c67d533720cceaa4、6ff24e4cc64870f0f1ac2ba4011bf052,16个字节的故障,符合要求

然后我们可以先放到phoenixSM4看看

tracefile里面的内容如下

结果如下

还差了第29轮的轮密钥,那我们就继续往上一轮注入攻击吧,我选的是0x36D4,故障密文分别是9d2fe59c814b4115072182f8279b69a6、c12dc7f7e8527e8713d8f637b979447d,基本都是16个字节的差分,然后继续phoenixSM4看看,结果出来了

至此我们拿到轮密钥了,接下来就是通过轮密钥还原主密钥,这里使用的是SM4_Keyschedule这个前面贴出的优秀工具

密钥就是39B8EC81 9A4A5585 40AFD76E 142A2B9E,IV就是62636461313233666364346432303139,放到CyberChef里面解密会发现报错:Invalid PKCS#7 padding.

看起来像是填充出现了问题,难道是魔改了填充吗?我当时的分析思路是:

1、是否魔改了填充,然后去看了一下最后一组的明文内容,发现填充没有问题

2、密文是否做了别的操作,观察了一下代码发现没有

3、是否是密文端序的问题,转换了一下端序继续使用SM4_Keyschedule发现轮密钥出不来,看起来不是密文端序的问题了

4、是否是标准的Base64,因为我一开始是使用抓包的结果来解密的,看了一下发现是标准的

5、白盒SM4的DFA是否受IV的影响,理论上是不受影响的,并且白盒AES的DFA也是不受IV影响的,这里我将最后一组明文内容改成了chuxin,并且手动补齐了填充,这样当作ECB的模式来重新DFA发现轮密钥还是一样的,也从实践证明了确实不受IV的影响

6、是否是轮密钥端序的问题,比如我们前面的是EDF3A9FA 682E0F96 B2D12B44 21E6B235,那有没有可能是FAA9F3ED 960F2E68 442BD1B2 35B2E621?试试看

看到这个Key,稳啦,这里就是轮密钥端序的问题,这里是SM4_Keyschedule和phoenixSM4的端序不一致导致的问题,感兴趣的可以修改一下源码方便以后的使用~

function main() {
    Java.perform(function () {
        var ByteString = Java.use("com.android.okhttp.okio.ByteString");
        function toBase64(data) {
            return ByteString.of(data).base64();
        }
        function toHex(data) {
            return ByteString.of(data).hex();
        }
 
        let CryptoTool = Java.use("com.bangcle.CryptoTool");
        CryptoTool["qsm4EncryptByteArr"].implementation = function (bArr, str, bArr2) {
            console.log(`CryptoTool.qsm4EncryptByteArr is called: bArr=${toHex(bArr)}, str=${str}, bArr2=${toHex(bArr2)}`);
            let result = this["qsm4EncryptByteArr"](bArr, str, bArr2);
            console.log(`CryptoTool.qsm4EncryptByteArr result=${toBase64(result)}`);
            return result;
        };
    })
}
function main() {
    Java.perform(function () {
        var ByteString = Java.use("com.android.okhttp.okio.ByteString");
        function toBase64(data) {
            return ByteString.of(data).base64();
        }
        function toHex(data) {
            return ByteString.of(data).hex();
        }
 
        let CryptoTool = Java.use("com.bangcle.CryptoTool");
        CryptoTool["qsm4EncryptByteArr"].implementation = function (bArr, str, bArr2) {
            console.log(`CryptoTool.qsm4EncryptByteArr is called: bArr=${toHex(bArr)}, str=${str}, bArr2=${toHex(bArr2)}`);
            let result = this["qsm4EncryptByteArr"](bArr, str, bArr2);
            console.log(`CryptoTool.qsm4EncryptByteArr result=${toBase64(result)}`);
            return result;
        };
    })
}
public static byte[] hexStringToBytes(String hexString) {
if (hexString.isEmpty()) {
return null;
}
hexString = hexString.replace(" ","");
hexString = hexString.toLowerCase();
final byte[] byteArray = new byte[hexString.length() >> 1];
int index = 0;
for (int i = 0; i < hexString.length(); i++) {
if (index > hexString.length() - 1) {
return byteArray;
}
byte highDit = (byte) (Character.digit(hexString.charAt(index), 16)
                   & 0xFF);
byte lowDit = (byte) (Character.digit(hexString.charAt(index + 1),
                                              16) & 0xFF);
        byteArray[i] = (byte) (highDit << 4 | lowDit);
        index += 2;
    }
    return byteArray;
}
 
public static String bytesTohexString(byte[] bytes) {
    StringBuffer sb = new StringBuffer();
    for(int i = 0; i < bytes.length; i++) {
        String hex = Integer.toHexString(bytes[i] & 0xFF);
        if(hex.length() < 2){
            sb.append(0);
        }
        sb.append(hex);
    }
    return sb.toString();
}
 
public static String readFile(String filePath) {
    StringBuilder content = new StringBuilder();
    try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
        String line;
        while ((line = reader.readLine()) != null) {
            content.append(line).append("\n");
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return content.toString();
}
public void call(){
    byte[] arg1 = hexStringToBytes("这里就不给出完整明文了");
    String arg2 = readFile("unidbg-android/src/test/java/com/tengshi/table");
    byte[] arg3 = hexStringToBytes("62636461313233666364346432303139");
 
    DvmObject<?> qsm4EncryptByteArr = NativeApi.callStaticJniMethodObject(emulator, "qsm4EncryptByteArr", arg1, arg2, arg3);
    byte[] bytes = (byte[]) qsm4EncryptByteArr.getValue();
    System.out.println("result => " + new String(Base64.getEncoder().encode(bytes)));
    System.out.println("result => " + bytesTohexString(bytes));
}
public static byte[] hexStringToBytes(String hexString) {
if (hexString.isEmpty()) {
return null;
}
hexString = hexString.replace(" ","");
hexString = hexString.toLowerCase();
final byte[] byteArray = new byte[hexString.length() >> 1];
int index = 0;
for (int i = 0; i < hexString.length(); i++) {
if (index > hexString.length() - 1) {
return byteArray;
}
byte highDit = (byte) (Character.digit(hexString.charAt(index), 16)
                   & 0xFF);
byte lowDit = (byte) (Character.digit(hexString.charAt(index + 1),
                                              16) & 0xFF);
        byteArray[i] = (byte) (highDit << 4 | lowDit);
        index += 2;
    }
    return byteArray;
}
 
public static String bytesTohexString(byte[] bytes) {
    StringBuffer sb = new StringBuffer();
    for(int i = 0; i < bytes.length; i++) {
        String hex = Integer.toHexString(bytes[i] & 0xFF);
        if(hex.length() < 2){
            sb.append(0);
        }
        sb.append(hex);
    }
    return sb.toString();
}
 
public static String readFile(String filePath) {
    StringBuilder content = new StringBuilder();
    try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
        String line;
        while ((line = reader.readLine()) != null) {
            content.append(line).append("\n");
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return content.toString();
}
public void call(){
    byte[] arg1 = hexStringToBytes("这里就不给出完整明文了");
    String arg2 = readFile("unidbg-android/src/test/java/com/tengshi/table");
    byte[] arg3 = hexStringToBytes("62636461313233666364346432303139");
 
    DvmObject<?> qsm4EncryptByteArr = NativeApi.callStaticJniMethodObject(emulator, "qsm4EncryptByteArr", arg1, arg2, arg3);
    byte[] bytes = (byte[]) qsm4EncryptByteArr.getValue();
    System.out.println("result => " + new String(Base64.getEncoder().encode(bytes)));
    System.out.println("result => " + bytesTohexString(bytes));
}
if (pathname.equals("/proc/self/maps")){
    return FileResult.success(new SimpleFileIO(oflags, new File("unidbg-android/src/test/resources/Test/tengshi_maps") , pathname));
}
if (pathname.equals("/proc/self/maps")){
    return FileResult.success(new SimpleFileIO(oflags, new File("unidbg-android/src/test/resources/Test/tengshi_maps") , pathname));
}
[main]D/crypto_tool: bangcle crypto tool error code : -1
Exception in thread "main" java.lang.NullPointerException
    at com.tengshi.encrypt.call(encrypt.java:248)
    at com.tengshi.encrypt.main(encrypt.java:273)
[main]D/crypto_tool: bangcle crypto tool error code : -1
Exception in thread "main" java.lang.NullPointerException
    at com.tengshi.encrypt.call(encrypt.java:248)
    at com.tengshi.encrypt.main(encrypt.java:273)
jbyteArray __fastcall Java_com_bangcle_CryptoTool_qsm4EncryptByteArr(
        JNIEnv *a1,
        __int64 a2,
        void *a3,
        __int64 a4,
        __int64 a5)
{
  int v5; // w1
  JNIEnv *v10; // [xsp+38h] [xbp+38h] BYREF
  unsigned int v11; // [xsp+44h] [xbp+44h] BYREF
  int v12; // [xsp+48h] [xbp+48h]
  unsigned int v13; // [xsp+4Ch] [xbp+4Ch]
  jbyte *v14; // [xsp+50h] [xbp+50h]
  char *v15; // [xsp+58h] [xbp+58h]
  jbyteArray v16; // [xsp+60h] [xbp+60h]
 
  v10 = a1;
  v14 = 0LL;
  v15 = 0LL;
  dword_18018 = 9;
  v12 = sub_5674(a3, a4);
  if ( v12 )
  {
    v12 = -1;
  }
  else
  {
    v14 = (*v10)->GetByteArrayElements(v10, a3, 0LL);
    if ( v14 )
    {
      v13 = (*v10)->GetArrayLength(v10, a3);
      v11 = 16 * (v13 / 16 + 1);
      v15 = _cxa_finalize(v11);
      if ( v15 )
      {
        v12 = sub_509C(&v10, v14, v13, v15, &v11, a4, a5);
        if ( !v12 )
        {
          v16 = (*v10)->NewByteArray(v10, v11);
          (*v10)->SetByteArrayRegion(v10, v16, 0LL, v11, v15);
        }
      }
      else
      {
        v12 = -1;
      }
    }
    else
    {
      v12 = -1;
    }
  }
  if ( v14 )
    (*v10)->ReleaseByteArrayElements(v10, a3, v14, 2LL);
  if ( v15 )
    strchr(v15, v5);
  if ( !v12 )
    return v16;
  strtoul((&dword_0 + 3), "crypto_tool", "bangcle crypto tool error code : %d");
  return 0LL;
}
jbyteArray __fastcall Java_com_bangcle_CryptoTool_qsm4EncryptByteArr(
        JNIEnv *a1,
        __int64 a2,
        void *a3,
        __int64 a4,
        __int64 a5)
{
  int v5; // w1
  JNIEnv *v10; // [xsp+38h] [xbp+38h] BYREF
  unsigned int v11; // [xsp+44h] [xbp+44h] BYREF
  int v12; // [xsp+48h] [xbp+48h]
  unsigned int v13; // [xsp+4Ch] [xbp+4Ch]
  jbyte *v14; // [xsp+50h] [xbp+50h]
  char *v15; // [xsp+58h] [xbp+58h]
  jbyteArray v16; // [xsp+60h] [xbp+60h]
 
  v10 = a1;
  v14 = 0LL;
  v15 = 0LL;
  dword_18018 = 9;
  v12 = sub_5674(a3, a4);
  if ( v12 )
  {
    v12 = -1;
  }
  else
  {
    v14 = (*v10)->GetByteArrayElements(v10, a3, 0LL);
    if ( v14 )
    {
      v13 = (*v10)->GetArrayLength(v10, a3);
      v11 = 16 * (v13 / 16 + 1);
      v15 = _cxa_finalize(v11);
      if ( v15 )
      {
        v12 = sub_509C(&v10, v14, v13, v15, &v11, a4, a5);
        if ( !v12 )
        {
          v16 = (*v10)->NewByteArray(v10, v11);
          (*v10)->SetByteArrayRegion(v10, v16, 0LL, v11, v15);
        }
      }
      else
      {
        v12 = -1;
      }
    }
    else
    {
      v12 = -1;
    }
  }
  if ( v14 )
    (*v10)->ReleaseByteArrayElements(v10, a3, v14, 2LL);
  if ( v15 )
    strchr(v15, v5);
  if ( !v12 )
    return v16;
  strtoul((&dword_0 + 3), "crypto_tool", "bangcle crypto tool error code : %d");
  return 0LL;
}
__int64 __fastcall sub_509C(JNIEnv *a1, __int64 a2, unsigned int a3, __int64 a4, __int64 a5, __int64 a6, __int64 a7)
{
  int v7; // w1
  unsigned int v16; // [xsp+68h] [xbp+58h]
  signed int v17; // [xsp+6Ch] [xbp+5Ch]
  unsigned int v18; // [xsp+70h] [xbp+60h]
  unsigned int v19; // [xsp+74h] [xbp+64h]
  __int64 v20; // [xsp+78h] [xbp+68h]
  __int64 v21; // [xsp+80h] [xbp+70h]
  char *v22; // [xsp+88h] [xbp+78h]
 
  v20 = 0LL;
  v21 = 0LL;
  v22 = 0LL;
  v16 = 0;
  bangcle_init(*a1);
  v17 = (*((*a1)->reserved0 + 168))(*a1, a6);
  if ( (v17 & 1) != 0 )
  {
    v16 = -1;
  }
  else
  {
    v21 = (*((*a1)->reserved0 + 169))(*a1, a6, 0LL);
    if ( v21 )
    {
      v18 = v17 / 2;
      v22 = _cxa_finalize((v17 / 2));
      if ( v22 )
      {
        sub_4F9C(v21, v17, v22);
        if ( !a7 )
        {
          if ( dword_18018 == 9 )
            v16 = bangcle_QSM4_ecb_encrypt(a2, a3, a4, a5, v22, v18, 1LL);
          else
            v16 = -1;
        }
        if ( a7 )
        {
          v20 = (*((*a1)->reserved0 + 184))(*a1, a7, 0LL);
          if ( v20 )
          {
            v19 = (*((*a1)->reserved0 + 171))(*a1, a7);
            if ( dword_18018 == 8 )
              bangcle_skb_encrypt(a2, a3, a4, a5, v20, v19, v22, v18, 1, 1);
            if ( dword_18018 == 9 )
              v16 = bangcle_QSM4_cbc_encrypt(a2, a3, a4, a5, v20, v19, v22, v18, 1);
            else
              v16 = -1;
          }
          else
          {
            v16 = -1;
          }
        }
      }
      else
      {
        v16 = -1;
      }
    }
    else
    {
      v16 = -1;
    }
  }
  if ( v21 )
    (*((*a1)->reserved0 + 170))(*a1, a6, v21);
  if ( v22 )
    strchr(v22, v7);
  if ( v20 )
    (*((*a1)->reserved0 + 192))(*a1, a7, v20, 2LL);
  return v16;
}
__int64 __fastcall sub_509C(JNIEnv *a1, __int64 a2, unsigned int a3, __int64 a4, __int64 a5, __int64 a6, __int64 a7)
{
  int v7; // w1
  unsigned int v16; // [xsp+68h] [xbp+58h]
  signed int v17; // [xsp+6Ch] [xbp+5Ch]
  unsigned int v18; // [xsp+70h] [xbp+60h]
  unsigned int v19; // [xsp+74h] [xbp+64h]
  __int64 v20; // [xsp+78h] [xbp+68h]
  __int64 v21; // [xsp+80h] [xbp+70h]
  char *v22; // [xsp+88h] [xbp+78h]
 
  v20 = 0LL;
  v21 = 0LL;
  v22 = 0LL;
  v16 = 0;
  bangcle_init(*a1);
  v17 = (*((*a1)->reserved0 + 168))(*a1, a6);
  if ( (v17 & 1) != 0 )
  {
    v16 = -1;
  }
  else
  {
    v21 = (*((*a1)->reserved0 + 169))(*a1, a6, 0LL);
    if ( v21 )
    {
      v18 = v17 / 2;
      v22 = _cxa_finalize((v17 / 2));
      if ( v22 )
      {
        sub_4F9C(v21, v17, v22);
        if ( !a7 )
        {
          if ( dword_18018 == 9 )
            v16 = bangcle_QSM4_ecb_encrypt(a2, a3, a4, a5, v22, v18, 1LL);
          else
            v16 = -1;
        }
        if ( a7 )
        {
          v20 = (*((*a1)->reserved0 + 184))(*a1, a7, 0LL);
          if ( v20 )
          {
            v19 = (*((*a1)->reserved0 + 171))(*a1, a7);
            if ( dword_18018 == 8 )
              bangcle_skb_encrypt(a2, a3, a4, a5, v20, v19, v22, v18, 1, 1);
            if ( dword_18018 == 9 )
              v16 = bangcle_QSM4_cbc_encrypt(a2, a3, a4, a5, v20, v19, v22, v18, 1);
            else
              v16 = -1;
          }
          else
          {
            v16 = -1;
          }
        }
      }
      else
      {
        v16 = -1;
      }
    }
    else
    {
      v16 = -1;
    }
  }
  if ( v21 )
    (*((*a1)->reserved0 + 170))(*a1, a6, v21);
  if ( v22 )
    strchr(v22, v7);
  if ( v20 )
    (*((*a1)->reserved0 + 192))(*a1, a7, v20, 2LL);
  return v16;
}
emulator.attach().addBreakPoint(module.base + 0x5100, new BreakPointCallback() {
    @Override
    public boolean onHit(Emulator<?> emulator, long address) {
        emulator.getBackend().reg_write(Arm64Const.UC_ARM64_REG_X0,0x40008);
        return true;
    }
});
emulator.attach().addBreakPoint(module.base + 0x5100, new BreakPointCallback() {
    @Override
    public boolean onHit(Emulator<?> emulator, long address) {
        emulator.getBackend().reg_write(Arm64Const.UC_ARM64_REG_X0,0x40008);
        return true;
    }
});
__int64 __fastcall bangcle_internal_crypto(
        __int64 a1,
        int a2,
        __int64 a3,
        __int64 a4,
        __int64 a5,
        unsigned int a6,
        __int64 a7,
        unsigned int a8,
        __int64 a9)
{
  unsigned int prio; // [xsp+54h] [xbp+54h]
  int v17; // [xsp+5Ch] [xbp+5Ch]
  int v18; // [xsp+60h] [xbp+60h]
  __int64 v19; // [xsp+78h] [xbp+78h] BYREF
  int v20; // [xsp+84h] [xbp+84h]
  int v21; // [xsp+90h] [xbp+90h]
 
  v19 = 0LL;
  v17 = 0;
  if ( dword_18008 )
  {
    if ( dword_1800C )
    {
      if ( sub_1704(a7, a8, &v19) == -1 )
      {
        prio = 5;
      }
      else
      {
        if ( !v20 || v20 == 3 || v20 == 4 || v20 == 7 || v20 == 9 )
        {
          v17 = 16;
        }
        else if ( v20 == 1 || v20 == 2 || v20 == 5 || v20 == 6 )
        {
          v17 = 8;
        }
        v18 = sub_2084(a1, a3, a5, a6, v17, a7);
        if ( v18 <= 0 )
        {
          if ( sub_1EDC(a9, &v19) )
          {
            prio = 2;
          }
          else if ( *(a9 + 28) || !(a2 % v17) )
          {
            if ( v21 == 1 && a2 % v17 )
              prio = 14;
            else
              prio = 3;
          }
          else
          {
            prio = 14;
          }
        }
        else
        {
          prio = v18;
        }
      }
    }
    else
    {
      prio = 7;
    }
  }
  else
  {
    prio = 6;
  }
  sub_1D2C(&v19);
  return prio;
}
__int64 __fastcall bangcle_internal_crypto(
        __int64 a1,
        int a2,
        __int64 a3,
        __int64 a4,
        __int64 a5,
        unsigned int a6,
        __int64 a7,
        unsigned int a8,
        __int64 a9)
{
  unsigned int prio; // [xsp+54h] [xbp+54h]
  int v17; // [xsp+5Ch] [xbp+5Ch]
  int v18; // [xsp+60h] [xbp+60h]
  __int64 v19; // [xsp+78h] [xbp+78h] BYREF
  int v20; // [xsp+84h] [xbp+84h]
  int v21; // [xsp+90h] [xbp+90h]
 
  v19 = 0LL;
  v17 = 0;
  if ( dword_18008 )
  {
    if ( dword_1800C )
    {
      if ( sub_1704(a7, a8, &v19) == -1 )
      {
        prio = 5;
      }
      else
      {
        if ( !v20 || v20 == 3 || v20 == 4 || v20 == 7 || v20 == 9 )
        {
          v17 = 16;
        }
        else if ( v20 == 1 || v20 == 2 || v20 == 5 || v20 == 6 )
        {
          v17 = 8;
        }
        v18 = sub_2084(a1, a3, a5, a6, v17, a7);
        if ( v18 <= 0 )
        {
          if ( sub_1EDC(a9, &v19) )
          {
            prio = 2;
          }
          else if ( *(a9 + 28) || !(a2 % v17) )
          {
            if ( v21 == 1 && a2 % v17 )
              prio = 14;
            else
              prio = 3;
          }
          else
          {
            prio = 14;
          }
        }
        else
        {
          prio = v18;
        }
      }
    }
    else
    {
      prio = 7;
    }
  }
  else
  {
    prio = 6;
  }
  sub_1D2C(&v19);
  return prio;
}
__int64 __fastcall bangcle_WB_QSM4_encrypt(__int64 a1, __int64 a2, __int64 *a3)
{
  int i; // [xsp+38h] [xbp+38h]
  int j; // [xsp+38h] [xbp+38h]
  int v7; // [xsp+40h] [xbp+40h]
  int v8; // [xsp+40h] [xbp+40h]
  int v9; // [xsp+40h] [xbp+40h]
  int v10; // [xsp+40h] [xbp+40h]
  int v11; // [xsp+40h] [xbp+40h]
  int v12; // [xsp+40h] [xbp+40h]
  int v13; // [xsp+40h] [xbp+40h]
  int v14; // [xsp+40h] [xbp+40h]
  unsigned int v15; // [xsp+40h] [xbp+40h]
  int v16; // [xsp+44h] [xbp+44h]
  int v17; // [xsp+44h] [xbp+44h]
  int v18; // [xsp+44h] [xbp+44h]
  int v19; // [xsp+44h] [xbp+44h]
  int v20; // [xsp+44h] [xbp+44h]
  int v21; // [xsp+44h] [xbp+44h]
  int v22; // [xsp+44h] [xbp+44h]
  int v23; // [xsp+44h] [xbp+44h]
  unsigned int v24; // [xsp+44h] [xbp+44h]
  int v25; // [xsp+48h] [xbp+48h]
  unsigned int v26; // [xsp+48h] [xbp+48h]
  unsigned int v27; // [xsp+48h] [xbp+48h]
  unsigned int v28; // [xsp+48h] [xbp+48h]
  unsigned int v29; // [xsp+48h] [xbp+48h]
  unsigned int v30; // [xsp+48h] [xbp+48h]
  unsigned int v31; // [xsp+48h] [xbp+48h]
  unsigned int v32; // [xsp+48h] [xbp+48h]
  unsigned int v33; // [xsp+48h] [xbp+48h]
  unsigned int v34; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v35; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v36; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v37; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v38; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v39; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v40; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v41; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v42; // [xsp+4Ch] [xbp+4Ch]
  __int64 v43; // [xsp+50h] [xbp+50h]
  _BYTE v44[16]; // [xsp+58h] [xbp+58h] BYREF
 
  v43 = *a3;
  for ( i = 0; i <= 15; ++i )
    v44[i] = *(bangcle_QSM4_encrypt_xor0 + *(a1 + i));
  v7 = sub_2DA4(v44, 0LL);
  v16 = sub_2DA4(v44, 1LL);
  v25 = sub_2DA4(v44, 2LL);
  v34 = sub_2DA4(v44, 3LL);
  v8 = v7 ^ sub_2EA8(v16 ^ v25 ^ v34, 0LL, v43);
  v17 = v16 ^ sub_2EA8(v8 ^ v25 ^ v34, 1LL, v43);
  v26 = v25 ^ sub_2EA8(v8 ^ v17 ^ v34, 2LL, v43);
  v35 = v34 ^ sub_2EA8(v8 ^ v17 ^ v26, 3LL, v43);
  v9 = v8 ^ sub_2EA8(v17 ^ v26 ^ v35, 4LL, v43);
  v18 = v17 ^ sub_2EA8(v9 ^ v26 ^ v35, 5LL, v43);
  v27 = v26 ^ sub_2EA8(v9 ^ v18 ^ v35, 6LL, v43);
  v36 = v35 ^ sub_2EA8(v9 ^ v18 ^ v27, 7LL, v43);
  v10 = v9 ^ sub_2EA8(v18 ^ v27 ^ v36, 8LL, v43);
  v19 = v18 ^ sub_2EA8(v10 ^ v27 ^ v36, 9LL, v43);
  v28 = v27 ^ sub_2EA8(v10 ^ v19 ^ v36, 10LL, v43);
  v37 = v36 ^ sub_2EA8(v10 ^ v19 ^ v28, 11LL, v43);
  v11 = v10 ^ sub_2EA8(v19 ^ v28 ^ v37, 12LL, v43);
  v20 = v19 ^ sub_2EA8(v11 ^ v28 ^ v37, 13LL, v43);
  v29 = v28 ^ sub_2EA8(v11 ^ v20 ^ v37, 14LL, v43);
  v38 = v37 ^ sub_2EA8(v11 ^ v20 ^ v29, 15LL, v43);
  v12 = v11 ^ sub_2EA8(v20 ^ v29 ^ v38, 16LL, v43);
  v21 = v20 ^ sub_2EA8(v12 ^ v29 ^ v38, 17LL, v43);
  v30 = v29 ^ sub_2EA8(v12 ^ v21 ^ v38, 18LL, v43);
  v39 = v38 ^ sub_2EA8(v12 ^ v21 ^ v30, 19LL, v43);
  v13 = v12 ^ sub_2EA8(v21 ^ v30 ^ v39, 20LL, v43);
  v22 = v21 ^ sub_2EA8(v13 ^ v30 ^ v39, 21LL, v43);
  v31 = v30 ^ sub_2EA8(v13 ^ v22 ^ v39, 22LL, v43);
  v40 = v39 ^ sub_2EA8(v13 ^ v22 ^ v31, 23LL, v43);
  v14 = v13 ^ sub_2EA8(v22 ^ v31 ^ v40, 24LL, v43);
  v23 = v22 ^ sub_2EA8(v14 ^ v31 ^ v40, 25LL, v43);
  v32 = v31 ^ sub_2EA8(v14 ^ v23 ^ v40, 26LL, v43);
  v41 = v40 ^ sub_2EA8(v14 ^ v23 ^ v32, 27LL, v43);
  v15 = v14 ^ sub_2EA8(v23 ^ v32 ^ v41, 28LL, v43);
  v24 = v23 ^ sub_2EA8(v15 ^ v32 ^ v41, 29LL, v43);
  v33 = v32 ^ sub_2EA8(v15 ^ v24 ^ v41, 30LL, v43);
  v42 = v41 ^ sub_2EA8(v15 ^ v24 ^ v33, 31LL, v43);
  sub_2E3C(v42, a2);
  sub_2E3C(v33, a2 + 4);
  sub_2E3C(v24, a2 + 8);
  sub_2E3C(v15, a2 + 12);
  for ( j = 0; j <= 15; ++j )
    *(a2 + j) = *(bangcle_QSM4_encrypt_xor1 + *(a2 + j));
  return __stack_chk_guard;
}
__int64 __fastcall bangcle_WB_QSM4_encrypt(__int64 a1, __int64 a2, __int64 *a3)
{
  int i; // [xsp+38h] [xbp+38h]
  int j; // [xsp+38h] [xbp+38h]
  int v7; // [xsp+40h] [xbp+40h]
  int v8; // [xsp+40h] [xbp+40h]
  int v9; // [xsp+40h] [xbp+40h]
  int v10; // [xsp+40h] [xbp+40h]
  int v11; // [xsp+40h] [xbp+40h]
  int v12; // [xsp+40h] [xbp+40h]
  int v13; // [xsp+40h] [xbp+40h]
  int v14; // [xsp+40h] [xbp+40h]
  unsigned int v15; // [xsp+40h] [xbp+40h]
  int v16; // [xsp+44h] [xbp+44h]
  int v17; // [xsp+44h] [xbp+44h]
  int v18; // [xsp+44h] [xbp+44h]
  int v19; // [xsp+44h] [xbp+44h]
  int v20; // [xsp+44h] [xbp+44h]
  int v21; // [xsp+44h] [xbp+44h]
  int v22; // [xsp+44h] [xbp+44h]
  int v23; // [xsp+44h] [xbp+44h]
  unsigned int v24; // [xsp+44h] [xbp+44h]
  int v25; // [xsp+48h] [xbp+48h]
  unsigned int v26; // [xsp+48h] [xbp+48h]
  unsigned int v27; // [xsp+48h] [xbp+48h]
  unsigned int v28; // [xsp+48h] [xbp+48h]
  unsigned int v29; // [xsp+48h] [xbp+48h]
  unsigned int v30; // [xsp+48h] [xbp+48h]
  unsigned int v31; // [xsp+48h] [xbp+48h]
  unsigned int v32; // [xsp+48h] [xbp+48h]
  unsigned int v33; // [xsp+48h] [xbp+48h]
  unsigned int v34; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v35; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v36; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v37; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v38; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v39; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v40; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v41; // [xsp+4Ch] [xbp+4Ch]
  unsigned int v42; // [xsp+4Ch] [xbp+4Ch]
  __int64 v43; // [xsp+50h] [xbp+50h]
  _BYTE v44[16]; // [xsp+58h] [xbp+58h] BYREF
 
  v43 = *a3;
  for ( i = 0; i <= 15; ++i )
    v44[i] = *(bangcle_QSM4_encrypt_xor0 + *(a1 + i));
  v7 = sub_2DA4(v44, 0LL);
  v16 = sub_2DA4(v44, 1LL);
  v25 = sub_2DA4(v44, 2LL);
  v34 = sub_2DA4(v44, 3LL);
  v8 = v7 ^ sub_2EA8(v16 ^ v25 ^ v34, 0LL, v43);
  v17 = v16 ^ sub_2EA8(v8 ^ v25 ^ v34, 1LL, v43);
  v26 = v25 ^ sub_2EA8(v8 ^ v17 ^ v34, 2LL, v43);
  v35 = v34 ^ sub_2EA8(v8 ^ v17 ^ v26, 3LL, v43);
  v9 = v8 ^ sub_2EA8(v17 ^ v26 ^ v35, 4LL, v43);
  v18 = v17 ^ sub_2EA8(v9 ^ v26 ^ v35, 5LL, v43);
  v27 = v26 ^ sub_2EA8(v9 ^ v18 ^ v35, 6LL, v43);
  v36 = v35 ^ sub_2EA8(v9 ^ v18 ^ v27, 7LL, v43);
  v10 = v9 ^ sub_2EA8(v18 ^ v27 ^ v36, 8LL, v43);
  v19 = v18 ^ sub_2EA8(v10 ^ v27 ^ v36, 9LL, v43);
  v28 = v27 ^ sub_2EA8(v10 ^ v19 ^ v36, 10LL, v43);
  v37 = v36 ^ sub_2EA8(v10 ^ v19 ^ v28, 11LL, v43);
  v11 = v10 ^ sub_2EA8(v19 ^ v28 ^ v37, 12LL, v43);
  v20 = v19 ^ sub_2EA8(v11 ^ v28 ^ v37, 13LL, v43);
  v29 = v28 ^ sub_2EA8(v11 ^ v20 ^ v37, 14LL, v43);
  v38 = v37 ^ sub_2EA8(v11 ^ v20 ^ v29, 15LL, v43);
  v12 = v11 ^ sub_2EA8(v20 ^ v29 ^ v38, 16LL, v43);
  v21 = v20 ^ sub_2EA8(v12 ^ v29 ^ v38, 17LL, v43);
  v30 = v29 ^ sub_2EA8(v12 ^ v21 ^ v38, 18LL, v43);
  v39 = v38 ^ sub_2EA8(v12 ^ v21 ^ v30, 19LL, v43);
  v13 = v12 ^ sub_2EA8(v21 ^ v30 ^ v39, 20LL, v43);
  v22 = v21 ^ sub_2EA8(v13 ^ v30 ^ v39, 21LL, v43);
  v31 = v30 ^ sub_2EA8(v13 ^ v22 ^ v39, 22LL, v43);
  v40 = v39 ^ sub_2EA8(v13 ^ v22 ^ v31, 23LL, v43);
  v14 = v13 ^ sub_2EA8(v22 ^ v31 ^ v40, 24LL, v43);
  v23 = v22 ^ sub_2EA8(v14 ^ v31 ^ v40, 25LL, v43);
  v32 = v31 ^ sub_2EA8(v14 ^ v23 ^ v40, 26LL, v43);
  v41 = v40 ^ sub_2EA8(v14 ^ v23 ^ v32, 27LL, v43);
  v15 = v14 ^ sub_2EA8(v23 ^ v32 ^ v41, 28LL, v43);
  v24 = v23 ^ sub_2EA8(v15 ^ v32 ^ v41, 29LL, v43);
  v33 = v32 ^ sub_2EA8(v15 ^ v24 ^ v41, 30LL, v43);
  v42 = v41 ^ sub_2EA8(v15 ^ v24 ^ v33, 31LL, v43);
  sub_2E3C(v42, a2);
  sub_2E3C(v33, a2 + 4);
  sub_2E3C(v24, a2 + 8);
  sub_2E3C(v15, a2 + 12);
  for ( j = 0; j <= 15; ++j )
    *(a2 + j) = *(bangcle_QSM4_encrypt_xor1 + *(a2 + j));
  return __stack_chk_guard;
}
[!] #################### Function Call ####################
[*] RX@0x1200502c[libbangcle_crypto_tool.so]0x502c         CALL -> 0x1020 (Count: 20)
[!] #################### Function Call ####################
[*] RX@0x120030bc[libbangcle_crypto_tool.so]0x30bc         CALL -> 0x2da4 (Count: 20)
[!] #################### Function Call ####################
[*] RX@0x12003568[libbangcle_crypto_tool.so]0x3568         CALL -> 0x2ea8 (Count: 20)
[!] #################### Function Call ####################
[*] RX@0x120037f4[libbangcle_crypto_tool.so]0x37f4         CALL -> 0x2e3c (Count: 20)
[!] #################### Function Call ####################
[*] RX@0x12002c2c[libbangcle_crypto_tool.so]0x2c2c         CALL -> 0x30ec (Count: 20)
[!] #################### Function Call ####################
[*] RX@0x1200502c[libbangcle_crypto_tool.so]0x502c         CALL -> 0x1020 (Count: 20)
[!] #################### Function Call ####################
[*] RX@0x120030bc[libbangcle_crypto_tool.so]0x30bc         CALL -> 0x2da4 (Count: 20)
[!] #################### Function Call ####################
[*] RX@0x12003568[libbangcle_crypto_tool.so]0x3568         CALL -> 0x2ea8 (Count: 20)
[!] #################### Function Call ####################
[*] RX@0x120037f4[libbangcle_crypto_tool.so]0x37f4         CALL -> 0x2e3c (Count: 20)
[!] #################### Function Call ####################
[*] RX@0x12002c2c[libbangcle_crypto_tool.so]0x2c2c         CALL -> 0x30ec (Count: 20)
import random
from enum import Enum
 
# 定义故障状态枚举类型
FaultStatus = Enum('FaultStatus',
          'Crash Loop NoFault MinorFault MajorFault WrongFault round31Fault round30Fault round29Fault')
 
blockSize = 16
sliceSize = blockSize // 4
 
# 基础运算函数定义
xor = lambda a, b: list(map(lambda x, y: x ^ y, a, b))  # 异或运算
rotl = lambda x, n: ((x << n) & 0xffffffff) | ((x >> (32 - n)) & 0xffffffff# 循环左移
 
# 字节序转换函数
get_uint32_be = lambda key_data: ((key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | (key_data[3]))  # 大端序
get_uint32_le = lambda key_data: ((key_data[3] << 24) | (key_data[2] << 16) | (key_data[1] << 8) | (key_data[0]))  # 小端序
 
put_uint32_be = lambda n: [((n >> 24) & 0xff), ((n >> 16) & 0xff), ((n >> 8) & 0xff), ((n) & 0xff)]
 
bytes_to_list = lambda data: [i for i in data]
 
list_to_bytes = lambda data: b''.join([bytes((i,)) for i in data])
 
dump_byte = lambda a: print(''.join(map(lambda x: ('/x' if len(hex(x)) >= 4 else '/x0') + hex(x)[2:], a)))
 
l_inv = lambda c: c ^ rotl(c, 2) ^ rotl(c, 4) ^ rotl(c, 8) ^ rotl(c, 12) ^ rotl(c, 14) ^ rotl(c, 16) ^ rotl(c,
                                                                                                            18) ^ rotl(
    c, 22) ^ rotl(c, 24) ^ rotl(c, 30)
 
int2bytes = lambda state, size: (state).to_bytes(size, byteorder='big', signed=False)
 
bytes2int = lambda state: int.from_bytes(state, 'big', signed=False)
 
intersect = lambda a, b: [val for val in a if val in b]
 
singleState = lambda a, index: (a >> (index * 8)) & 0xff
 
getSlices = lambda block: [(block >> (32 * i) & 0xffffffff) for i in range(0, 4)]
 
byte2slices = lambda state: [get_uint32_be(state[i * 4: (i + 1) * 4]) for i in range(4)]
 
find_candidate_index = lambda diff: [i for i in range(4, len(diff)) if diff[i] != b'\x00'][0] % 4
 
 
def check_diff(diffmap, n):
    for i in range(n - 1):
        if diffmap[i] is not i:
            return False
    return True
 
 
SM4_ENCRYPT = 0
SM4_DECRYPT = 1
 
SM4_BOXES_TABLE = [
    0xd6, 0x90, 0xe9, 0xfe, 0xcc, 0xe1, 0x3d, 0xb7, 0x16, 0xb6, 0x14, 0xc2, 0x28, 0xfb, 0x2c,
    0x05, 0x2b, 0x67, 0x9a, 0x76, 0x2a, 0xbe, 0x04, 0xc3, 0xaa, 0x44, 0x13, 0x26, 0x49, 0x86,
    0x06, 0x99, 0x9c, 0x42, 0x50, 0xf4, 0x91, 0xef, 0x98, 0x7a, 0x33, 0x54, 0x0b, 0x43, 0xed,
    0xcf, 0xac, 0x62, 0xe4, 0xb3, 0x1c, 0xa9, 0xc9, 0x08, 0xe8, 0x95, 0x80, 0xdf, 0x94, 0xfa,
    0x75, 0x8f, 0x3f, 0xa6, 0x47, 0x07, 0xa7, 0xfc, 0xf3, 0x73, 0x17, 0xba, 0x83, 0x59, 0x3c,
    0x19, 0xe6, 0x85, 0x4f, 0xa8, 0x68, 0x6b, 0x81, 0xb2, 0x71, 0x64, 0xda, 0x8b, 0xf8, 0xeb,
    0x0f, 0x4b, 0x70, 0x56, 0x9d, 0x35, 0x1e, 0x24, 0x0e, 0x5e, 0x63, 0x58, 0xd1, 0xa2, 0x25,
    0x22, 0x7c, 0x3b, 0x01, 0x21, 0x78, 0x87, 0xd4, 0x00, 0x46, 0x57, 0x9f, 0xd3, 0x27, 0x52,
    0x4c, 0x36, 0x02, 0xe7, 0xa0, 0xc4, 0xc8, 0x9e, 0xea, 0xbf, 0x8a, 0xd2, 0x40, 0xc7, 0x38,
    0xb5, 0xa3, 0xf7, 0xf2, 0xce, 0xf9, 0x61, 0x15, 0xa1, 0xe0, 0xae, 0x5d, 0xa4, 0x9b, 0x34,
    0x1a, 0x55, 0xad, 0x93, 0x32, 0x30, 0xf5, 0x8c, 0xb1, 0xe3, 0x1d, 0xf6, 0xe2, 0x2e, 0x82,
    0x66, 0xca, 0x60, 0xc0, 0x29, 0x23, 0xab, 0x0d, 0x53, 0x4e, 0x6f, 0xd5, 0xdb, 0x37, 0x45,
    0xde, 0xfd, 0x8e, 0x2f, 0x03, 0xff, 0x6a, 0x72, 0x6d, 0x6c, 0x5b, 0x51, 0x8d, 0x1b, 0xaf,
    0x92, 0xbb, 0xdd, 0xbc, 0x7f, 0x11, 0xd9, 0x5c, 0x41, 0x1f, 0x10, 0x5a, 0xd8, 0x0a, 0xc1,
    0x31, 0x88, 0xa5, 0xcd, 0x7b, 0xbd, 0x2d, 0x74, 0xd0, 0x12, 0xb8, 0xe5, 0xb4, 0xb0, 0x89,
    0x69, 0x97, 0x4a, 0x0c, 0x96, 0x77, 0x7e, 0x65, 0xb9, 0xf1, 0x09, 0xc5, 0x6e, 0xc6, 0x84,
    0x18, 0xf0, 0x7d, 0xec, 0x3a, 0xdc, 0x4d, 0x20, 0x79, 0xee, 0x5f, 0x3e, 0xd7, 0xcb, 0x39,
    0x48,
]
 
SM4_FK = [0xa3b1bac6, 0x56aa3350, 0x677d9197, 0xb27022dc]
 
SM4_CK = [
    0x00070e15, 0x1c232a31, 0x383f464d, 0x545b6269,
    0x70777e85, 0x8c939aa1, 0xa8afb6bd, 0xc4cbd2d9,
    0xe0e7eef5, 0xfc030a11, 0x181f262d, 0x343b4249,
    0x50575e65, 0x6c737a81, 0x888f969d, 0xa4abb2b9,
    0xc0c7ced5, 0xdce3eaf1, 0xf8ff060d, 0x141b2229,
    0x30373e45, 0x4c535a61, 0x686f767d, 0x848b9299,
    0xa0a7aeb5, 0xbcc3cad1, 0xd8dfe6ed, 0xf4fb0209,
    0x10171e25, 0x2c333a41, 0x484f565d, 0x646b7279
]
 
 
def gen_IN_table():
    IN_table = [[[] for i in range(2 ** 8)] for j in range(2 ** 8)]
    for diff_in in range(1, 2 ** 8):
        for x in range(2 ** 8):
            diff_out = SM4_BOXES_TABLE[x] ^ SM4_BOXES_TABLE[diff_in ^ x]
            IN_table[diff_in][diff_out].append(x)
    return IN_table
 
 
def recovery_key(last_round_key):
    rk = [0] * 36
    rk[32:] = last_round_key[::-1]
    for i in range(31, -1, -1):
        rk[i] = rk[i + 4] ^ round_key(rk[i + 1] ^ rk[i + 2] ^ rk[i + 3] ^ SM4_CK[i])
    rk[:4] = xor(rk[:4], SM4_FK)
    return rk
 
 
def get_masterKey(sk):
    MK = b''.join(int2bytes(x, sliceSize) for x in sk[:4])
    return MK
 
 
def round_key(ka):
    b = [0, 0, 0, 0]
    a = put_uint32_be(ka)
    b[0] = SM4_BOXES_TABLE[a[0]]
    b[1] = SM4_BOXES_TABLE[a[1]]
    b[2] = SM4_BOXES_TABLE[a[2]]
    b[3] = SM4_BOXES_TABLE[a[3]]
    bb = get_uint32_be(b[0:4])
    rk = bb ^ (rotl(bb, 13)) ^ (rotl(bb, 23))
    return rk
 
 
def set_key(key, mode):
    key = bytes_to_list(key)
    sk = [0] * 32
    MK = [0, 0, 0, 0]
    k = [0] * 36
    MK[0:4] = byte2slices(key)
    k[0:4] = xor(MK[0:4], SM4_FK[0:4])
    for i in range(32):
        k[i + 4] = k[i] ^ (
            round_key(k[i + 1] ^ k[i + 2] ^ k[i + 3] ^ SM4_CK[i]))
        sk[i] = k[i + 4]
    mode = mode
    if mode == SM4_DECRYPT:
        for idx in range(16):
            t = sk[idx]
            sk[idx] = sk[31 - idx]
            sk[31 - idx] = t
    return sk
 
 
def f_function(x0, x1, x2, x3, rk):
    # "T algorithm" == "L algorithm" + "t algorithm".
    # args:    [in] a: a is a 32 bits unsigned value;
    # return: c: c is calculated with line algorithm "L" and nonline algorithm "t"
    def sm4_l_t(ka):
        b = [0, 0, 0, 0]
        a = put_uint32_be(ka)
        b[0] = SM4_BOXES_TABLE[a[0]]
        b[1] = SM4_BOXES_TABLE[a[1]]
        b[2] = SM4_BOXES_TABLE[a[2]]
        b[3] = SM4_BOXES_TABLE[a[3]]
        bb = get_uint32_be(b[0:4])
        c = bb ^ (rotl(bb, 2)) ^ (rotl(bb, 10)) ^ (rotl(bb, 18)) ^ (rotl(bb, 24))
        return c
 
    return (x0 ^ sm4_l_t(x1 ^ x2 ^ x3 ^ rk))
 
 
def round(sk, in_put):
    out_put = []
    ulbuf = [0] * 36
    ulbuf[0:4] = byte2slices(in_put)
    for idx in range(32):
        ulbuf[idx + 4] = f_function(ulbuf[idx], ulbuf[idx + 1], ulbuf[idx + 2], ulbuf[idx + 3], sk[idx])
    out_put += put_uint32_be(ulbuf[35])
    out_put += put_uint32_be(ulbuf[34])
    out_put += put_uint32_be(ulbuf[33])
    out_put += put_uint32_be(ulbuf[32])
    return out_put
 
 
def sm4_encrypt(in_put, sk):
    in_put = bytes_to_list(in_put)
    output = round(sk, in_put)
    return list_to_bytes(output)
 
 
# 核心函数
def gen_fault_cipher(in_put, sk, inject_round, verbose=1):
    in_put = bytes_to_list(in_put)
    out_put = []
    ulbuf = [0] * 36
    ulbuf[0:4] = byte2slices(in_put)
    for idx in range(32):
        # 这里在注入攻击了
        if idx is inject_round:
            # 模拟随机故障和故障的随机偏移
            diff = random.randint(1, 2 ** 8 - 1)
            offset = random.randrange(0, 25, 8)
            index = random.randint(1, 3)
            if (verbose > 3):
                print("round %d:Inject diff 0x%.2x at offset %d" % (inject_round, diff, offset))
            ulbuf[idx + index] ^= diff << offset
        ulbuf[idx + 4] = f_function(ulbuf[idx], ulbuf[idx + 1], ulbuf[idx + 2], ulbuf[idx + 3], sk[idx])
    out_put += put_uint32_be(ulbuf[35])
    out_put += put_uint32_be(ulbuf[34])
    out_put += put_uint32_be(ulbuf[33])
    out_put += put_uint32_be(ulbuf[32])
 
    return list_to_bytes(out_put)
 
 
def decrypt_round(in_put, last_round_key, verbose=1):
    output = []
    ulbuf = [0] * 36
    ulbuf[0:4] = byte2slices(in_put)
    round_num = len(last_round_key)
    for idx in range(round_num):
        ulbuf[idx + 4] = f_function(ulbuf[idx], ulbuf[idx + 1], ulbuf[idx + 2], ulbuf[idx + 3], last_round_key[idx])
        if verbose > 3:
            print("decrypt round in %d:%x" % (idx, ulbuf[idx + 4]))
    output += put_uint32_be(ulbuf[round_num])
    output += put_uint32_be(ulbuf[round_num + 1])
    output += put_uint32_be(ulbuf[round_num + 2])
    output += put_uint32_be(ulbuf[round_num + 3])
    return list_to_bytes(output)
 
 
def crack_round(roundFaultList, ref, last_round_key=[], verbose=1):
    if not last_round_key:
        pass
    else:
        """
            if last round key is not empty: require to decrypt the cipher by it
        """
        ref = decrypt_round(ref, last_round_key, verbose)
        for index in range(len(roundFaultList)):
            roundFaultList[index] = decrypt_round(roundFaultList[index], last_round_key, verbose)
    return crack_bytes(roundFaultList, ref, verbose)
 
 
def check(output, encrypt=None, verbose=1, init=False, _intern={}):
    if init:
        _intern.clear()
 
    if not _intern:
        _intern['goldenref'] = output
        if verbose > 2:
            print("FI: record golden ref")
        return (FaultStatus.NoFault, None)
    if output == _intern['goldenref']:
        if verbose > 2:
            print("FI: no impact")
        return (FaultStatus.NoFault, None)
    # diff = int2bytes(output ^ _intern['goldenref'], blockSize)
    diff = xor(output, _intern['goldenref'])
    # record the index of difference
    diffmap = [i for i in range(len(diff)) if diff[i] != 0]
    diffsum = len(diffmap)
    status = FaultStatus.Loop
    if diffsum == 5 or diffsum == 8 or diffsum == 9 or diffsum == 12 or diffsum == 13:
        if check_diff(diffmap, diffsum):
            if verbose > 2:
                if diffsum == 5:
                    print("FI: good candidate for round31!")
                if diffsum == 9 or diffsum == 8:
                    print("FI: good candidate for round30!")
                if diffsum == 13 or diffsum == 12:
                    print("FI: good candidate for round29!")
                if diffsum == 5:
                    status = FaultStatus.round31Fault
                if diffsum == 9 or diffsum == 8:
                    status = FaultStatus.round30Fault
                if diffsum == 12 or diffsum == 13:
                    status = FaultStatus.round29Fault
            # big endian int, transform the index
            return (status, (3 - diffmap[diffsum - 1] % 4))
        else:
            if verbose > 2:
                print("FI: wrong candidate  (%2i)" % diffsum)
            return (FaultStatus.WrongFault, None)
    elif diffsum < 5:
        if verbose > 2:
            print("FI: too few impact  (%2i)" % diffsum)
        return (FaultStatus.MinorFault, None)
    else:
        if verbose > 2:
            print("FI: too much impact (%2i)" % diffsum)
        return (FaultStatus.MajorFault, None)
 
 
def get_candidates(faultCipher, ref, index, verbose=1):
    if not hasattr(get_candidates, '_IN_TABLE'):
        get_candidates._IN_TABLE = gen_IN_table()
 
    faultCipher = bytes2int(faultCipher)
    ref = bytes2int(ref)
    ref_slice = getSlices(ref)
    fault_slice = getSlices(faultCipher)
    delta_C = xor(ref_slice, fault_slice)[3]
    delta_B = l_inv(delta_C)
    A = ref_slice[0] ^ ref_slice[1] ^ ref_slice[2]
    A_star = fault_slice[0] ^ fault_slice[1] ^ fault_slice[2]
    alpha = singleState(A ^ A_star, index)
    beta = singleState(delta_B, index)
    result = get_candidates._IN_TABLE[alpha][beta]
    if result:
        result = [singleState(A, index) ^ x for x in result]
    else:
        result = []
        print("Error: empty key candidate!")
    return result
 
 
def crack_bytes(roundFaultList, ref, verbose=1):
    candidates = [[], [], [], []]
    key = [None] * 4
    _, index = check(ref, init=True)
    for faultCipher in roundFaultList:
        _, index = check(faultCipher)
        if index is not None:
            if key[index] is not None:
                continue
        else:
            if verbose > 2:
                print("bad fault cipher:")
                dump_byte(faultCipher)
                continue
        if verbose > 1:
            print("key index at %d" % (index))
        c = get_candidates(faultCipher, ref, index, verbose)
 
        if not candidates[index]:
            # initial candidate state
            candidates[index] = c
        else:
            candidates[index] = intersect(candidates[index], c)
            # get the exact key
            if (len(candidates[index]) == 1):
                key[index] = candidates[index][0]
                if verbose > 1:
                    print("Round key bytes recovered:")
                    print(''.join(["%02X" % x if x is not None else ".." for x in key]))
 
    # check whether all key bytes have been recovered
    for byte in key:
        if (byte is None):
            print("Only partly recovered:")
            print(''.join(["%02X" % x if x is not None else ".." for x in key]))
            return None
    return get_uint32_le(key)
 
 
def foo():
    masterKey = b'\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10'
    in_put = b'\x01\x23\x45\x67\x89\xab\xcd\xef\xfe\xdc\xba\x98\x76\x54\x32\x10'
    # last_round_key = [k31, k30, k29, k28]
    # last_round_key = [0x9124a012, 0x01cf72e5 ,0x62293496, 0x428d3654]
 
    sk = set_key(masterKey, SM4_ENCRYPT)
    # print("fault output:")
    # 这里是在29-32生成总共120组故障密文
    r31 = [gen_fault_cipher(in_put, sk, 31) for i in range(30)]
    r30 = [gen_fault_cipher(in_put, sk, 30) for i in range(30)]
    r29 = [gen_fault_cipher(in_put, sk, 29) for i in range(30)]
    r28 = [gen_fault_cipher(in_put, sk, 28) for i in range(30)]
    # for i in r31:
    #     print(bytes.hex(i))
    # for i in r30:
    #     print(bytes.hex(i))
    # for i in r29:
    #     print(bytes.hex(i))
    # for i in r28:
    #     print(bytes.hex(i))
    # 这个是计算出正确的密文
    ref = sm4_encrypt(in_put, sk)
    # print(bytes.hex(ref))
    last_round_key = []
    key_schedule = []
    last_round_key.append(crack_round(r31, ref))
    last_round_key.append(crack_round(r30, ref, last_round_key))
    last_round_key.append(crack_round(r29, ref, last_round_key))
    last_round_key.append(crack_round(r28, ref, last_round_key))
    # Round key 32 found:
    # 12A02491
    # Round key 31 found:
    # E572CF01
    # Round key 30 found:
    # 96342962
    # Round key 29 found:
    # 54368D42
    # 这个结果对应32、31、30、29轮
    print(last_round_key)
 
    # key_schedule = recovery_key(last_round_key)
    # MK = get_masterKey(key_schedule)
    # print("Master Key found:")
    # dump_byte(MK)
 
 
if __name__ == '__main__':
    foo()
import random
from enum import Enum
 
# 定义故障状态枚举类型
FaultStatus = Enum('FaultStatus',
          'Crash Loop NoFault MinorFault MajorFault WrongFault round31Fault round30Fault round29Fault')
 
blockSize = 16
sliceSize = blockSize // 4
 
# 基础运算函数定义
xor = lambda a, b: list(map(lambda x, y: x ^ y, a, b))  # 异或运算
rotl = lambda x, n: ((x << n) & 0xffffffff) | ((x >> (32 - n)) & 0xffffffff# 循环左移
 
# 字节序转换函数
get_uint32_be = lambda key_data: ((key_data[0] << 24) | (key_data[1] << 16) | (key_data[2] << 8) | (key_data[3]))  # 大端序
get_uint32_le = lambda key_data: ((key_data[3] << 24) | (key_data[2] << 16) | (key_data[1] << 8) | (key_data[0]))  # 小端序
 
put_uint32_be = lambda n: [((n >> 24) & 0xff), ((n >> 16) & 0xff), ((n >> 8) & 0xff), ((n) & 0xff)]
 
bytes_to_list = lambda data: [i for i in data]
 
list_to_bytes = lambda data: b''.join([bytes((i,)) for i in data])
 
dump_byte = lambda a: print(''.join(map(lambda x: ('/x' if len(hex(x)) >= 4 else '/x0') + hex(x)[2:], a)))

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 118
支持
分享
最新回复 (72)
雪    币: 226
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
学习一下
2025-1-19 21:47
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
3
逐行学习
2025-1-19 22:21
0
雪    币: 157
活跃值: (2316)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
白盒SM4 无法还原出秘钥吧?
2025-1-19 22:24
0
雪    币: 156
活跃值: (2407)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
5
woainihehe 白盒SM4 无法还原出秘钥吧?
可以还原出密钥的
2025-1-20 09:08
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
逐行学习
2025-1-20 09:22
0
雪    币: 157
活跃值: (2316)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
amwait 可以还原出密钥的
好吧,看了SM4的源码   和aes的扩展不一样  以为这样还原不出来 - -  
没想到 还有  phoenixSM4
2025-1-20 09:57
0
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
1
2025-1-20 12:00
0
雪    币: 1450
活跃值: (2748)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
6  学习了
2025-1-20 12:04
0
雪    币: 30
活跃值: (1186)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
10
6
2025-1-20 23:56
0
雪    币: 30
活跃值: (1735)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
tql
2025-1-21 08:25
0
雪    币: 2148
活跃值: (2012)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
12
6
2025-1-21 09:43
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
学习学习
2025-1-21 13:52
0
雪    币: 55
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
14
6
2025-1-21 14:10
0
雪    币: 413
活跃值: (1484)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
15
感谢最后的密钥轮序思路 卡在这卡了小半个月 算出来的密钥死活不对 看到最后醍醐灌顶
2025-1-21 15:21
0
雪    币: 375
活跃值: (2681)
能力值: ( LV3,RANK:30 )
在线值:
发帖
回帖
粉丝
16
学习学习
2025-1-22 10:13
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
17
666
2025-1-22 10:16
0
雪    币: 387
活跃值: (1226)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
18
学习
2025-1-22 13:31
0
雪    币: 1837
活跃值: (3680)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
19
666
2025-1-22 17:10
0
雪    币: 3
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
20
11
2025-1-22 20:57
0
雪    币:
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
21
原来sm4也可以dfa
2025-1-24 15:50
0
雪    币: 753
活跃值: (1442)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
22
老师太厉害了
2025-1-26 09:50
0
雪    币: 27
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
23
2025-1-26 11:22
0
雪    币: 224
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
24
11
2025-1-26 15:50
0
雪    币: 0
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
25
学习一下
2025-1-28 17:06
0
游客
登录 | 注册 方可回帖
返回