吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 2875|回复: 2
收起左侧

[Android 原创] Ph0ne1x-100的解题感想

  [复制链接]
西班牙三棵树 发表于 2021-7-21 10:58

最近在做XCTF新手区的题目,发现了这个Ph0ne1x-100这道题十分的有意思,这道题的难度不高,但是十分适合新手,它的解题思路多种多样,不仅可以动态分析,还可以静态分析,包括Hook,改源码等技巧。
经过最近的研究,我通过这道题也研究出五种解法,其中有一些方法的思想相同,只是角度和难度不同,希望可以给大家提供一些思路。

  • 老规矩,安装上去看一下,有一个输入flag的窗口,登录显示Fail:

image-20210719092434291.png

  • 再扔到JEB中打开,这个APK有SO库,主要类就是MainActivity,BuildConfig里面Debug为false,MainActvity中加载了库phcm,接下来是两个类库中的关键函数encrypt和getFlag,MainActvity中有一个getSecret函数通过调用那两个native函数完成加密:

image-20210719093002356.png

  • 分析一下MainActivity中的getSecret函数,这个函数参与运算的外来参数只有一个传入参数,这就意味着如果我们在放入getsecurity之前获得了flag,不经过getsecurity也能直接比较,那么这道题的主要逻辑就是:

    内置的字符串和函数通过setflag加工,生成加密密文。输入的flag通过encrypt进行加密生成密文,最后这两个密文是一致的,所以接下来我们就要先通过setflag进行加工,生成密文,再逆向encrpt加密生成flag。

image-20210719095006983.png
image-20210719095031256.png

  • 接下来看so库的文件:

    • getFlag如下,主要是getFlag没有输入,这就意味着这里面使用的是内置的参数进行的加密,看起来挺简单的,但是里面有一个unk_2784并没有被成功解析出来,但是v10又被用于接下来的计算,unk_2784内存处是一些无法理解的东西,这就导致了静态分析陷入了僵局:

image-20210719170917620.png
image-20210719171118796.png

  • 静态分析失败,接下来转为动态,既然v10进行复制的是内存的一段,我们就在动态运行时截取下来getflag的返回值这一段进行读取,再放入encrypt进行逆向分析,解析出需要我们输入的Flag

动态分析1,借用maketext输出:

  • 先找到MainActivity中调用getFlag的函数:

image-20210719161858437.png

  • 拷贝到下方输出Failed的位置,覆盖掉原有的Failed。

image-20210719161952978.png

  • 重新打包,运行,出现中间值:

image-20210719162101388.png

  • encrypt如下,主要就是按位减一:

image-20210719162522672.png

  • 编写程序按位加1得到源程序:

``c++ string ciphertext = "ekfz@q2^x/t^fn0mF^6/^rbqanqntfg^Ehq|";
for(int x=0; x < ciphertext.length(); x++){
ciphertext[x] = ciphertext[x] + 1;
}
cout<<ciphertext;

输出:



image-20210719171526944.png




动态分析二:

使用AndroidStudio进行分析:

- 创建一个remote,对APK拆包出来的APK中的smali文件夹导入到项目中,并设置成resourceroot




image-20210720091929040.png




- 接下来在虚拟机上运行smali文件,并attach到进程:




image-20210720092032173.png




- 将断点打在MainActivity的getFlag之后,传入getsecret函数之后,单步跳入查看传入getsecret函数的string值:




image-20210720092220602.png






image-20210720092305039.png




动态分析三:

使用Frida框架对So库getFlag()进行So层Hook操作:

Hook代码:

```python
import frida
import sys

jscode = """
Java.perform(function(){
    Interceptor.attach(Module.findExportByName("libphcm.so","Java_com_ph0en1x_android_1crackme_MainActivity_getFlag"),{
        onEnter: function(args) {
        },
        onLeave: function(retval){
            var String_java = Java.use('java.lang.String');
            var args_4 = Java.cast(retval, String_java);
            send("getFlag()输出信息为:"+args_4);
        }
    });
});
"""
def printMessage(message,data):
    if message['type'] == 'send':
        print(' {0}'.format(message['payload']))
    else:
        print(message)

process = frida.get_usb_device().attach('Android_crackme')
script = process.create_script(jscode)
script.on('message',printMessage)
script.load()
sys.stdin.read()

输出结果:

image-20210720154731342.png

静态分析补充:

  • 刚刚遇到了无法解决的unk_2748,经过动态分析我们知道了这个每次都是不变的,那么我们可以直接将这40个按int型抄下来,再转化成char返回给v10,代码如下:

```c++

include<iostream>

using namespace std;

include<stdlib.h>

int main(){

int v2; // r0
int v3; // r3
char *v4; // ST04_4
char v5; // ST08_1
int v6; // r1
int v8; // [sp+0h] [bp-68h]
char v9[16];
char v10[40];
int v11[40] = {0x2E, 0x36, 0x42, 0x4C, 0x5F, 0xBF, 0xE0, 0x3A, 0xA8, 0xC3, 0x20, 0x63, 
0x89, 0xB7, 0xC0, 0x1C, 0x1D, 0x44, 0xC2, 0x28, 0x7F, 0xED, 0x02, 0x0E, 0x5D, 0x66, 0x8F, 0x98,
0xB5, 0xB7, 0xD0, 0x16, 0x4D, 0x83, 0xF8, 0xFB, 0x01, 0x43, 0x47, 0x00};
strcpy(v9,"Hello Ph0en1x");
//memcpy(v10, &v11, 40);
//memcpy(v10, &v9, 40);

for(int x=0; x<=40;x++){
    v10[x] = v11[x];
}
//cout<<strlen(v10);
v8 = strlen(v9);
v2 = strlen(v10) - 1;

 while ( v2 > 0 )
{
    v3 = ((unsigned __int8)v10[v2]++ + 1) & 0xFF;
    v4 = &v10[v2 - 1];
    v5 = v3 - v10[v2 - 1];
    v6 = v2-- % v8;
    v4[1] = (v9[v6] ^ v5) - 1;
}
v10[0] = (v10[0] ^ 0x48) - 1;//至此获得了用于比较的字符数组v10,长度为40

//接下来是反向处理
string str(v10);
//cout<<str;

string ciphertext = "ek`fz@q2^x/t^fn0mF^6/^rb`qanqntfg^E`hq|";
for(int x=0; x < ciphertext.length(); x++){
    ciphertext[x] = ciphertext[x] + 1;
}
cout<<ciphertext;

}


动态分析4:

使用Frida对Java层函数getSecret()函数进行Hook操作:

```python
import frida
import sys

jscode = """
if(Java.available){
    Java.perform(function(){
        var MainActivity = Java.use("com.ph0en1x.android_crackme.MainActivity");
        //找到getSecret函数,将输入参数str返回即可
        MainActivity.getSecret.implementation=function(str){
            send("getFlag()输出信息为:"+str);
            return str;    
        }
    });

}
"""
def printMessage(message,data):
    if message['type'] == 'send':
        print(' {0}'.format(message['payload']))
    else:
        print(message)

process = frida.get_usb_device().attach('Android_crackme')
script = process.create_script(jscode)
script.on('message',printMessage)
script.load()
sys.stdin.read()

image-20210720172959421.png

FLAG:flag{Ar3_y0u_go1nG_70_scarborough_Fair}

image-20210719104358651.png
image-20210719171125645.png
image-20210719171459120.png

免费评分

参与人数 7吾爱币 +6 热心值 +7 收起 理由
朝太阳奔跑 + 1 + 1 我很赞同!
AG6 + 1 + 1 谢谢@Thanks!
jokermadister + 1 + 1 谢谢
xqaipojie + 1 + 1 我很赞同!
52pojie666z + 1 + 1 用心讨论,共获提升!
dddpppyyy + 1 用心讨论,共获提升!
Yang09 + 1 + 1 谢谢@Thanks!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 西班牙三棵树 发表于 2021-7-21 11:03
Markdown粘贴的,格式全乱了。。。
朝太阳奔跑 发表于 2021-7-21 18:35
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-28 10:01

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表