【Android安全-白盒SM4的DFA方案】此文章归类为:Android安全。
声明:本文章中所有内容仅供学习交流使用,不用于其他任何目的,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关,擅自使用本文讲解的技术而导致的任何意外,作者均不负责,若有侵权,请联系作者立即删除!
介绍:随着越来越多的人都会白盒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)))
更多【Android安全-白盒SM4的DFA方案】相关视频教程:www.yxfzedu.com