当前位置:   article > 正文

常见加解密算法01 - 初步认识

加解密算法

系列目标

  • 常见加解密算法的基本原理

  • 在App逆向分析过程中快速识别其使用的算法

基本原理

加密的工作原理是将“明文”编码为“密文”,通常通过使用被称为算法的密码学数学模型来实现。要将数据解码回明文,需要使用解密密钥,也就是由某种算法创建的一串数字或密码。安全的加密方法会有大量的加密密钥,因此未经授权的人员既无法猜出正确的密钥,也无法使用计算机通过尝试每个可能的组合(称为暴力破解)轻松计算出正确的字符串。

解密的工作原理就是加密的逆过程。

分类

  1. 对称密码:即加密与解密时使用相同密钥典型的有RC4、DES、AES等。

    1. 序列密码(流密码):将明文消息按字符逐位进行加密。明文与密文长度一致。RC4是序列密码。

    2. 分组密码:将明文消息分组(每组有多个字符),逐组进行加密。由于进行了分组,最后一组可能长度不够而进行填充,所以密文可能比明文要长。

  2. 非对称密码:即加密与解密时使用不用密钥典型的有RSA、ECC椭圆曲线等。

  3. 散列算法:又称哈希函数,对不同长度的输入消息,产生固定长度的输出。这个固定长度的输出称为原输入消息的"散列"或"消息摘要"(Messagedigest)。

BASE64编码

BASE64不是一个加密算法,只是一个编码,它没有密钥。只要得到其编码算法,就能还原。

我们以字符串"Hello"为例,解释Base64算法:

  1. 将字符串"Hello"转换为二进制数据:

    1. H:01001000
    2. e:01100101
    3. l:01101100
    4. l:01101100
    5. o:01101111
  2. 将这些二进制数据拼接在一起:

    01001000 01100101 01101100 01101100 01101111
    
  3. 将这些二进制数据按照每3个字节(24位)进行分组:

    1. 01001000 01100101 01101100
    2. 01101100 01101111
  4. 将每个分组的24位数据分割成4个6位的小组:

    1. 010010 000110 010101 101100
    2. 011011 000110 110111
  5. 将每个6位的小组转换为对应的Base64字符:

    1. 010010 -> S
    2. 000110 -> G
    3. 010101 -> V
    4. 101100 -> s
    5. 011011 -> b
    6. 000110 -> G
    7. 110111 -> 8
  6. 将这些Base64字符拼接在一起:

    SGVsbG8=
    

在这个例子中,Hello经过Base64编码后的结果是SGVsbG8=。请注意,由于Hello的字节数不是3的倍数,因此会在编码结果中添加额外的=字符进行填充。

可以想一下,编码完成前后字符长度的关系:

= ((S / 3+ 1* 4

上面的第5步是核心,它在转换的时候是有一个编码表的:

所以,如果我们改变这个表,比如交换一些字符的位置,就能实现一些base64的变种。

了解了BASE64的原理,那BASE32也就好理解了,它就是使用32个可见字符做编码表,然后是5个bit作为一组,也有填充。

同理,BASE16也好理解。

例子1

base64_ollvm.apk

查看JNI函数:

__int64 __fastcall Java_com_kanxue_encrypt01_MainActivity_caicaikanjni(_JNIEnv *a1, __int64 a2, __int64 a3)

第一个参数改成 JNIEnv 指针类型,可读性会好一些。

so里面方法不多,可以一个一个看过去,看谁比较可疑,也可以直接搜索编码表字符串,当然也有可能遇到字符串加密的情况。

找到 sub_72c 函数,混淆的非常严重:

看反汇编还是可以看到编码表的:

  1. if ( v35 != 161672030 )
  2.   break;
  3. strcpy(v60"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
  4. v39 = strlen(a1);
  5. v43 = v39;
  6. v35 = 1382490904;

使用 frida 来主动调用一下这个函数,看看它的输入与输出:

  1. function invoke_sub_72C() {
  2.     var offset = 0x72c;
  3.     var module = Process.getModuleByName("libnative-lib.so");
  4.     var sub_72C_address = module.base.add(offset);
  5.     var arg0 = Memory.alloc(5);
  6.     ptr(arg0).writeUtf8String("abcde");
  7.     var hooked_sub_72= new NativeFunction(sub_72C_address'pointer', ['pointer']);
  8.     var result = hooked_sub_72C(arg0);
  9.     console.log("result:", hexdump(result));
  10. }
  11. function invoke_sub_72C_with_args(arg_content) {
  12.     var offset = 0x72c;
  13.     var module = Process.getModuleByName("libnative-lib.so");
  14.     var sub_72C_address = module.base.add(offset);
  15.     var arg_address = Memory.alloc(5);
  16.     ptr(arg_address).writeUtf8String(arg_content);
  17.     var hooked_sub_72= new NativeFunction(sub_72C_address'pointer', ['pointer']);
  18.     var result = hooked_sub_72C(arg_address);
  19.     console.log("result:", hexdump(result));
  20. }

注意,这里使用 12.8.0 版本的 frida 来执行,15.2.2的在 cli 里面找不到函数,需要再研究研究。

看结果:

  1. [Google Pixel::com.kanxue.encrypt01]-> invoke_sub_72C()                                                             
  2. result:              0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
  3. 709f48eef0  59 57 46547 55 300 00 00 00 00 00 00 00  YWJjZGU=........
  4. 709f48ef00  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  5. 709f48ef10  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  6. 709f48ef20  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  7. 709f48ef30  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  8. 709f48ef40  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  9. 709f48ef50  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  10. 709f48ef60  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  11. 709f48ef70  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  12. 709f48ef80  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  13. 709f48ef90  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  14. 709f48efa0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  15. 709f48efb0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  16. 709f48efc0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  17. 709f48efd0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  18. 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

脚本如下:

  1. function invoke_sub72c() {
  2.     var offset = 0x72c;
  3.     var module = Process.getModuleByName("libnative-lib.so");
  4.     var sub_72C_address = module.base.add(offset);
  5.     var arg_address = Memory.alloc(5);
  6.     arg_address.writeUtf8String("abcde");
  7.     var hooked_sub_72= new NativeFunction(sub_72C_address'pointer', ['pointer']);
  8.     var result = hooked_sub_72C(arg_address);
  9.     console.log("result:", hexdump(result));
  10. }
  11. function invoke_sub72c_with_args(arg_contentstring) {
  12.     var offset = 0x72c;
  13.     var module = Process.getModuleByName("libnative-lib.so");
  14.     var sub_72C_address = module.base.add(offset);
  15.     var arg_address = Memory.alloc(5);
  16.     arg_address.writeUtf8String(arg_content);
  17.     var hooked_sub_72= new NativeFunction(sub_72C_address'pointer', ['pointer']);
  18.     var result = hooked_sub_72C(arg_address);
  19.     console.log("result:", hexdump(result));
  20. }
  21. rpc.exports = {
  22.     invokesub72c: invoke_sub72c
  23.     invokesub72cwithargs: invoke_sub72c_with_args
  24. };

输出结果如下:

  1. [Pixel::PID::4747 ]-> rpc.exports.invokesub72cwithargs("abcc")
  2. result:              0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F  0123456789ABCDEF
  3. 70a22145e0  59 57 4659 77 3300 00 00 00 00 00 00 00  YWJjYw==........
  4. 70a22145f0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  5. 70a2214600  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  6. 70a2214610  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  7. 70a2214620  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  8. 70a2214630  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  9. 70a2214640  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  10. 70a2214650  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  11. 70a2214660  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  12. 70a2214670  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  13. 70a2214680  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  14. 70a2214690  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  15. 70a22146a0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  16. 70a22146b0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  17. 70a22146c0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
  18. 70a22146d0  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................

例子2

base64_ollvm_packer.apk

这个就是上面的包加了个壳而已,使用 frida-dexdump dump一下就可以了。

  1. public class MainActivity extends AppCompatActivity {
  2.     public native String caicaikanjni(String str);
  3.     static {
  4.         System.loadLibrary("native-lib");
  5.     }
  6.     /* JADX INFO: Access modifiers changed from: protected */
  7.     @Override // androidx.appcompat.app.AppCompatActivity, androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
  8.     public void onCreate(Bundle savedInstanceState) {
  9.         super.onCreate(savedInstanceState);
  10.         setContentView(R.layout.activity_main);
  11.         TextView tv = (TextView) findViewById(R.id.sample_text);
  12.         Log.i("test""test:" + caicaikan("test"+ "---" + caicaikanjni("test"));
  13.         tv.setText("考考你");
  14.     }
  15.     public String caicaikan(String content) {
  16.         try {
  17.             String encodedString = Base64.encodeToString(content.getBytes("UTF-8"), 0);
  18.             return encodedString;
  19.         } catch (UnsupportedEncodingException e) {
  20.             e.printStackTrace();
  21.             return null;
  22.         }
  23.     }
  24. }

frida直接进行hook与调用,不需要考虑classLoader:

  1. export function invokecaicaikan(content) {
  2.     Java.perform(function () {
  3.         // 找到类的实例对象
  4.         var mainActivityClass = Java.use("com.kanxue.encrypt01.MainActivity");
  5.         mainActivityClass.$ownMembers.forEach(
  6.             function(value) {
  7.                 log(value)
  8.             }
  9.         )
  10.         Java.choose("com.kanxue.encrypt01.MainActivity", {
  11.             onMatchfunction (instance) {
  12.                 log(instance.caicaikanjni(content));
  13.                 log(instance.caicaikan(content))
  14.             }, onCompletefunction () { }
  15.         });
  16.     });
  17. }

查看输出:

  1. [Pixel::PID::16065 ]-> rpc.exports.invokecck("asf")
  2. caicaikan
  3. caicaikanjni
  4. onCreate
  5. YXNm
  6. YXNm

文件

已上传到 https://github.com/aprz512/Android-Crack

二手的程序员

红日初升,其道大光。河出伏流,一泻汪洋。潜龙腾渊,鳞爪飞扬。乳虎啸谷,百兽震惶。鹰隼试翼,风尘翕张。奇花初胎,矞矞皇皇。干将发硎,有作其芒。天戴其苍,地履其黄。纵有千古,横有八荒。前途似海,来日方长。

公众号

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/600341
推荐阅读
相关标签
  

闽ICP备14008679号