猿人学APP逆向对抗前三题

第一题是java层加密,第二道是so加密,第三道是so混淆,经过分析前三道题sign生成算法不同,但是可以用相同的方法获取flag,这里以第一题为例:charles + postern抓包分析获取到两个接口。/time接口获取时间戳,/app1接口传入参数page,sign和前面获取的时间戳,返回数据就是对应page页面的所有数据。

题目需要计算所有0-100页面中所有值的和。有两个思路,一是主动调用app发送http请求的函数并hook接收返回数据的函数获取所有页面的值,二是分析sign的算法,然后主动调用sign算法生成sign值并构造http请求获取0-100页面的值,最后将值相加。

分析sign算法主动构造http请求

因为/app1接口会请求对应page页面的数据,jadx直接搜索接口字符串可以看到对应的接口函数。

查看什么位置调用了此接口函数。

来到接口函数调用处,很清楚的看到其将"page=" + page页面值 + 时间戳组合后调用sign函数生成sign值。

对应的sign函数貌似就是一个md4的哈希函数,接下来就可以通过主动调用此sign函数生成sign值并构造http请求了。


js中主动调用sign函数并导出,python利用rpc调用此导出函数生成sign并构造http请求即可获得所有页面的值,最后计算和为4902423

function callsign(string1){
    var sign_value;
    Java.perform(function(){
        sign_value = Java.use("com.yuanrenxue.match2022.security.Sign").$new().sign(stringToByte(string1));
    })
    return sign_value;
}

rpc.exports = {
    callsign: callsign,
};

hook app自身的收发包函数

另一种方法就是直接通过主动调用app的发包函数和hook收包函数来计算所有页面的值。查看反编译代码发现lambda$initListeners$3函数会先获取时间戳,然后调用lambda$initListeners$2获取页面数据,所以只要主动调用100次此函数就可以获取到所有页面的值。

lambda$initListeners$3ChallengeOneFragment类的成员函数,所以需要先在内存中找到ChallengeOneFragment类实例。

var ChallengeOne_instance;
Java.choose("com.yuanrenxue.match2022.fragment.challenge.ChallengeOneFragment", {
    onMatch:function(instance){
        ChallengeOne_instance = instance;
        console.log(ChallengeOne_instance);
    },onComplete:function(){}
})

lambda$initListeners$3还有一个参数是一个接口类,右键查看调用实例发现OooO0oO函数会调用lambda$initListeners$3并传递接口类作为参数。

紧接着右键查看是哪个函数调用的OooO0oO,可以看到有两个位置会调用此函数并且传入的参数都是com.scwang.smartrefresh.layout.SmartRefreshLayout类实例。

最后就得到lambda$initListeners$3函数的参数是com.scwang.smartrefresh.layout.SmartRefreshLayout类实例,所以需要先在内存中找到此实例。

var Smart_instance;
Java.choose("com.scwang.smartrefresh.layout.SmartRefreshLayout",{
    onMatch:function(instance){
        Smart_instance = instance;
        console.log(Smart_instance);
    },onComplete:function(){

    } 
})

现在就可以主动调用lambda$initListeners$3函数请求0-100所有页面的数据了。

for(var i = 0; i < 100; i++){
    ChallengeOne_instance.page.value = i;
    ChallengeOne_instance.lambda$initListeners$3(Smart_instance);
    Thread.sleep(1)
}

还需要hook接收数据包的函数,进一步获取所有请求的返回值并计算所有页面之和。再次查看lambda$initListeners$3函数发现其最后会调用subscribe函数并new了一个对象。

查看此对象对应的类发现了onNext函数,此函数的参数是一个C3751OooO00o(名称反混淆过了)类对象。

查看此类对象发现其成员包含了返回数据包中的state, data。而data是一个类对象列表,列表中对象的成员变量包含了页面的value值。

所以通过hook onNext函数并调用C3751OooO00o类的成员函数即可获得此次请求返回的页面value值,onNext类是内部类com.yuanrenxue.match2022.fragment.challenge.ChallengeOneFragment$OooO0O0的成员函数。这里还要特别注意onNext函数的参数是java.lang.Object类型,需要先利用Java.cast强制转换为对应的类后才能调用对应的成员函数和获取其成员变量。

var sum = 0;
Java.use("com.yuanrenxue.match2022.fragment.challenge.ChallengeOneFragment$OooO0O0").onNext.overload('java.lang.Object').implementation = function(arg1){
    var class1 = Java.cast(arg1, Java.use("o00O000.OooO00o"));
    var class2_array = class1.OooO00o().toArray();
    for(var i = 0; i < class2_array.length; i++){
        var class2 = Java.cast(class2_array[i], Java.use("o00O000.OooO0OO"));
        var value_string = class2.OooO00o();    
        sum = sum + int64(value_string);
    }
}

最后通过主动调用app发包函数,并hook收包函数获取所有页面的value之和为4902423

完整的脚本如下:

function main(){
    Java.perform(function(){
       Java.use("com.yuanrenxue.match2022.security.Sign").sign.overload('[B').implementation = function(arg1){
            var arg1_string = byteToString(arg1);
            console.log("\narg1 :", arg1_string);
            console.log("sign_value is :", this.sign(arg1));
            return this.sign(arg1);
       }

        var ChallengeOne_instance;
        Java.choose("com.yuanrenxue.match2022.fragment.challenge.ChallengeOneFragment", {
            onMatch:function(instance){
                ChallengeOne_instance = instance;
                console.log(ChallengeOne_instance);
            },onComplete:function(){}
        })

        var Smart_instance;
        Java.choose("com.scwang.smartrefresh.layout.SmartRefreshLayout",{
            onMatch:function(instance){
                Smart_instance = instance;
                console.log(Smart_instance);
            },onComplete:function(){

            } 
        })

        var sum = 0;
        Java.use("com.yuanrenxue.match2022.fragment.challenge.ChallengeOneFragment$OooO0O0").onNext.overload('java.lang.Object').implementation = function(arg1){
            var class1 = Java.cast(arg1, Java.use("o00O000.OooO00o"));
            var class2_array = class1.OooO00o().toArray();
            for(var i = 0; i < class2_array.length; i++){
                var class2 = Java.cast(class2_array[i], Java.use("o00O000.OooO0OO"));
                var value_string = class2.OooO00o();    
                sum = sum + int64(value_string);
            }
        }
       
        for(var i = 0; i < 100; i++){
            ChallengeOne_instance.page.value = i;
            ChallengeOne_instance.lambda$initListeners$3(Smart_instance);
            Thread.sleep(1)
        }
        console.log("sum is ", sum);
    })
}
setImmediate(main)

参考:https://www.jianshu.com/p/a36a51166fb2

posted @ 2023-03-14 02:41  怎么可以吃突突  阅读(260)  评论(0编辑  收藏  举报