-
-
[原创]CTF黑客精神:算法还原
-
2023-12-8 00:51
7985
-
一、前言
这题的解法有很多,可以通过so文件的静态分析,直接将结果写入文件达到解题的目的,不过核心思想肯定是希望我们通过逆向他的算法来获取flag.接下来我将详细的分析算法逆向的过程。
二、代码分析
java层的静态分析
打开APP先简单试玩下,发现点击不玩了或者注册码填写错误提交都会发生闪退,把代码拖入到jadx中。我们从代码找到注册页面。
通过代码可以看出,他的flag核心代码在libmyjni.so里面,调用了这iniSN()、saveSM()、work()这三个函数。在分析so文件之前,我们先解决闪退的问题,关键代码 Process.killProcess(Process.myPid());通过frida hook修改其逻辑。
1 2 3 4 5 6 7 8 | function hook_d(){
Java.perform(function(){
var process_class = Java.use( "android.os.Process" );
process_class[ 'killProcess' ].implementation = function(pid){
this[ 'killProcess' ]( 444 ); / / 或者注释掉,不调用这个也行
}
})
}
|
so层分析
将APK解压,找到目标文件,拖入到IDA中,一眼看去并没有发现iniSN()、saveSM()、work()这个三个函数。
接着我们去JNI_OnLoad中去定位,找到他的伪C代码。
这段代码是一个 JNI(Java Native Interface)的实现,用于在Java虚拟机加载本地库时执行。然后主要看这段代码:
1 | if ( !( * g_env) - >RegisterNatives(g_env, (jclass)native_class, (const JNINativeMethod * )off_5004, 3 ) )
|
RegisterNatives 用于将本地方法与 Java 类关联起来。off_5004 是一个本地方法数组,包含要注册的本地方法的信息。这里注册了3个本地方法。在进入到off_5004后。如图所示,我们来理解下这段汇编代码。
aInitsn: 字符串,表示第一个本地方法的名字为 "initSN"
aV: 字符串,表示第一个本地方法的签名,这里是 "()V",表示无参数无返回值的方法。
n1+1: 指向 initSN 本地方法的实现函数的地址。
那么以此类推:
n1 = iniSN()
n2 = saveSM()
n3 = work()
这个时候思路就非常清晰了,先来看下这三个函数的代码:
n1这个函数的执行逻辑:
打开一个文件 /sdcard/reg.dat,读取文件内容,与指定的字符串进行比较,然后通过调用 setValue 函数将结果写入内存中。最后,关闭文件并返回 fclose 函数的结果。
n2这个函数的执行逻辑:
读取指定文件中的内容,对内容进行异或加密,然后将加密后的内容写回文件中。密钥是根据字符串 "W3_arE_whO_we_ARE" 计算得到的。
n3这个函数的执行逻辑:
他再次调用了n1函数,调用 getValue 函数的值,来判断该返回什么样的内存。返回值有&unk_2E6B、&unk_2E95、&unk_2E5B。我们点进去按A。
很明显Value = getValue(a1) ==1 是我想要的结果。再回过头看n1函数。想要等于1就要满足
1 | strcmp((const char * )v6, "EoPAoY62@ElRD" ) )
|
他又是从文件/sdcard/reg.dat文件取出来的,那问题来了,他又是怎么存的呢?n2函数里面的 :
1 | fputs(v16, v5); / / 将加密后的内容写入文件
|
代码逻辑是输入的值存进去的。再综合整理下代码的执行逻辑。
输入的值等于EoPAoY62@ElRD,再完成加密,保持EoPAoY62@ElRD到/sdcard/reg.dat文件中,再调用n3 (n1 里面满足了条件,设置1值)得到value=1.结束。
目标就是输入的值经过加密等于: EoPAoY62@ElRD 就得出flag
三、算法还原
通过上面的分析他的核心算法在n2中。
通过分析发现他是将我们输入的值和给定的值进行异或操作。然后我们用python 进行算法还原:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | from urllib import parse
v7 = "EoPAoY62@ElRD"
v8 = bytearray(v7, 'utf-8' )
v13 = "W3_arE_whO_we_ARE"
v12 = len (v7)
v9 = 2016
v10 = 0
while v10 < v12:
if v10 % 3 = = 1 :
v9 = (v9 + 5 ) % 16
v11 = ord (v13[v9 + 1 ])
elif v10 % 3 = = 2 :
v9 = (v9 + 7 ) % 15
v11 = ord (v13[v9 + 2 ])
else :
v9 = (v9 + 3 ) % 13
v11 = ord (v13[v9 + 3 ])
v8[v10] ^ = v11
v10 + = 1
decrypted_bytes = bytes(v8)
print (decrypted_bytes.decode( 'utf-8' )) / / 201608Am ! 2333
|
将结果201608Am!2333输入到APP中:
当再次打开的是时候出现:如下。
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法