赞
踩
常见加解密算法的基本原理
在App逆向分析过程中快速识别其使用的算法
加密的工作原理是将“明文”编码为“密文”,通常通过使用被称为算法的密码学数学模型来实现。要将数据解码回明文,需要使用解密密钥,也就是由某种算法创建的一串数字或密码。安全的加密方法会有大量的加密密钥,因此未经授权的人员既无法猜出正确的密钥,也无法使用计算机通过尝试每个可能的组合(称为暴力破解)轻松计算出正确的字符串。
解密的工作原理就是加密的逆过程。
对称密码:即加密与解密时使用相同密钥典型的有RC4、DES、AES等。
序列密码(流密码):将明文消息按字符逐位进行加密。明文与密文长度一致。RC4是序列密码。
分组密码:将明文消息分组(每组有多个字符),逐组进行加密。由于进行了分组,最后一组可能长度不够而进行填充,所以密文可能比明文要长。
非对称密码:即加密与解密时使用不用密钥典型的有RSA、ECC椭圆曲线等。
散列算法:又称哈希函数,对不同长度的输入消息,产生固定长度的输出。这个固定长度的输出称为原输入消息的"散列"或"消息摘要"(Messagedigest)。
BASE64不是一个加密算法,只是一个编码,它没有密钥。只要得到其编码算法,就能还原。
我们以字符串"Hello"为例,解释Base64算法:
将字符串"Hello"转换为二进制数据:
- H:01001000
- e:01100101
- l:01101100
- l:01101100
- o:01101111
将这些二进制数据拼接在一起:
01001000 01100101 01101100 01101100 01101111
将这些二进制数据按照每3个字节(24位)进行分组:
- 01001000 01100101 01101100
- 01101100 01101111
将每个分组的24位数据分割成4个6位的小组:
- 010010 000110 010101 101100
- 011011 000110 110111
将每个6位的小组转换为对应的Base64字符:
- 010010 -> S
- 000110 -> G
- 010101 -> V
- 101100 -> s
- 011011 -> b
- 000110 -> G
- 110111 -> 8
将这些Base64字符拼接在一起:
SGVsbG8=
在这个例子中,Hello
经过Base64编码后的结果是SGVsbG8=
。请注意,由于Hello
的字节数不是3的倍数,因此会在编码结果中添加额外的=
字符进行填充。
可以想一下,编码完成前后字符长度的关系:
E = ((S / 3) + 1) * 4
上面的第5步是核心,它在转换的时候是有一个编码表的:
所以,如果我们改变这个表,比如交换一些字符的位置,就能实现一些base64的变种。
了解了BASE64的原理,那BASE32也就好理解了,它就是使用32个可见字符做编码表,然后是5个bit作为一组,也有填充。
同理,BASE16也好理解。
base64_ollvm.apk
查看JNI函数:
__int64 __fastcall Java_com_kanxue_encrypt01_MainActivity_caicaikanjni(_JNIEnv *a1, __int64 a2, __int64 a3)
第一个参数改成 JNIEnv
指针类型,可读性会好一些。
so里面方法不多,可以一个一个看过去,看谁比较可疑,也可以直接搜索编码表字符串,当然也有可能遇到字符串加密的情况。
找到 sub_72c
函数,混淆的非常严重:
看反汇编还是可以看到编码表的:
- if ( v35 != 161672030 )
- break;
- strcpy(v60, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
- v39 = strlen(a1);
- v43 = v39;
- v35 = 1382490904;
使用 frida 来主动调用一下这个函数,看看它的输入与输出:
- function invoke_sub_72C() {
- var offset = 0x72c;
- var module = Process.getModuleByName("libnative-lib.so");
- var sub_72C_address = module.base.add(offset);
-
- var arg0 = Memory.alloc(5);
- ptr(arg0).writeUtf8String("abcde");
- var hooked_sub_72C = new NativeFunction(sub_72C_address, 'pointer', ['pointer']);
- var result = hooked_sub_72C(arg0);
- console.log("result:", hexdump(result));
- }
-
- function invoke_sub_72C_with_args(arg_content) {
- var offset = 0x72c;
- var module = Process.getModuleByName("libnative-lib.so");
- var sub_72C_address = module.base.add(offset);
-
- var arg_address = Memory.alloc(5);
- ptr(arg_address).writeUtf8String(arg_content);
- var hooked_sub_72C = new NativeFunction(sub_72C_address, 'pointer', ['pointer']);
- var result = hooked_sub_72C(arg_address);
- console.log("result:", hexdump(result));
- }
注意,这里使用 12.8.0 版本的 frida 来执行,15.2.2的在 cli 里面找不到函数,需要再研究研究。
看结果:
- [Google Pixel::com.kanxue.encrypt01]-> invoke_sub_72C()
- result: 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
- 709f48eef0 59 57 4a 6a 5a 47 55 3d 00 00 00 00 00 00 00 00 YWJjZGU=........
- 709f48ef00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48ef10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48ef20 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48ef30 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48ef40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48ef50 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48ef60 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48ef70 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48ef80 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48ef90 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48efa0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48efb0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48efc0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48efd0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 709f48efe0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
可以看到 sub_72C 这个函数就是做了一个 base64 编码操作。
后来研究了一下 https://github.com/oleavr/frida-agent-example 的使用,发现要对函数进行导出,导出名不可以有大写字母或者下划线。需要将代码写在 agent/index.ts 里面,使用 typescript 编写,体验也还不错。
加载脚本,使用 npm run watch,脚本更新后自动编译到 _agent.js:
frida -U -f com.xxxx.xxx --no-pause -l _agent.js
不加 -f
参数就不会重新启动,但是需要传递进程 id:
frida -U 4747 --no-pause -l _agent.js
脚本如下:
- function invoke_sub72c() {
- var offset = 0x72c;
- var module = Process.getModuleByName("libnative-lib.so");
- var sub_72C_address = module.base.add(offset);
-
- var arg_address = Memory.alloc(5);
- arg_address.writeUtf8String("abcde");
- var hooked_sub_72C = new NativeFunction(sub_72C_address, 'pointer', ['pointer']);
- var result = hooked_sub_72C(arg_address);
- console.log("result:", hexdump(result));
- }
-
- function invoke_sub72c_with_args(arg_content: string) {
- var offset = 0x72c;
- var module = Process.getModuleByName("libnative-lib.so");
- var sub_72C_address = module.base.add(offset);
-
- var arg_address = Memory.alloc(5);
- arg_address.writeUtf8String(arg_content);
- var hooked_sub_72C = new NativeFunction(sub_72C_address, 'pointer', ['pointer']);
- var result = hooked_sub_72C(arg_address);
- console.log("result:", hexdump(result));
- }
-
- rpc.exports = {
- invokesub72c: invoke_sub72c
- invokesub72cwithargs: invoke_sub72c_with_args
- };
输出结果如下:
- [Pixel::PID::4747 ]-> rpc.exports.invokesub72cwithargs("abcc")
- result: 0 1 2 3 4 5 6 7 8 9 A B C D E F 0123456789ABCDEF
- 70a22145e0 59 57 4a 6a 59 77 3d 3d 00 00 00 00 00 00 00 00 YWJjYw==........
- 70a22145f0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a2214600 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a2214610 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a2214620 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a2214630 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a2214640 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a2214650 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a2214660 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a2214670 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a2214680 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a2214690 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a22146a0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a22146b0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a22146c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
- 70a22146d0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
base64_ollvm_packer.apk
这个就是上面的包加了个壳而已,使用 frida-dexdump dump一下就可以了。
- public class MainActivity extends AppCompatActivity {
- public native String caicaikanjni(String str);
-
- static {
- System.loadLibrary("native-lib");
- }
-
- /* JADX INFO: Access modifiers changed from: protected */
- @Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- TextView tv = (TextView) findViewById(R.id.sample_text);
- Log.i("test", "test:" + caicaikan("test") + "---" + caicaikanjni("test"));
- tv.setText("考考你");
- }
-
- public String caicaikan(String content) {
- try {
- String encodedString = Base64.encodeToString(content.getBytes("UTF-8"), 0);
- return encodedString;
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- return null;
- }
- }
- }
frida直接进行hook与调用,不需要考虑classLoader:
- export function invokecaicaikan(content) {
- Java.perform(function () {
- // 找到类的实例对象
- var mainActivityClass = Java.use("com.kanxue.encrypt01.MainActivity");
- mainActivityClass.$ownMembers.forEach(
- function(value) {
- log(value)
- }
- )
-
- Java.choose("com.kanxue.encrypt01.MainActivity", {
- onMatch: function (instance) {
- log(instance.caicaikanjni(content));
- log(instance.caicaikan(content))
- }, onComplete: function () { }
- });
- });
- }
查看输出:
- [Pixel::PID::16065 ]-> rpc.exports.invokecck("asf")
- caicaikan
- caicaikanjni
- onCreate
- YXNm
- YXNm
已上传到 https://github.com/aprz512/Android-Crack
二手的程序员
红日初升,其道大光。河出伏流,一泻汪洋。潜龙腾渊,鳞爪飞扬。乳虎啸谷,百兽震惶。鹰隼试翼,风尘翕张。奇花初胎,矞矞皇皇。干将发硎,有作其芒。天戴其苍,地履其黄。纵有千古,横有八荒。前途似海,来日方长。
公众号
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。