题目一
使用jadx
打开algorithmbase_10.apk
JAVA层
使用Frida
获取先生成的随机字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
传入的随机字符串和字符串在Native层加密后的结果如下
1 2 |
|
- 随机字符串:meUx3DppB%Gj]-2J
- 加密后的结果: LCllTadbMHYZ0kNnDitri5==
我们进入Native
层查看加密后的结果
Native层
使用unzip
命令解压apk
获取so
文件
unzip algorithmbase_10.apk -d fileso10
使用Ida
打开so
文件,在Export
表输入JNI
查看加密函数的位置
使用快捷键Y
和N
修改传入的参数名称,以便我们方便我们后续分析
返回值是v13
我们从下往上回溯来到 sub_EE38(v9, v10, v11)
函数
使用Frida
对sub_EE38
函数进行hook
,目的是为了查看传入的参数和传出的参数
脚本如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
|
点击app按钮,Hook的代码如下所示
我们获取到如下参数
sub_EE38(_BYTE *a1, __int64 a2, int a3) | 第一个参数 | 第二个参数 | 第三个参数 |
---|---|---|---|
未处理前 | "" | meUx3DppB%Gj]-2J | 0x10 |
处理后后 | LCllTadbMHYZ0kNnDitri5== |
1 2 3 4 5 6 7 |
|
也就是说 sub_EE38
就是我们找的加密call
我们不妨进入sub_EE38
查看加密细节
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
|
根据call,还原加密算法
用C代码还原加密算法如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
|
还原得到加密结果
1 |
|
使用JS还原加密算法如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
|
使用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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
|
题目二
algorithmbase_12.apk
使用apkInfo打开发现是32位程序
JAVA层
分析如图,先生成随机字符串(我们可以通过Hook
encodeFromJni_12
函数来查看生成的随机字符串的值),然后把生成的随机字符串丢到encodeFromJni_12
里面处理
我们先Hook一下encodeFromJni_12
函数,查看加密前的字符串和加密后的字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
结果如下
1 2 |
|
加密的结果看起来很像是Base64
,我们用64编码后的结果对比一下
不是64编码,但是结果很像。猜测可能是换了码表或并非完全的base64
加密
so
打开IDA
,来到加密函数Java_com_kanxue_algorithmbase_MainActivity_encodeFromJni_112(int a1, int a2, int a3, char *a4)
里面 进行分析
从下往上回溯,定位到sub_8B04
函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
|
进行hook
这里有个坑,就是在使用拦截器对sub_8B04
位置进行定位,如果使用var sub_8B04 = base_address.add(0x8B04);
拦截出错就加一
这样才能进入函数里面
1 |
|
hook代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
hook显示的结果如下
1 2 3 4 |
|
使用IDA
对libnative-lib.so
进行动态调试的时候会发现 aAyzpq23ijrtffg
字符串和我们之前记录的字符串(aAyzpq23ijrtffg DCB "AYZpq23IJrTFfghijklCDE1KLMmBdestU5678GHz0cuvwabN9+/VWXnoOPQRSxy4",0
)并不相同,结合前面的思考,换了码表可能性更大
我们开始hook
aAyzpq23ijrtffg
码表(0x1B000
),这个有个hook
技巧: 复制使用readCString
打印字符串 可能会造成数据丢失,所以我们使用hexdump
(16进制)显示码表的内容会更精确
hook代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
|
码表果然发生改变
看看是不是只是简单换了码表,其他地方有没有进行加密
是的,和猜测的结果完全一致!
再来看看码表是在哪个位置发生改变的,在sub_8ABC
找到码表发生变化的位置,并且发现传参(a1)就是加密前的字符串被int强转
完整的hook代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
|
使用C++还原加密流程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
|
使用JS对加密算法进行还原
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
|
使用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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
|
题目三:
JAVA层
algorithmbase_13.apk
使用JADX
打开apk
文件
通过调用MainActivity类中的encodeFromJni_12方法,将randomAscii字符串进行加密,并将加密后的结果存储在encodeFromJni_12字符串中。然后,使用Log.e方法将randomAscii字符串和加密后的结果一起打印出来,以便进行调试和分析。
SO
使用IDA
打开,按照以前的思路继续从下往上分析
按照第二题的思路,来到sub_8B04
发现和上一题加密没啥区别就只是多了和参数a3
的异或
使用Frida Hook
一下传入参数和传出参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
hook的结果更加验证了我们的思路
直接开始还原算法验证
C++代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 |
|
JS代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
|
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 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
|
JS还原C++函数总结
字符串篇
C++字符串型的传参在JS中的变化
C++的函数传参是如果char数组,当使用JS
对其进行还原时候,为了方便后面字符串型参数在JS
中计算字符串偏移,最好把字符串转为8位无符号整型数组Uint8Array(对应是C++
中的(unsigned char*
))
如下
1 2 3 4 5 6 7 8 9 10 |
|
这么看不太直观。拿刚才的第三题举例
C++中
1 2 3 4 5 6 7 |
|
这里面str1是char数组被强转为整型当成传参进入的sub_8B04
函数
但在JS
中,我们进入sub_8B04
就是传参str是字符串,不需要变整型
为了方便后面的字节偏移计算,我们再把字符串str 转为8位无符号整型数组
1 |
|
C++ 函数里面与字符串类型传参相关的参数 在JS中的还原
1 2 3 4 5 |
|
在JS中还原C++函数sub_8B04里面的参数V9思路:
因为a2
原先是被int强转char数组。v9是(char*
)a2数组偏移v7个字节后的参数
在前面的步骤中,a2在JS中已经被我们设置为8位无符号整型数组,所以v9=a2[v7]
1 2 3 4 |
|
根据这个思路
使用JS还原一下C++代码中* (unsigned __int8*)(v9 + 2) 和 * (unsigned __int8*)(v9 + 2) 代码如下
1 2 |
|
字节运算
由于JS语言的特性,不能直接把字符和数字进行直接的运算,所以要把字符转为Unicode
编码与数字进行运算后,再把得到的结果重新转为字符
需要用js的charat
charCodeAt
fromCharCode
三个函数进行转化这里面贴一下介绍
charAt
方法用于返回指定索引位置的字符。索引位置从0开始计数。语法:
string.charAt(index)
示例:
Copy
1
2
3
var
str =
"Hello World"
;
console.log(str.charAt(0));
// 输出 "H"
console.log(str.charAt(6));
// 输出 "W"
charCodeAt
方法返回指定索引位置的字符的Unicode编码。索引位置从0开始计数。语法:
string.charCodeAt(index)
示例:
1
2
3
var
str =
"Hello World"
;
console.log(str.charCodeAt(0));
// 输出 72
console.log(str.charCodeAt(6));
// 输出 87
fromCharCode
方法从Unicode编码创建一个字符串。语法:
String.fromCharCode(number1, number2, ... , numberX)
示例:
1
console.log(String.fromCharCode(72, 101, 108, 108, 111));
// 输出 "Hello"
现在分析一下前面第三题贴的C代码
1 |
|
这段C代码的含义是将变量a2和v7的和作为地址,取出该地址处的一个字节数据,将其右移两位后转换为无符号8位整数,然后使用这个值作为索引,从数组aAyzpq23ijrtffg中取出对应位置的值,与变量a3进行异或操作,并将结果赋给变量v8。
在JS中aAyzpq23ijrtffg[*(unsigned __int8*)(a2 + v7) >> 2]
这个字符要和a3进行异或,a3是整型不能和字符异或,所以我们要把先把字符转为Unicode码再和a3进行异或运算,然后转为字符串如下
1 |
|
Python 还原C++函数总结
字符串篇
C++字符串型的传参在python中的变化
C++的函数传参是如果char数组,当使用python
对其进行还原时候,为了方便后面字符串型参数在python
中计算字符串偏移,最好把字符串转为bytes数组(对应是C++
中的(unsigned char*
))
如下
1 2 |
|
C++ 函数里面与字符串类型传参相关的参数 在python中的还原
1 2 3 4 5 |
|
在python中还原C++函数sub_8B04里面的参数V9思路:
因为a2
原先是被int强转char数组。v9是(char*
)a2数组偏移v7个字节后的参数
在前面的步骤中,a2在python中已经被我们设置为bytes
数组,所以v9=a2[v7]
1 |
|
根据这个思路
使用python还原一下C++代码中* (unsigned __int8*)(v9 + 2) 和 * (unsigned __int8*)(v9 + 2) 代码如下
1 2 |
|
字节运算
由于python语言的特性,不能直接把字符和数字进行直接的运算,所以要把字符转为Unicode
编码与数字进行运算后,再把得到的结果重新转为字符
chr和ord是Python中的内置函数,用于字符和对应的Unicode编码之间的转换。
chr函数接受一个整数参数,返回对应的字符。例如:
1
2
3
(
chr
(
65
))
# 输出A
(
chr
(
97
))
# 输出a
(
chr
(
8364
))
# 输出€
ord函数接受一个字符参数,返回对应的Unicode编码。例如:
1
2
3
(
ord
(
'A'
))
# 输出65
(
ord
(
'a'
))
# 输出97
(
ord
(
'€'
))
# 输出8364
通过chr和ord函数,我们可以方便地在字符和对应的Unicode编码之间进行转换。
现在分析一下前面第三题贴的C代码
1 |
|
这段C代码的含义是将变量a2和v7的和作为地址,取出该地址处的一个字节数据,将其右移两位后转换为无符号8位整数,然后使用这个值作为索引,从数组aAyzpq23ijrtffg中取出对应位置的值,与变量a3进行异或操作,并将结果赋给变量v8。
在python中aAyzpq23ijrtffg[*(unsigned __int8*)(a2 + v7) >> 2]
这个字符要和a3进行异或,a3是整型不能和字符异或,所以我们要把先把字符转为Unicode码再和a3进行异或运算,然后转为字符串如下
1 |
|