攻防世界新手练习题_MOBILE(移动)

攻防世界新手练习题_MOBILE(移动) 

一、easy-apk

看到某个学习安卓逆向的路线,看到攻防世界。。。然后就去看了下题目
在这里插入图片描述

好久没碰安卓了,工作偶尔需要查看源码,一直没有深入学习

1.安装apk

拿到apk的思路,安装查看一遍,安装后发现需要验证
在安卓模拟器安装
在这里插入图片描述

2.反编译源码

使用jadx-gui直接打开apk
一般在MainActivity就可以看到程序一开始运行的操作
发现需要输入和该字符串类似的才能验证通过

在这里插入图片描述

找到核心加密的编码方法

 public void onClick(View view) {
      if (new Base64New().Base64Encode(((EditText) MainActivity.this.findViewById(C0295R.C0297id.editText)).getText().toString().getBytes()).equals("5rFf7E2K6rqN7Hpiyush7E6S5fJg6rsi5NBf6NGT5rs=")) {
           Toast.makeText(MainActivity.this, "验证通过!", 1).show();
       } else {
           Toast.makeText(MainActivity.this, "验证失败!", 1).show();
       }

发现后面有“=”,与base64的编码类似,但是到在线的base64解密是显示解密失败或者乱码

代码段意思是需要验证输入的文本经过Base64New().Base64Encode后的字符与"5rFf7E2K6rqN7Hpiyush7E6S5fJg6rsi5NBf6NGT5rs="相等,则验证通过

所以我们找到Base64New().Base64Encode方法
在这里插入图片描述

这是对字符串编码的过程
我自己没看懂。。。百度一波后才发现,真的是base64编码,但是解密的字符换了
然后百度了一波原理,最后还是百度找的代码
使用了java进行解密
参考了知乎大神:https://zhuanlan.zhihu.com/p/116110552的解题思路,这里自己只是记录一下

3.解码

最后脚本:

package mobile;
import java.io.ByteArrayOutputStream;  
import java.io.IOException;  
import java.io.OutputStream;  
import java.io.UnsupportedEncodingException;

public class Base64New {
    private static final char[] Base64ByteToStr = {'v', 'w', 'x', 'r', 's', 't', 'u', 'o', 'p', 'q', '3', '4', '5', '6', '7', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'y', 'z', '0', '1', '2', 'P', 'Q', 'R', 'S', 'T', 'K', 'L', 'M', 'N', 'O', 'Z', 'a', 'b', 'c', 'd', 'U', 'V', 'W', 'X', 'Y', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', '8', '9', '+', '/'};
    private static final int RANGE = 255;
    private static byte[] StrToBase64Byte = new byte[128];
    
    //base64 原编码
    public String Base64Encode(byte[] bytes) {
        StringBuilder res = new StringBuilder();
        for (int i = 0; i <= bytes.length - 1; i += 3) {
            byte[] enBytes = new byte[4];
            byte tmp = 0;
            for (int k = 0; k <= 2; k++) {
                if (i + k <= bytes.length - 1) {
                    enBytes[k] = (byte) (((bytes[i + k] & 255) >>> ((k * 2) + 2)) | tmp);
                    tmp = (byte) ((((bytes[i + k] & 255) << (((2 - k) * 2) + 2)) & 255) >>> 2);
                } else {
                    enBytes[k] = tmp;
                    tmp = 64;
                }
            }
            enBytes[3] = tmp;
            for (int k2 = 0; k2 <= 3; k2++) {
                if (enBytes[k2] <= 63) {
                    res.append(Base64ByteToStr[enBytes[k2]]);
                } else {
                    res.append('=');
                }
            }
        }
        return res.toString();
    }
    
    
  //base64 解码 网上找的
    public static byte getIndex(char x)
    {
        byte index = -1;
        String talbe = new String(Base64New.Base64ByteToStr);
        if(x != '=') {
            index = (byte) talbe.indexOf(x);
        }
        else {
            index = 0;
        }
        return index;
    }
    
    
    public static String Base64Decode(String str) {
        String flag = "";
        String flag_temp = "";
        for(int i = 0;i < str.length();i+=4)
        {
            String enf = str.substring(i,i+4);
            byte flag1 = (byte)(((getIndex(enf.charAt(0)) & 255) << 2 | ((getIndex(enf.charAt(1)) & 255) >>> 4)));
            byte flag2 = (byte)(((getIndex(enf.charAt(1)) & 255) << 4 | ((getIndex(enf.charAt(2)) & 255) >>> 2)));
            byte flag3 = (byte)(((getIndex(enf.charAt(2)) & 255) << 6 | ((getIndex(enf.charAt(3)) & 255))));

            flag_temp = "" + (char)flag1 + (char)flag2 + (char)flag3;
            flag += flag_temp;
        }
        System.out.println(flag);
        return flag;
    } 
    
    
    public static void main(String[] args) {
        String str="5rFf7E2K6rqN7Hpiyush7E6S5fJg6rsi5NBf6NGT5rs=";
        String flag=Base64New.Base64Decode(str);
    }
    
}

输出结果:
在这里插入图片描述
还找到一个python解法的,直接原链接:https://blog.csdn.net/sun8890446/article/details/102688191
base64原理解析:https://blog.csdn.net/wo541075754/article/details/81734770

4.输入flag

最后输入flag,
还有标准格式flag{}
估计后面的都是要这样的。。
所以最终答案为flag{05397c42f9b6da593a3644162d36eb01}
感想:
平时工作就是直接使用工具反编译,然后看懂app的大概思路,找后台传输数据或者app的核心运行原理思路,突然一接触这题目,学习路程漫漫

 app1

最重要的一步:下载app,安装然后认真的去运行:

  1. 随意输入会收到提示“再接再厉,加油~”,不输入的话提示“年轻人不要耍小聪明噢”。除了被调戏没有可获取信息的了,所以只能靠逆向扒了她:
  2. 使用(1)jeb直接打开apk 或者 (2)dex2jar反编译apk,使用jd-gui打开jar文件,搜索上述提到的关键字:可以看到主要逻辑了,我们要闯关成功,所以要做到下面这个条件才可以:

但首先我们要完成这个条件:

(有过代码基础的同学可以轻易看出来,没有基础的同学可以把代码复制到百度上,有相关的解释)

其实就是一个异或运算,而异或比较的两个值就是 str1 和 I , 将关键词在代码中查询,可找到:

        然后继续====》

所以到这就很清晰了, 将 15和 X<cP[?PHNB<P?aj 进行异或。

异或过程把字符串转为ascii码, 15转成16进制,将每个字符转成的ascii码与 “15的16进制”进行异或:

C代码:

Python代码:

a 和 b 的16进制自己去填

a= 
b=[]
flag=''
for j in b:
    flag +=(a^j)
    print(flag) 

最后结果为:W3l_T0_GAM3_0ne

app2

使用APKIDE打开apk文件,我们发现这里多了一个libc的文件夹,libc好像是什么函数动态链接库
在这里插入图片描述
查看一下,里面好像有个什么解密的????
先不管,继续往下走~~
主函数里面没看出什么鬼东西,不过发现了一个类中有一个特殊的字符串,需要解密啥的:
在这里插入图片描述
看见import com.tencent.testvuln.c.Encryto;,直接进入其中:
在这里插入图片描述
发现好熟悉,就显示我们刚刚在libc中看见的那个东西???一个解密的函数???
直接是看libc中的这个文件,直接解压apk2,找到libc中的这个文件
.os文件直接使用ida打开,发现有一个encode函数,直接查看伪代码:
在这里插入图片描述
根据名字感觉是AES加密,不过AES需要密钥,我们可以看见上面有一串字符串:thisisatestkey==
猜测这就是密钥,试试:
在这里插入图片描述
好像就是,,,,提交正确,,,,
说实话这里面的函数完全没看懂,,,全靠猜,,
一脸懵逼

easyjni

下载文件,发现是个APK文件,直接APKIDE分析一波,发现有lib,看一看名字:
在这里插入图片描述
直接java反编译,找到主函数:
在这里插入图片描述
调用了lib里面的库。同时还调用了a类:
在这里插入图片描述
母庸置疑,和我们之前做的一个base64变码表的一样,应该也是一个变换码表的base64:
在这里插入图片描述
进入到libc中的native中看看,解压后用ida打开:
在这里插入图片描述
emmmm,好像逻辑还是挺简单的,前面16个和后面的16个换一下位置,然后两两交换一下位置:
在这里插入图片描述
得到一个字符串:QAoOQMPFks1BsB7cbM3TQsXg30i9g3==
使用上次自己写的base64变换码表脚本,变换一下码表,跑一下:
在这里插入图片描述
get flag:flag{just_ANot#er_@p3}

easy-so

拖进模拟器是一个验证框,我们直接上jeb:

调用本地方法public static native int CheckString(String arg0),若验证一致返回1,否则返回0.

将apk重命名为zip后解压,在lib目录将.so文件拖进IDA,找到函数CheckString,代码如下:

 
_BOOL4 __cdecl Java_com_testjava_jack_pingan2_cyberpeace_CheckString(int a1, int a2, int a3)
{
  const char *v3; // ST1C_4
  size_t v4; // edi
  char *v5; // esi
  size_t v6; // edi
  char v7; // al
  char v8; // al
  size_t v9; // edi
  char v10; // al
 
  v3 = (const char *)(*(int (__cdecl **)(int, int, _DWORD))(*(_DWORD *)a1 + 676))(a1, a3, 0);
  v4 = strlen(v3);
  v5 = (char *)malloc(v4 + 1);
  memset(&v5[v4], 0, v4 != -1);
  memcpy(v5, v3, v4);
  if ( strlen(v5) >= 2 )
  {
    v6 = 0;
    do
    {
      v7 = v5[v6];
      v5[v6] = v5[v6 + 16];
      v5[v6++ + 16] = v7;
    }
    while ( v6 < strlen(v5) >> 1 );
  }
  v8 = *v5;
  if ( *v5 )
  {
    *v5 = v5[1];
    v5[1] = v8;
    if ( strlen(v5) >= 3 )
    {
      v9 = 2;
      do
      {
        v10 = v5[v9];
        v5[v9] = v5[v9 + 1];
        v5[v9 + 1] = v10;
        v9 += 2;
      }
      while ( v9 < strlen(v5) );
    }
  }
  return strcmp(v5, "f72c5a36569418a20907b55be5bf95ad") == 0;
}
 
 

 

我们大胆推测const char * v3是传入的字符串,接下来逐个分析代码逻辑:

v4 = strlen(v3);              //取变量v4=v3的字符串长度,假设v3="abcd",v4=4
v5 = (char *)malloc(v4 + 1);  //为字符指针v5请求一块长度为v4+1的内存空间
memset(&v5[v4], 0, v4 != -1); //将v5扩增一倍并后面扩增的部分初始化为0,此行代码结束,v5=----0000
memcpy(v5, v3, v4);           //将v3的内容复制到v5中
if ( strlen(v5) >= 2 )        //若v5的长度大于等于2则执行花括号内的内容
  {
    v6 = 0;             //初始化v6=0
    do                  //执行循环
    {
      v7 = v5[v6];     //从第0个开始读取v5的每个字符
      v5[v6] = v5[v6 + 16];   //逐个将v5的第v6个字符与第v6+16个字符交换位置
      v5[v6++ + 16] = v7;     //v6自增1
    }
    while ( v6 < strlen(v5) >> 1 );
  }

假设传入字符串为abcd,则上述代码执行完之后的v5为cdab

继续分析接下来的代码:

v8 = *v5;      //指针v8指向v5
  if ( *v5 )      //v5存在,执行花括号内的逻辑
  {   
    *v5 = v5[1];    
    v5[1] = v8;
    if ( strlen(v5) >= 3 ) //v5的长度大于等于3
    {
      v9 = 2;         //初始化v9=2
      do
      {
        v10 = v5[v9];   
        v5[v9] = v5[v9 + 1];
        v5[v9 + 1] = v10;
        v9 += 2;
      }
      while ( v9 < strlen(v5) );
    }
  }

这段代码很简单,就是两两交换。

根据上述我们直接手动得到flag的code:

1.将f72c5a36569418a20907b55be5bf95ad两两交换得到7fc2a5636549812a90705bb55efb59da

2.将7fc2a5636549812a90705bb55efb59da从中间砍断,头拼接到尾,得到90705bb55efb59da7fc2a5636549812a

3.加上flag{}就是flag。

easyjava

解题过程

    1、将该apk安装进夜神模拟器中,发现有一个输入框和一个按钮,随便输入信息,点击按钮,发现弹出信息You are wrong!Bye~

在这里插入图片描述

    2、将该APK拖进AndroidKiller中反编译,反编译完成后,搜索字符串You are wrong,发现该字符串位于MainActivity.java中,如下图所示:
在这里插入图片描述

    3、用jeb反编译该apk,反编译完成后直接将smali层代码转为java代码,发现在onCreate函数出现该字符串,发现将输入框中的字符串作为参数传入MainActivity的a(string)函数中,返回的布尔值确认flag是否正确。

在这里插入图片描述
    4、而该a函数调用了MainActivity的b函数,b函数返回一个布尔值,b函数作用为首先获取输入框的字符串截取前5个符flag{与最后一个字符}之间的字符串。

在这里插入图片描述

在这里插入图片描述

    然后实例化了一个b类,并且传入了一个参数2,而b类的构造函数首先为一个整形数组复制,然后左移该整形数组,左移的次数就是传进来的整数,这里是2。

在这里插入图片描述
    在然后实例化了一个a类,传入了一个参数3,而a类的构造函数与b类的构造函数基本相同,就不再说了。

在这里插入图片描述

    之后遍历截取下来的字符串的每个字符,然后将调用MainActivity中的a(string,b,a)函数返回回来的字符串添加到变量v3的尾部,遍历完后,将得到的字符串与字符串wigwrkaugala比较,若结果为真,返回True,否则返回False

在这里插入图片描述

    5、接着来看MainActivity的a(string,b,a)函数,该函数首先将传进来的第一个字符串作为调用b类的a函数的参数传入,然后将返回回来的结果作为调用a类的a函数的参数传入,最后返回一个字符。

在这里插入图片描述

    再来看b类的a函数,该函数首先获取传进来的字符在字符串b.b中的索引,然后得到在b类中定义的整形数组中与该该索引相等的在数组中的索引,然后调用b类的a()函数,该函数作用为将b类中数组与字符串左移一位,然后返回该数组索引。

在这里插入图片描述

在这里插入图片描述

    接着再来看a类中的a(int)函数,该函数首先获取与传进来的参数相等的数组中的值的索引,然后获取在字符串中索引为该数组索引的字符,最后返回该字符,当然,其中也调用a()函数,但是该函数要求等于25,所以该函数木有任何作用。

在这里插入图片描述

在这里插入图片描述

    6、上面几步以及详细讲述了该apk的flag加密流程,要获取flag,逆向解密即可,解密脚本跑出结果如下:
在这里插入图片描述


三、附件

解密脚本:(要求python版本3.6或者与上)

cipherText = 'wigwrkaugala'

aArray = [21,4,24,25,20,5,15,9,17,6,13,3,18,12,10,19,0,22,2,11,23,1,8,7,14,16]
aString = 'abcdefghijklmnopqrstuvwxyz'

bArray = [17,23,7,22,1,16,6,9,21,0,15,5,10,18,2,24,4,11,3,14,19,12,20,13,8,25]
bString = 'abcdefghijklmnopqrstuvwxyz'

def changeBArrayandString():
    global bString
    global bArray
    chArray = bArray[0]
    chString = bString[0:1]
    for i in range(len(bArray) - 1):
        bArray[i] = bArray[i + 1]
    bArray[len(bArray) - 1] = chArray
    bString = bString[1:]
    bString += chString

def getBchar(ch):
    v2 = bArray[ch]
    arg = bString[v2]
    changeBArrayandString()
    return arg

def getAint(ch):
    global aString
    global aArray
    v1 = aString.index(ch)
    arg5 = aArray[v1]
    return arg5
    
print('flag{',end='')
for k in cipherText:
    v0 = getAint(k)
    print(getBchar(v0),end='')
print('}')
原文地址:https://www.cnblogs.com/cainiao-chuanqi/p/13565030.html