EasyJNI

  最近正好在出写JNI,正好看到了一道JNI相关的较为简单明了的CTF,就一时兴起的写了,不得不说逆向工程和正向开发确实是可以互补互相加深的

JNI

  JNI(Java Native Interface)即java本地接口,众所周知,android有四层结构(也有说五层结构,即多了一个抽象层,这里不予讨论),应用层与应用接口层是用Java写的,而C/C++核心库和linux内核层由C/C++写的,既然知道了这一点,那理解JNI就很简单了,Java和C/C++肯定是不能直接互相调用的,那么应用层肯定就不能直接调用底层的东西,比如从应用层直接用Java想调用底层C/C++开发的启动相机或NFC等肯定是不能直接实现的。

  这就是JNI的作用了,它充当桥梁,向C/C++转译Java中的方法,向Java转译C/C++的函数,而NDK就是JNI开发工具。

开始解题

      首先不考虑那么多的先AndroidKiller试试

        

      既然都AK(AndroidKiller)了就索性看看JADE

        

    实不相瞒的说在看到这两个东西之前我还想过会不会就是名字放着看的,不是调用的JNI,既然看到这里了就知道了确确实实调用了JNI,那就一步一步的分析吧,首先看OnCreate()

        

 就这一句话是有用的,别的没啥用,这段代码中设置了一个点击事件,即创立了一个Button,并通过ID找到Button,然后设置SetOnClickListener设置监听,点击之后调用上图中的a()方法,传入的String是输入的String,通过a()方法返回一个布尔值

   那就回去看a方法就好了,先放个整体图出来吧

        

       这样就能看到这题恶心人的地方了,他的MainActivity中有a方法,但是还有一个a类,而且在a类中,还有一个静态的Byte[]也叫a,但没啥影响,降维理解一下这里吧:

       初始化一个a类locala,并把输入进去的String类型的字符串穿换成Byte[]类型组传入a类locala的a方法。看着可能绕,自己做的就还行了。

        

       那就看a类吧

          

   不知道是不是我jade版本的问题,看这个a数组里边的东西,明明是char型的东西,没有自动转换成字符,这就很烦了,于是我换成了JEB

 1 package com.a.easyjni;
2
3 public class a {
4 private static final char[] a;
5
6 static {
7 a.a = new char[]{'i', '5', 'j', 'L', 'W', '7', 'S', '0', 'G', 'X', '6', 'u', 'f', '1', 'c', 'v', '3', 'n', 'y', '4', 'q', '8', 'e', 's', '2', 'Q', '+', 'b', 'd', 'k', 'Y', 'g', 'K', 'O', 'I', 'T', '/', 't', 'A', 'x', 'U', 'r', 'F', 'l', 'V', 'P', 'z', 'h', 'm', 'o', 'w', '9', 'B', 'H', 'C', 'M', 'D', 'p', 'E', 'a', 'J', 'R', 'Z', 'N'};
8 }
9
10 public a() {
11 super();
12 }
13
14 public String a(byte[] arg10) {
15 int v8 = 3;
16 StringBuilder v4 = new StringBuilder();
17 int v0;
18 for(v0 = 0; v0 <= arg10.length - 1; v0 += 3) {
19 byte[] v5 = new byte[4];
20 int v3 = 0;
21 byte v2 = 0;
22 while(v3 <= 2) {
23 if(v0 + v3 <= arg10.length - 1) {
24 v5[v3] = ((byte)(v2 | (arg10[v0 + v3] & 0xFF) >>> v3 * 2 + 2));
25 v2 = ((byte)(((arg10[v0 + v3] & 0xFF) << (2 - v3) * 2 + 2 & 0xFF) >>> 2));
26 }
27 else {
28 v5[v3] = v2;
29 v2 = 0x40;
30 }
31
32 ++v3;
33 }
34
35 v5[v8] = v2;
36 int v2_1;
37 for(v2_1 = 0; v2_1 <= v8; ++v2_1) {
38 if(v5[v2_1] <= 0x3F) {
39 v4.append(a.a[v5[v2_1]]);
40 }
41 else {
42 v4.append('=');
43 }
44 }
45 }
46
47 return v4.toString();
48 }
49 }

   实不相瞒的说,要不是我去看别人的WP我都没有意识到这是变种的Base64加密,不过这个a作为密码表还是很明显的,那再重新理一下逻辑,输入了一个字符串Str,这个字符串在a.a中被加密成立Str1,Str1被传到了布尔型的native层的ncheck中经过检测,    并由返回的布尔值判断输出。

   那就简单了,看ncheck就好了,在ncheck中有判断需要逆向回去的字符串

      将apk解压

      

  

      so库就在lib下了

      IDA大法好

        

  Ctrl+F搜索ncheck就得到了想要的函数,分析一下这个函数吧,在v6之前说的都是废话,不用管

 1 v6 = (const char *)(*(int (__fastcall **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a3, 0);
2 if ( strlen(v6) == 32 )
3 {
4 v7 = 0;
5 do
6 {
7 v8 = &s1[v7];
8 s1[v7] = v6[v7 + 16];
9 v9 = v6[v7++];
10 v8[16] = v9;
11 }
12 while ( v7 != 16 );
13 (*(void (__fastcall **)(int, int, const char *))(*(_DWORD *)v4 + 680))(v4, v5, v6);
14 v10 = 0;
15 do
16 {
17 v12 = __OFSUB__(v10, 30);
18 v11 = v10 - 30 < 0;
19 v16 = s1[v10];
20 s1[v10] = s1[v10 + 1];
21 s1[v10 + 1] = v16;
22 v10 += 2;
23 }
24 while ( v11 ^ v12 );
25 v13 = memcmp(s1, "MbT3sQgX039i3g==AQOoMQFPskB1Bsc7", 0x20u);
26 result = 0;
27 if ( !v13 )
28 result = 1;
29 }
30 else
31 {
32 (*(void (__fastcall **)(int, int, const char *))(*(_DWORD *)v4 + 680))(v4, v5, v6);
33 result = 0;
34 }
35 return result;

    不管别的,至少我第一眼看到的就是“MbT3sQgX039i3g==AQOoMQFPskB1Bsc7”,在这我大概就知道是Base64了,这个两个等号太明显了,而且前面的密码表很明显跟base64不一样,那就确定是base64的变种了。在这个函数中也有加密,先看一下这个加密吧,两个do...while,先看两个while,一个是v7由0变到16,一个是计算一个异或值。

    先看第一个do...while,就是将前16位与后16位交换位置,没啥好多说的

    看看第二个do..while,V12是一个判断溢出,__OFSUB__(int a,int b)经过查询之后的作用是判断a-b是否会产生溢出,即a+(-b)是否溢出,-30补码为11100010,如果要产生溢出,那最小的补码为00100000,十进制就是16,跟前面那个就有点意思了,也是16。并且在0到16期间也不可能减去30大于等于0,即V11永远为0。那么V11^V12的第一个跳出条件就是,当V10为16的时候。也能看出来作用就是两两交换位置。

    “v13 = memcmp(s1, "MbT3sQgX039i3g==AQOoMQFPskB1Bsc7", 0x20u)”这里,memcmp(String str1,String str2,int n)作用判断为判断str1与str2的前n位,而0x20就是32。

    从吾爱白嫖来的解密脚本:

#白嫖来的base64解密函数

base64_charset = "i5jLW7S0GX6uf1cv3ny4q8es2Q+bdkYgKOIT/tAxUrFlVPzhmow9BHCMDpEaJRZN"

def decode(base64_str):
base64_bytes = ['{:0>6}'.format(str(bin(base64_charset.index(s))).replace('0b', '')) for s in base64_str if
s != '=']
resp = bytearray()
nums = len(base64_bytes) // 4
remain = len(base64_bytes) % 4
integral_part = base64_bytes[0:4 * nums] while integral_part:
tmp_unit = ''.join(integral_part[0:4])
tmp_unit = [int(tmp_unit[x: x + 8], 2) for x in [0, 8, 16]]
for i in tmp_unit:
resp.append(i)
integral_part = integral_part[4:] if remain:
remain_part = ''.join(base64_bytes[nums * 4:])
tmp_unit = [int(remain_part[i * 8:(i + 1) * 8], 2) for i in range(remain - 1)]
for i in tmp_unit:
resp.append(i) return resp #两两交换位置,还原so内的加密
flag = "AQOoMQFPskB1Bsc7MbT3sQgX039i3g=="
tmp=""
for i in range(len(flag)//2):
tmp += flag[i*2+1] + flag[i*2]
print(tmp)
print(decode(tmp))

攻防世界Mobile5 EasyJNI 安卓逆向CTF的更多相关文章

  1. CTF的一道安卓逆向

    前几天打CTF时遇到的一道安卓逆向,这里简单的写一下思路 首先用jadx打开apk文件,找到simplecheck处(文件名是simplecheck),可以看到基本逻辑就是通过函数a对输入的内容进行判 ...

  2. 攻防世界逆向——game

    攻防世界逆向:game wp 攻防世界逆向新手区的一道题目. 是一道windows的creak,动态调试打开是这样的: 题目说明是让屏幕上所有的图像都亮之后,会出现flag,看来应该是可以玩出来的. ...

  3. 攻防世界 WEB 高手进阶区 TokyoWesterns CTF shrine Writeup

    攻防世界 WEB 高手进阶区 TokyoWesterns CTF shrine Writeup 题目介绍 题目考点 模板注入 Writeup 进入题目 import flask import os a ...

  4. CTF -攻防世界-crypto新手区(5~11)

    easy_RSA 首先如果你没有密码学基础是得去恶补一下的 然后步骤是先算出欧拉函数 之后提交注意是cyberpeace{********}这样的 ,博主以为是flag{}耽误了很长时间  明明没算错 ...

  5. 攻防世界 reverse 进阶 APK-逆向2

    APK-逆向2 Hack-you-2014 (看名以为是安卓逆向呢0.0,搞错了吧) 程序是.net写的,直接祭出神器dnSpy 1 using System; 2 using System.Diag ...

  6. 攻防世界 reverse evil

    这是2017 ddctf的一道逆向题, 挑战:<恶意软件分析> 赛题背景: 员工小A收到了一封邮件,带一个文档附件,小A随手打开了附件.随后IT部门发现小A的电脑发出了异常网络访问请求,进 ...

  7. 攻防世界 | CAT

    来自攻防世界官方WP | darkless师傅版本 题目描述 抓住那只猫 思路 打开页面,有个输入框输入域名,输入baidu.com进行测试 发现无任何回显,输入127.0.0.1进行测试. 发现已经 ...

  8. 记录下做攻防世界的misc题

    0x00 记录一下,代表自己做过 0x01 flag_universe 看简介是来自2018年的百越杯. 将文件下载下来后,就一个flag_universe.pcapng文件,wireshark打开. ...

  9. 异或加密 - cr2-many-time-secrets(攻防世界) - 异性相吸(buuctf)

    Crib dragging attack 在开始了解 Crib dragging attack 之前,先来理一理 异或. 异或加密 [详情请戳这里] XOR 加密简介 异或加密特性: ① 两个值相同时 ...

随机推荐

  1. embed标签属性

    embed标签属性   (一).基本语法: embed src=url 说明:embed可以用来插入各种多媒体,格式可以是 Midi.Wav.AIFF.AU.MP3等等,    Netscape及新版 ...

  2. vim模式及基础命令

    VIM基本介绍vi和在修改vim命令是linux中强大的文本编辑器,由于linux系统一切皆文件,而配置一个服务就是在修改其配置文件的参数vim其实是vi的升级版yum install -y vim ...

  3. 创想变现:斯坦福设计创新课堂ME310分享(下篇)

    编者按:今年6月,微软亚洲研究院人机交互组研究员顾嘉唯,在美国斯坦福大学担任了D-School的ME310设计课程的项目评审.该课程是斯坦福大学的全球联合新产品设计创新课程,学习方式以小组为单位,每个 ...

  4. 让java不再难懂

    废话不都说,直接上图哈 java基础思维导图整理.png 1.Java 简介.png 2.java主要特性.png 3.java发展历史.png 4.java 开发环境配置.png 5.java 基础 ...

  5. [LC] 110. Balanced Binary Tree

    Given a binary tree, determine if it is height-balanced. For this problem, a height-balanced binary ...

  6. Ubuntu14 定时查询任务进程存活状态以及定时杀死进程和重启

    #!/bin/bash while true do pkill -f "savePic.py" python ./savePic.py & #fi sleep 3600 d ...

  7. AndroidP推出多项AI功能,会不会引发新的隐私担忧?

    让谷歌很"伤心"的是,相比苹果iOS系统的统一,Android系统的碎片化态势实在太严重了.就像已经发布一年多的Android O,其占有率仅有4.6%.主要是因为很多手机厂商都会 ...

  8. 安装oracle 11g 客户端,检查过程中报物理内存不足的解决

    今早接到同事电话,说安装oracle 11g客户端的时候,在检查先决条件的时候,报错,说内存不足,但是本机的内存是2G,肯定够用:如图: 找了一圈,原来Oracle执行先决条件检查是依赖c$共享,很多 ...

  9. spring学习笔记四:spring常用注解总结

    使用spring的注解,需要在配置文件中配置组件扫描器,用于在指定的包中扫描注解 <context:component-scan base-package="xxx.xxx.xxx.x ...

  10. A - Divide it! CodeForces - 1176A

    题目: You are given an integer nn. You can perform any of the following operations with this number an ...