-
-
[原创] KCTF 2020 Win. 第四题 路在何方
-
发表于: 2020-11-24 12:18 4856
-
庚子年十月初八,小雪,贞曰:宜坏垣(拆除围墙)。
摘要:这是一条几乎全静态的分析之路,部分片段使用了Unicorn模拟执行。最后有上机的操作,是在最后一步自陷后,投石问路之举。目的是捕捉下特定时态的内存空间,拍个照,以印证静态分析走过的路是否正确,能否继续走下去。分析都是以第一个加固版本为样本【App_Crackme.apk】。
两个版本样本【App_Crackme.apk】和【disanti.apk】参考文后附件。
一、初次见面
(1)7z x App_Crackme.apk -oApp_Crackme
(2)App_Crackme.apk 直接上JEB,如图。
我们通过(1)的7z.exe命令行压缩工具,将apk当zip解压到App_Crackme目录,以备用。通过(2)直接上了手头的JEB。题外,按以前的套路,(1)过后会直接将App_Crackme目录的class.dex上IDA分析;或者通过ApkToolkit.exe将apk转换为jar后上jdgui.exe查看,或者apk直接拖进jadxgui.exe也行(应注意到与前面的jdgui.exe不是同一个东西)。
大概浏览下apk的目录树,这时候注意到目录树中一些so库,这时候,
(3)可以将感兴趣的放IDA分析,如libcrack.so和libjaigu_x86.so,如图。
so在IDA自动分析完后,首先关注的肯定是敏感的到处函数,如JIN_OnLoad和java_com*这些,我们也注意到一些decode**类的函数。
概览后做了一些防护,一时间没法得到太多有用信息,先回去看看java或smali层。
二、绳头
从Mainifest中我们找到绳头"com.stub.StubApp"启动类,如图。
最开始JEB并不会对反编译对象做任何改动,所以类或成员以及方法都是一些不可见字节内容。这样会非常影响阅读和交叉引用查阅,我们可以右键选择rename all自动全部做初步人性化命名。让后根据StupApp类的构造函数和一些关键函数,随类方法和成员做基本重命名。
题外,IDA自身并不带自动重命名,这点比JEB弱很多,估计得找或自行写脚本重命名。
AppStub的OnCreate没什么重要信息,如图。
在AppStub相关构造函数中,我们知晓了AppStup的基本结构。
我们在AppStub非混淆的方法中,注意到attachBaseContext(Context)方法,如图。
其中clsUtils->decrypt_str是逆向后根据起意义重新在jeb自动命名后修正的伪类名和方法。通过逆向可以看到"decrypt_str"类方法是对一类加密字符解密操作,所以做出此命名,如
"q~tb\u007Fyt>s\u007F~du~d>`}>@qs{qwu@qbcub4@qs{qwu"
算法也简单,只是对字符异或0x10,python的解密实现如下。
我们也对clsUtils的其他方法功能做了初步分析,都是写功能性的方法,
其也在util包中,所以如此命名,其他方法如:
(1)make_p0_close(Closeable);传入文件等一类具有.close()的可关闭对象,关闭对象。
(2)IsSupported_x86();是否支持x86。
(3)cmp_bis(BufferedInputStream, BufferedInputStream)Z;对比文件流,用于比对文件。
(4)cmp_copy__assetsFilename_to_dir_filename(Context, String, String, String);迁移文件等操作,用来搬动所需so等文件。
attachBaseContext加载加固相关的so后,转入了so地方方法中
.method public static native interface5(Application)V
而加固相关的so又是自藏式的,如果还有一条路走到黑,就只能网jiagu.so的JNI_OnLoad等方法硬干了。否则,至此,java或smali层已经没太多信息,静态之路变得不好走了,如何是好?
三、遇事不决,量子力学
量子力学?开个玩笑。我们还是继续看看其他什么角落又什么发现。翻箱倒柜,Certificate,.appkey,Resource下的一堆有瞄过,发现有个奇怪的【b.txt】,不得不说,JEB在自动识别上还是很人性化。直接双击就自动识别打开了,惊不惊喜,意不意外?
(1)直接点开了check,如图。
当然,正常还是得按套路,从MainActivity开始。
题外,有试过在手上的Mate 30 Pro安装运行,结果如图。
虽然运行不起来,但还是注意到了CHECK这些。
check的在JEB的smali视图中也还算清晰
(1)key长度为16
(2)ek = crypt(key)
(3)ck = com.kanxue.crackme.MyCrack.crackjni(ek)
(4)eck = crypt(ck)
(5)rk = base64.b64encode(eck)
(6) crk = com.kanxue.crackme.MyCrack.crypt
bOK = (rk== crk? crk:"test")
至此,基本任务需要分别拿下crypt和crackjni方法还有MyCrack.crypt成员。JEB的crypt方法smalic基本看一下有点像RC41。用ApkToolkit将b.txt (重命名为b.dex)转换为jar用jdgui打开,如图
题外:到此,实际上如果一开始直接将【App_Crackme.apk】仍经jadx-gui.exe;完全没有那么什么量子力学什么事,jadx-gui.exe直接一杆到底,自动识别了b.txt,并合并到类代码中,且可以对比看到,jdgui对0x100还不大友好,jadx-gui.exe比较完美还原,如图。
题外:关于crypt方法,只看到samli的时候,直接推定了为RC41(也的确是真的RC41),因为没有动态调试,暂时也没想到直接测试反编译的java代码。所以放一边了,只知道这就算是改版的RC41,至少问题有解。就先看crackjni方法。实际上,眼见不一定为实,这时候一只脚已经踩坑里,尤其当你觉得一个真的RC41是假的时候,而真假似乎都对不上时,"你可能开始怀疑人生"。
(2)crackjni
在IDA中,我们直接定位libcrack.so的导出函Java_com_kanxue_crackme_MyCrack_crackjni,如图
密密麻麻一堆,且IDA的字符串信息几乎没有有价值的信息。
事实上前期的探查中,我们注意到decode一类的导出函数的业务逻辑相对简单,就是对全局静态内容的异或操作,可知是加解密操作,通过观察,我们发现其都是用字节串最后一个字节作为异或因子,因此,我们可以直接解密字节内容看看。通过采样解密,确定是字符串一类。全部解密如下,
我们注意到 ('1F21B', "'crypt'"),也注意到一些base64字符串如,
('1F250', "'l+x7fKd2FBaaEY4NV4309A==\n'")
('1F270', "'ZmxhZ3RyeWFnYWluP30='")
当然还有我们在不支持运行手机上执行的提示
('1F290', "'only support Android 7,Android 7.1,Android 8,Android 8.1,Android 9'")
注意到('1F105', "'frida'")大概猜测到要反这类插桩的功能组件了。我们静态分析,可以先忽略。其中('1F21B', "'crypt'")的交叉引用到Java_com_kanxue_crackme_MyCrack_crackjni,我们跟进查看。逻辑相对也清晰 FindClass、GetStaticFieldID、NewStringUTF的逆向原理来自JNIEnv*结构,其是接口虚函数,根据偏移量可以确定函数名称。
(A)图中下半部分与crypt相关的就是com.kanxue.crackme.MyCrack.crypt="l+x7fKd2FBaaEY4NV4309A==\n"
本想解决crackjni,反而先解决了.MyCrack.crypt的值。
(B)图中上半部分,由于一开始没有逆向确定Hi_dex_for_mprotect的含义,所以无法确定其意义。实际这是挽救前面掉进坑的那只脚的关键。到这里先放一放,本来也没太在意这一部分,毕竟防护加固,涉及写内存修改难免。
在crackjni大概的浏览分析中,基本确定了只有部分有效代码,如下图方框所示,而几个间隔的循环材猜测是反调试一类操作。crackjni入口处,通过定位ek的交叉引用,我们得到了crackjni对ek运输的核心处理函数Hi_core_func_ek_ck_rk,如图
Hi_core_func_ek_ck_rk函数业务逻辑比较清楚
其中ek是crackjni输入,ck是内置的"kaokaonikaokaoni",rk为处理结果。
(1)rk=ek;k初始为ek值,
(2)Hi_stepA_gen_xtbl_from_pck;通过ck,生产xtbl表
(3)Hi_stepB;通过xtbl表和内置的sbox表等对rk进行变换。
其中(2)中的Hi_stepA_gen_xtbl_from_pck没用到ek,而只用到内置固定的ck和固定的sbox表和Hi_Noekeon表。
如图, Hi_sbox_at只是对sbox表的索引[idx]。
其中(3)StepB对rk进行主要的变换,我们需要根据变换业务逻辑得到逆变换
StepB如图,业务逻辑也比较清晰,其中关键的几个变换函数如下
(3.1)Hi_prk_xor_xtbl_R0_A10h_X,使用xtbl中第X行矩阵与rk异或,xtbl有11行,每行16个字节,rk也是16个字节矩阵,变换可逆。
(3.2)Hi_sbox_map,使用sbox表对rk进行索引变换,即rk[i]=sbox[rk[i]],i=0...15,变换可逆。
(3.3)Hi_rxbhgd对rk内部16字节顺序进行交换,变换可逆。
(3.4)Hi_cbx对rk分段(没四个字节一段)进行变换,半可逆。
可见,要完成逆变换,关键是(2)中的xtbl,sbox表以及(3)中的各个变换的逆变换
四、正负变换
(4.1)获取xtbl,sbox
(4.2)各正逆变换
IDA分析中发现,Hi_core_func_ek_ck_rk函数层级调用全是纯内调的shellcode(内有对系统api调用)。所以用unicorn简单配置即可模拟执行,不需要任何额外劫持解析。如图,初始化ek,ck,rk的指针,然后从0xC2B8运行到Step停止,然后我们读取xtbl的值(也读取sbox)备用。
其中关键还是(3.4)的cbx半可逆变换。这里称cbx为16字节Noekeon变换,其只是是分段完成,关键是4字节Noekeon变换;之所以称为Noekeon,是在xtbl生成中用到Noekeon表,为啥又称Noekeon表,因为在用表中的常量google时,其结果展示Noekeon算法,而4字节Noekeon变换与Noekeon算法又相似之处,虽然未证明其关系如何,姑且成为Noekeon变换。这里的之所以说半可逆,可能时能力有限,未找到高效的完全可逆算法,这里四个字节,相互限制,一个确定,则可以推定另一个,是一个闭环证明。及4字节只有256中情形,
所以可以遍历的方式进行逆变换。
五、出路
各正逆变换都是测试过多,大体逻辑应该也不会有问题。在排查各正逆变换没问题后,最终还是回到了前述crackjni对crypt属性设置的代码前面部分片段
如图,这一部分意思可以看出 Hi_dex_for_mprotect 放的是一个指针,
其对指针Hi_dex_for_mprotect偏移0x16D3A6位置修改为 0xD3,0x3D,那么Hi_dex_for_mprotect是啥?静态分析并不能得到快速确定,我们看下Hi_dex_for_mprotect的交叉引用,尝试确定其赋值位置。
如图,有几处都是赋值位置,我们看下第一处。
在第一处赋值位置,我们看到其值来自于局部变量lv_30_src_ptr.04hww,
其也作为mprotect函数的地址addr参数,结果PROT_WIRTE,我们就更确定是为后面的内存修改铺垫了。同时主要到lv_30_src_ptr.04hww也即Hi_dex_for_mprotect的长度是lv_30_src_ptr.08hww。
在mprotect修改前,lv_30_src_ptr.08hww长度len与0x281CB8比较,必须小于它,可见目标内存长度原则上不超过0x281CB8,突破口源自对整个数字的敏感。因为前面用7z解压出的b.txt大小就是2MB左右,会不会就是它?这只是猜测,我们用python的相关函数获取了b.txt的字节大小,
发现就是0x281CB8,我们有理由相信这不是巧合,应该是对b.txt的dex做了修改。我们用十六进制编辑器修改偏移0x16D3A6位置修改为 0xD3,0x3D,然后拖进JEB查看。发现crypt函数内置的crypt_key变了,由原来的"kaokaonio"变成了"keepGoing",可见这是修改了资源ID。这下就清楚了,在encrypt_StepB(即crackjni)对ek处理,crypt函数使用后是"kaokaonio",而调用encrypt_StepB后,变为了"keepGoing",所以crypt再次变换使用了不同的rc_key。即正向变换应该为
结果如图,即flag{thisiskey!}为我们所求。
题外,实际上在rc_key上走过的路并没那么顺畅就跳出坑了。一开始并没有反应过来两次使用的rc_key不一样,测试中都使用相同的rc_key=kaokaonio或keepGoing,结果自然失配。最后怀疑到crackjni使用的内置"kaokaonikaokaoni"会不会也修改成了其他值。但静态分析并没有发现由直接的修改,所以只能上测试机看下。实测中发现反调试还是如预料中那么”强大“。
六、上机
测试机是以前已经刷了官方开发版和unlock的红米,主要是对adb reboot命令和root权限无障碍。
cmd命令行窗口1:
cmd命令行窗口2:
实际上我们目的只是想采集某个时态的内存情况,并不一定需要可以跑起来。
因为校验失败后,退出前有一定的停留,且这时候crackjni已经执行过,xtbl等都可能保留,这是我们捕捉的时机。
具体步骤如下:
如,可直接通过模块导出函数定位crackjni,也可以结合基址和偏移就定位到 ck="kaokaonikaokaoni"如图
Jump(0xCC4A7000+0xC2BE)
这里可以发现ck并没有改变(也可能改回去也不一定,只是假设)
进一步跟进,通过prk和pck指针,我们可以得到 prk = crackjni(crypt(key="1234567890123456"))的运行结果
可以用于验证我们静态分析得到的正变换是否正确,如果逆向的正变换正确,则逆变换也正确;
题外:逆向的正逆变换本身可以自证,因为 anti_tranf(tranf(input))==input,正确性与正变换一致。
经过这个简单的拍照印证,可以确定下述正变换逻辑正确
import
re
def
decrypt_str(bs
=
''):
cbs
=
re.findall(
'\\u00(..)'
,bs)
for
cb
in
cbs:
bs
=
bs.replace(
'\\u00'
+
cb,
chr
(
int
(cb,
0x10
)))
return
''.join([
chr
(
ord
(b)^
0x10
)
for
b
in
bs])
decrypt_str(
"q~tb\u007Fyt>s\u007F~du~d>`}>@qs{qwu@qbcub4@qs{qwu"
)
import
re
def
decrypt_str(bs
=
''):
cbs
=
re.findall(
'\\u00(..)'
,bs)
for
cb
in
cbs:
bs
=
bs.replace(
'\\u00'
+
cb,
chr
(
int
(cb,
0x10
)))
return
''.join([
chr
(
ord
(b)^
0x10
)
for
b
in
bs])
decrypt_str(
"q~tb\u007Fyt>s\u007F~du~d>`}>@qs{qwu@qbcub4@qs{qwu"
)
s_ea
=
0x1F004
e_ea
=
0x1F2FB
cea
=
s_ea
ea_ss
=
{}
while
cea <
=
e_ea:
n
=
idc.Name(cea)
if
n:
nea
=
cea
bs
=
[idc.GetOriginalByte(cea)]
cea
+
=
1
while
idc.Name(cea)
=
=
''
and
cea <
=
e_ea:
bs.append(idc.GetOriginalByte(cea))
cea
+
=
1
while
len
(bs)>
0
and
bs[
-
1
]
=
=
0
:
bs.pop(
-
1
)
x
=
bs[
-
1
]
if
nea
in
[
0x1F0B8
]:
pass
else
:
ea_ss[nea]
=
''.join([
chr
(b^x)
for
b
in
bs])
print
(
"{:X}"
.
format
(nea),ea_ss[nea][:
-
1
].__repr__())
for
i
in
range
(
len
(ea_ss[nea])):
idc.PatchByte(nea
+
i,
ord
(ea_ss[nea][i]))
idc.MakeStr(nea,nea
+
len
(ea_ss[nea]))
s_ea
=
0x1F004
e_ea
=
0x1F2FB
cea
=
s_ea
ea_ss
=
{}
while
cea <
=
e_ea:
n
=
idc.Name(cea)
if
n:
nea
=
cea
bs
=
[idc.GetOriginalByte(cea)]
cea
+
=
1
while
idc.Name(cea)
=
=
''
and
cea <
=
e_ea:
bs.append(idc.GetOriginalByte(cea))
cea
+
=
1
while
len
(bs)>
0
and
bs[
-
1
]
=
=
0
:
bs.pop(
-
1
)
x
=
bs[
-
1
]
if
nea
in
[
0x1F0B8
]:
pass
else
:
ea_ss[nea]
=
''.join([
chr
(b^x)
for
b
in
bs])
print
(
"{:X}"
.
format
(nea),ea_ss[nea][:
-
1
].__repr__())
for
i
in
range
(
len
(ea_ss[nea])):
idc.PatchByte(nea
+
i,
ord
(ea_ss[nea][i]))
idc.MakeStr(nea,nea
+
len
(ea_ss[nea]))
结果如下
(
'1F004'
,
"'/proc/self/task'"
)
(
'1F014'
,
"'/proc/%d/task'"
)
(
'1F022'
,
"'/proc/self/maps'"
)
(
'1F032'
,
"'r'"
)
(
'1F034'
,
"'r-xp'"
)
(
'1F039'
,
"'%lx'"
)
(
'1F03D'
,
"'/system/lib/'"
)
(
'1F04A'
,
"'/odm/lib/'"
)
(
'1F054'
,
"'/vendor/lib/'"
)
(
'1F061'
,
"'openmemory'"
)
(
'1F06C'
,
"'DexFile'"
)
(
'1F074'
,
"'art'"
)
(
'1F078'
,
"'OpenMemory'"
)
(
'1F083'
,
"'OatDexFile'"
)
(
'1F090'
,
"'LoadClassMembers'"
)
(
'1F0A1'
,
"'ClassLinker'"
)
(
'1F0AD'
,
"'LoadMethod'"
)
(
'1F0C0'
,
"'ro.build.version.sdk'"
)
(
'1F0D5'
,
"'\\x7fELF'"
)
(
'1F0E0'
,
"'read /proc/self/maps'"
)
(
'1F0F5'
,
"'/proc/self/maps'"
)
(
'1F105'
,
"'frida'"
)
(
'1F10B'
,
"'fdafd'"
)
(
'1F120'
,
"'%x-%lx %4s %lx %*s %*s %s'"
)
(
'1F13A'
,
"'.oat'"
)
(
'1F140'
,
"'ro.product.cpu.abi'"
)
(
'1F160'
,
"'ro.build.version.sdk'"
)
(
'1F175'
,
"'libc.so'"
)
(
'1F17D'
,
"'execve'"
)
(
'1F190'
,
"'/system/lib/libart.so'"
)
(
'1F1A6'
,
"'openmemory'"
)
(
'1F1C0'
,
"'LoadClassMembers'"
)
(
'1F1E0'
,
"'kaokaonikaokaoni'"
)
(
'1F200'
,
"'com/kanxue/crackme/MyCrack'"
)
(
'1F21B'
,
"'crypt'"
)
(
'1F230'
,
"'Ljava/lang/String;'"
)
(
'1F250'
,
"'l+x7fKd2FBaaEY4NV4309A==\\n'"
)
(
'1F270'
,
"'ZmxhZ3RyeWFnYWluP30='"
)
(
'1F285'
,
"'warn'"
)
(
'1F290'
,
"'only support Android 7,Android 7.1,Android 8,Android 8.1,Android 9'"
)
(
'1F2D3'
,
"'x86'"
)
(
'1F2E0'
,
"'only support arm and arm64'"
)
结果如下
(
'1F004'
,
"'/proc/self/task'"
)
(
'1F014'
,
"'/proc/%d/task'"
)
(
'1F022'
,
"'/proc/self/maps'"
)
(
'1F032'
,
"'r'"
)
(
'1F034'
,
"'r-xp'"
)
(
'1F039'
,
"'%lx'"
)
(
'1F03D'
,
"'/system/lib/'"
)
(
'1F04A'
,
"'/odm/lib/'"
)
(
'1F054'
,
"'/vendor/lib/'"
)
(
'1F061'
,
"'openmemory'"
)
(
'1F06C'
,
"'DexFile'"
)
(
'1F074'
,
"'art'"
)
(
'1F078'
,
"'OpenMemory'"
)
(
'1F083'
,
"'OatDexFile'"
)
(
'1F090'
,
"'LoadClassMembers'"
)
(
'1F0A1'
,
"'ClassLinker'"
)
(
'1F0AD'
,
"'LoadMethod'"
)
(
'1F0C0'
,
"'ro.build.version.sdk'"
)
(
'1F0D5'
,
"'\\x7fELF'"
)
(
'1F0E0'
,
"'read /proc/self/maps'"
)
(
'1F0F5'
,
"'/proc/self/maps'"
)
(
'1F105'
,
"'frida'"
)
(
'1F10B'
,
"'fdafd'"
)
(
'1F120'
,
"'%x-%lx %4s %lx %*s %*s %s'"
)
(
'1F13A'
,
"'.oat'"
)
(
'1F140'
,
"'ro.product.cpu.abi'"
)
(
'1F160'
,
"'ro.build.version.sdk'"
)
(
'1F175'
,
"'libc.so'"
)
(
'1F17D'
,
"'execve'"
)
(
'1F190'
,
"'/system/lib/libart.so'"
)
(
'1F1A6'
,
"'openmemory'"
)
(
'1F1C0'
,
"'LoadClassMembers'"
)
(
'1F1E0'
,
"'kaokaonikaokaoni'"
)
(
'1F200'
,
"'com/kanxue/crackme/MyCrack'"
)
(
'1F21B'
,
"'crypt'"
)
(
'1F230'
,
"'Ljava/lang/String;'"
)
(
'1F250'
,
"'l+x7fKd2FBaaEY4NV4309A==\\n'"
)
(
'1F270'
,
"'ZmxhZ3RyeWFnYWluP30='"
)
(
'1F285'
,
"'warn'"
)
(
'1F290'
,
"'only support Android 7,Android 7.1,Android 8,Android 8.1,Android 9'"
)
(
'1F2D3'
,
"'x86'"
)
(
'1F2E0'
,
"'only support arm and arm64'"
)
from
unicorn
import
*
from
unicorn.arm_const
import
*
import
sark
import
idc
import
struct
segs
=
list
(sark.segments())
elf_base
=
segs[
0
].ea
elf_size
=
segs[
-
1
].ea
+
segs[
-
1
].size
elf_size
=
0x1000
*
((elf_size
+
0x0FFF
)
/
0x1000
)
stack_size
=
4
*
1024
*
1024
mem_size
=
4
*
1024
*
1024
mem_ptr
=
elf_base
+
elf_size
+
stack_size
all_size
=
elf_size
+
stack_size
+
mem_size
stack_init
=
elf_base
+
elf_size
+
stack_size
/
2
mu
=
Uc(UC_ARCH_ARM, UC_MODE_ARM)
mu.mem_map(elf_base, all_size)
print
(
"Init Module: base:= {:06X} size:= {:04X}"
.
format
(elf_base,all_size))
for
seg
in
segs:
segdata
=
idc.get_bytes(seg.ea,seg.size)
mu.mem_write(seg.ea, segdata)
print
(
"Init Seg: base:= {:06X} size:= {:04X}"
.
format
(seg.ea,seg.size))
ek_ptr
=
mem_ptr
+
0
ck_ptr
=
mem_ptr
+
0x100
rk_ptr
=
mem_ptr
+
0x200
mu.mem_write(ek_ptr,
'T\xa7\xd0I;D\xaf\xe2d\xb2\x1b\x1cO%\x1d\x1e'
)
#crypt('1234567890123456') kaokaonio
mu.mem_write(ck_ptr,
"kaokaonikaokaoni"
)
mu.mem_write(rk_ptr,
"\x00"
*
16
)
SP
=
stack_init
-
0x448
mu.reg_write(UC_ARM_REG_SP,SP)
mu.mem_write(SP
+
0xBC
,struct.pack(
"L"
,ek_ptr))
mu.mem_write(SP
+
0xB0
,struct.pack(
"L"
,ck_ptr))
mu.mem_write(SP
+
0xAC
,struct.pack(
"L"
,rk_ptr))
mu.emu_start(
0xC2B8
+
1
,
0x7F98
)
#实际由于StepB不对xtbl变换,可以模拟执行完整个正变换(0xC2B8+1, 0xC2C2)踩读取xtbl
xtbl_ea
=
0x1F318
sbox_ea
=
0x18F5B
xtbl
=
mu.mem_read(xtbl_ea,
0x10
*
11
)
sbox
=
mu.mem_read(sbox_ea,
0x256
)
from
unicorn
import
*
from
unicorn.arm_const
import
*
import
sark
import
idc
import
struct
segs
=
list
(sark.segments())
elf_base
=
segs[
0
].ea
elf_size
=
segs[
-
1
].ea
+
segs[
-
1
].size
elf_size
=
0x1000
*
((elf_size
+
0x0FFF
)
/
0x1000
)
stack_size
=
4
*
1024
*
1024
mem_size
=
4
*
1024
*
1024
mem_ptr
=
elf_base
+
elf_size
+
stack_size
all_size
=
elf_size
+
stack_size
+
mem_size
stack_init
=
elf_base
+
elf_size
+
stack_size
/
2
mu
=
Uc(UC_ARCH_ARM, UC_MODE_ARM)
mu.mem_map(elf_base, all_size)
print
(
"Init Module: base:= {:06X} size:= {:04X}"
.
format
(elf_base,all_size))
for
seg
in
segs:
segdata
=
idc.get_bytes(seg.ea,seg.size)
mu.mem_write(seg.ea, segdata)
print
(
"Init Seg: base:= {:06X} size:= {:04X}"
.
format
(seg.ea,seg.size))
ek_ptr
=
mem_ptr
+
0
ck_ptr
=
mem_ptr
+
0x100
rk_ptr
=
mem_ptr
+
0x200
mu.mem_write(ek_ptr,
'T\xa7\xd0I;D\xaf\xe2d\xb2\x1b\x1cO%\x1d\x1e'
)
#crypt('1234567890123456') kaokaonio
mu.mem_write(ck_ptr,
"kaokaonikaokaoni"
)
mu.mem_write(rk_ptr,
"\x00"
*
16
)
SP
=
stack_init
-
0x448
mu.reg_write(UC_ARM_REG_SP,SP)
mu.mem_write(SP
+
0xBC
,struct.pack(
"L"
,ek_ptr))
mu.mem_write(SP
+
0xB0
,struct.pack(
"L"
,ck_ptr))
mu.mem_write(SP
+
0xAC
,struct.pack(
"L"
,rk_ptr))
mu.emu_start(
0xC2B8
+
1
,
0x7F98
)
#实际由于StepB不对xtbl变换,可以模拟执行完整个正变换(0xC2B8+1, 0xC2C2)踩读取xtbl
xtbl_ea
=
0x1F318
sbox_ea
=
0x18F5B
xtbl
=
mu.mem_read(xtbl_ea,
0x10
*
11
)
sbox
=
mu.mem_read(sbox_ea,
0x256
)
def
encrypt_StepB(ek):
#Step正变换
rk
=
ek
if
isinstance
(ek,
str
):
rk
=
[
ord
(c)
for
c
in
ek]
rk
=
x16(rk,xtbl_xm(
0
))
for
i
in
range
(
1
,
10
):
rk
=
sbox_map(rk)
rk
=
rxchg(rk)
rk
=
cbx16(rk)
rk
=
x16(rk,xtbl_xm(i))
rk
=
sbox_map(rk)
rk
=
rxchg(rk)
rk
=
x16(rk,xtbl_xm(
10
))
return
''.join([
chr
(v)
for
v
in
rk])
def
decrypt_AntiStepB(rk):
#Step逆变换
rk
=
rk
if
isinstance
(rk,
str
):
rk
=
[
ord
(c)
for
c
in
rk]
rk
=
x16(rk,xtbl_xm(
10
))
rk
=
anti_rxchg(rk)
rk
=
anti_sbox_map(rk)
for
i
in
range
(
9
,
0
,
-
1
):
rk
=
x16(rk,xtbl_xm(i))
rk
=
anti_cbx16(rk)
rk
=
anti_rxchg(rk)
rk
=
anti_sbox_map(rk)
rk
=
x16(rk,xtbl_xm(
0
))
return
''.join([
chr
(v)
for
v
in
rk])
def
xtbl_xm(x):
#选取xtbl第x行矩阵
return
xtbl[x
*
16
:x
*
16
+
16
]
def
sbox_map(m):
#sbox_map正变换
r
=
[
0
]
*
len
(m)
for
i,v
in
enumerate
(m):
r[i]
=
sbox[v]
return
r
def
anti_sbox_map(m):
#sbox_map逆变换
r
=
[
0
]
*
len
(m)
for
i,v
in
enumerate
(m):
r[i]
=
sbox.index(
chr
(v))
return
r
def
x16(a,b):
#行矩阵异或
c
=
[
0
]
*
16
for
i
in
range
(
16
):
c[i]
=
a[i]^b[i]
return
c
def
rxchg(r):
#行元素位置正交换
v
=
r[
1
]
r[
1
]
=
r[
5
]
r[
5
]
=
r[
9
]
r[
9
]
=
r[
13
]
r[
13
]
=
v
#
v
=
r[
2
]
r[
2
]
=
r[
10
]
r[
10
]
=
v
#
v
=
r[
6
]
r[
6
]
=
r[
14
]
r[
14
]
=
v
#
v
=
r[
3
]
r[
3
]
=
r[
15
]
r[
15
]
=
r[
11
]
r[
11
]
=
r[
7
]
r[
7
]
=
v
return
r
def
anti_rxchg(r):
#行元素位置逆交换
v
=
r[
7
]
r[
7
]
=
r[
11
]
r[
11
]
=
r[
15
]
r[
15
]
=
r[
3
]
r[
3
]
=
v
#
v
=
r[
14
]
r[
14
]
=
r[
6
]
r[
6
]
=
v
#
v
=
r[
10
]
r[
10
]
=
r[
2
]
r[
2
]
=
v
#
v
=
r[
13
]
r[
13
]
=
r[
9
]
r[
9
]
=
r[
5
]
r[
5
]
=
r[
1
]
r[
1
]
=
v
return
r
def
bxchg(b):
#字节Noekeon正变换,姑且起名Noekeon
b
=
c_ubyte(b)
bm
=
b.value&
0x80
b.value
=
b.value <<
1
if
bm:
b.value ^
=
0b00011011
else
:
b.value ^
=
0b00000000
return
b.value
def
anti_bxchg(b):
#字节Noekeon负变换
b
=
c_ubyte(b)
bm
=
b.value&
1
if
bm:
b.value ^
=
0b00011011
else
:
b.value ^
=
0b00000000
b.value
=
b.value >>
1
b.value
=
b.value|
0x80
if
bm
else
b.value
return
b.value
def
cbx4(m):
#4字节Noekeon正变换
c0,c1,c2,c3
=
m
cx
=
c0
cy
=
c0^c1^c2^c3
t0
=
c0 ^ cy ^ bxchg(c0^c1)
t1
=
c1 ^ cy ^ bxchg(c2^c1)
t2
=
c2 ^ cy ^ bxchg(c3^c2)
t3
=
c3 ^ cy ^ bxchg(c3^c0)
bm
=
[t0,t1,t2,t3]
return
bm
def
cbx16(m):
#16字节Noekeon正变换
bm
=
[]
for
i
in
range
(
0
,
16
,
4
):
bm
+
=
cbx4(m[i:i
+
4
])
return
bm
def
anti_cbx4(m):
#4字节Noekeon负变换
t0,t1,t2,t3
=
m
cy
=
t0^t1^t2^t3
for
rx
in
range
(
0
,
0x100
):
r0
=
rx
r1
=
anti_bxchg(t0^cy^r0)^r0
r2
=
anti_bxchg(t1^cy^r1)^r1
r3
=
anti_bxchg(t2^cy^r2)^r2
br0
=
anti_bxchg(t3^cy^r3)^r3
if
r0
=
=
br0:
return
[r0,r1,r2,r3]
#print(r0,r1,r2,r3)
def
anti_cbx16(m):
#16字节Noekeon负变换
bm
=
[]
for
i
in
range
(
0
,
16
,
4
):
bm
+
=
anti_cbx4(m[i:i
+
4
])
return
bm
from
Crypto.Cipher
import
ARC4
def
myRC4(data,key
=
'kaokaonio'
):
rc41
=
ARC4.new(key)
encrypted
=
rc41.encrypt(data)
def
crypt(p,key
=
'keepGoing'
):
#crypt,实际就是RC41,可用 myRC4替换
pm
=
p
if
isinstance
(p,
str
):
pm
=
[
ord
(c)
for
c
in
p]
#bk = [ord(c) for c in 'kaokaonio']
bk
=
[
ord
(c)
for
c
in
key]
bk_len
=
len
(bk)
bm
=
[i
for
i
in
range
(
0x100
)]
k
=
0
m
=
0
for
i
in
range
(
0x100
):
m
=
(m
+
bk[k]
+
bm[i])&
0xFF
t
=
bm[i]
bm[i]
=
bm[m]
bm[m]
=
t
k
=
(k
+
1
)
%
bk_len
#
pm_len
=
len
(pm)
rm
=
[
0
]
*
pm_len
x
=
0
y
=
0
for
i
in
range
(pm_len):
x
=
(x
+
1
)&
0xFF
y
=
(y
+
bm[x])&
0xFF
t
=
bm[x]
bm[x]
=
bm[y]
bm[y]
=
t
n
=
(bm[x]
+
bm[y])&
0xFF
rm[i]
=
pm[i]^bm[n]
return
rm
def
ts(m):
#矩阵到字符串
return
''.join([
chr
(c_ubyte(i).value)
for
i
in
m])
def
tm(s):
#字符串到矩阵
return
[
ord
(i)
for
i
in
s]
def
prk(m):
#辅助打印矩阵或字符串
if
isinstance
(m,
str
):
print
(
' '
.join([
"{:02X}"
.
format
(
ord
(c))
for
c
in
m]))
else
:
print
(
' '
.join([
"{:02X}"
.
format
(c)
for
c
in
m]))
def
encrypt_StepB(ek):
#Step正变换
rk
=
ek
if
isinstance
(ek,
str
):
rk
=
[
ord
(c)
for
c
in
ek]
rk
=
x16(rk,xtbl_xm(
0
))
for
i
in
range
(
1
,
10
):
rk
=
sbox_map(rk)
rk
=
rxchg(rk)
rk
=
cbx16(rk)
rk
=
x16(rk,xtbl_xm(i))
rk
=
sbox_map(rk)
rk
=
rxchg(rk)
rk
=
x16(rk,xtbl_xm(
10
))
return
''.join([
chr
(v)
for
v
in
rk])
def
decrypt_AntiStepB(rk):
#Step逆变换
rk
=
rk
if
isinstance
(rk,
str
):
rk
=
[
ord
(c)
for
c
in
rk]
rk
=
x16(rk,xtbl_xm(
10
))
rk
=
anti_rxchg(rk)
rk
=
anti_sbox_map(rk)
for
i
in
range
(
9
,
0
,
-
1
):
rk
=
x16(rk,xtbl_xm(i))
rk
=
anti_cbx16(rk)
rk
=
anti_rxchg(rk)
rk
=
anti_sbox_map(rk)
rk
=
x16(rk,xtbl_xm(
0
))
return
''.join([
chr
(v)
for
v
in
rk])
def
xtbl_xm(x):
#选取xtbl第x行矩阵
return
xtbl[x
*
16
:x
*
16
+
16
]
def
sbox_map(m):
#sbox_map正变换
r
=
[
0
]
*
len
(m)
for
i,v
in
enumerate
(m):
r[i]
=
sbox[v]
return
r
def
anti_sbox_map(m):
#sbox_map逆变换
r
=
[
0
]
*
len
(m)
for
i,v
in
enumerate
(m):
r[i]
=
sbox.index(
chr
(v))
return
r
def
x16(a,b):
#行矩阵异或
c
=
[
0
]
*
16
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课
赞赏
- [原创] KCTF 2022 Win. 第六题 约束与伪随机 6744
- [原创] KCTF 2021 Win. 第二题 排排坐 21170
- [原创] KCTF 2021 Win. 第一题 算力与攻击模式 4117
- 鸿蒙通识 26023
- [原创] KCTF 2021 Spr. 第二题 未选择的路 9244