这题的解法有很多,可以通过so文件的静态分析,直接将结果写入文件达到解题的目的,不过核心思想肯定是希望我们通过逆向他的算法来获取flag.接下来我将详细的分析算法逆向的过程。
打开APP先简单试玩下,发现点击不玩了或者注册码填写错误提交都会发生闪退,把代码拖入到jadx中。我们从代码找到注册页面。
通过代码可以看出,他的flag核心代码在libmyjni.so里面,调用了这iniSN()、saveSM()、work()这三个函数。在分析so文件之前,我们先解决闪退的问题,关键代码 Process.killProcess(Process.myPid());通过frida hook修改其逻辑。
将APK解压,找到目标文件,拖入到IDA中,一眼看去并没有发现iniSN()、saveSM()、work()这个三个函数。
接着我们去JNI_OnLoad中去定位,找到他的伪C代码。
这段代码是一个 JNI(Java Native Interface)的实现,用于在Java虚拟机加载本地库时执行。然后主要看这段代码:
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就要满足
他又是从文件/sdcard/reg.dat文件取出来的,那问题来了,他又是怎么存的呢?n2函数里面的 :
代码逻辑是输入的值存进去的。再综合整理下代码的执行逻辑。
输入的值等于EoPAoY62@ElRD,再完成加密,保持EoPAoY62@ElRD到/sdcard/reg.dat文件中,再调用n3 (n1 里面满足了条件,设置1值)得到value=1.结束。
目标就是输入的值经过加密等于: EoPAoY62@ElRD 就得出flag
通过上面的分析他的核心算法在n2中。
通过分析发现他是将我们输入的值和给定的值进行异或操作。然后我们用python 进行算法还原:
将结果201608Am!2333输入到APP中:
当再次打开的是时候出现:如下。
function hook_d(){
Java.perform(function(){
var process_class
=
Java.use(
"android.os.Process"
);
process_class[
'killProcess'
].implementation
=
function(pid){
this[
'killProcess'
](
444
);
/
/
或者注释掉,不调用这个也行
}
})
}
function hook_d(){
Java.perform(function(){
var process_class
=
Java.use(
"android.os.Process"
);
process_class[
'killProcess'
].implementation
=
function(pid){
this[
'killProcess'
](
444
);
/
/
或者注释掉,不调用这个也行
}
})
}
if
( !(
*
g_env)
-
>RegisterNatives(g_env, (jclass)native_class, (const JNINativeMethod
*
)off_5004,
3
) )
if
( !(
*
g_env)
-
>RegisterNatives(g_env, (jclass)native_class, (const JNINativeMethod
*
)off_5004,
3
) )
strcmp((const char
*
)v6,
"EoPAoY62@ElRD"
) )
strcmp((const char
*
)v6,
"EoPAoY62@ElRD"
) )
fputs(v16, v5);
/
/
将加密后的内容写入文件
fputs(v16, v5);
/
/
将加密后的内容写入文件
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
from
urllib
import
parse
[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!