soul cs字段unidbg實(shí)現(xiàn)

soul cs字段unidbg實(shí)現(xiàn)

環(huán)境

app 3.83.0

Java

image-20220120150049788

jadx搜索

image-20220117164321104

查找用例

image-20220117164420865

cn.soulapp.android.net.q.j.b

image-20220117164450987

cn.soulapp.android.net.SoulNetworkSDK.g

image-20220117164538640

cn.soulapp.android.soulpower.SoulPowerful.i

image-20220117164704641

cn.soulapp.android.soulpower.SoulPowerful.h

image-20220117165143223

hook 看看

android hooking watch class_method cn.soulapp .android.soulpower.SoulPowerful.h --dump-args --dump-return
image-20220120141036784

可以看到再愈,第二個(gè)參數(shù)是時(shí)間戳寿谴,第三個(gè)參數(shù)url经柴,第四個(gè)參數(shù)是headers里面的參數(shù)拼接起來(lái)的昆咽。這個(gè)的簽名長(zhǎng)度是36家妆,是一個(gè)比較不常見(jiàn)的數(shù)字钠龙,對(duì)比了幾個(gè)簽名蝗拿,發(fā)現(xiàn)都是028f**77ac的形式(實(shí)際上口渔,在另一臺(tái)手機(jī)中样屠,cs是02af**008c的形式),接下來(lái)研究一下cs的具體構(gòu)成缺脉。

通過(guò)frida痪欲,我們自己控制傳入的參數(shù),看看輸出是怎樣的枪向。

// hook_soul.js
function callh(ts, url, header) {
    var soul = Java.use("cn.soulapp.android.soulpower.SoulPowerful");
    var currentApplication = Java.use("android.app.ActivityThread").currentApplication();
    var context = currentApplication.getApplicationContext();
    var ret = soul.h(context, ts, url, header);
    // console.log("h-ret:", ret);
    return ret;
}

rpc.exports = {
    callh: callh
}
# hook_soul.py
import frida

def read_js():
    with open(r"soul\hook_soul.js", 'r') as fp:
        return fp.read()

def on_message(message, data):
    pass

if __name__ == '__main__':
    device = frida.get_usb_device()
    pid = device.get_frontmost_application().pid
    session = device.attach(pid)
    js = read_js()
    script = session.create_script(js)
    script.on('message', on_message)
    script.load()

    url = 'hello'
    header = 'everhu'
    for ts in [0x000000ff, 0x0000ff00, 0x00ff0000, 0x01234567, 0x10325476, 0x11111111]:
        cs = script.exports.callh(int(ts), url, header)
        print(f'0x{ts:08x}', url, header, cs)
    
    ts = 0x11111111
    header = 'everhu'
    for c in 'abcdef':
        url = c * 5
        cs = script.exports.callh(ts, url, header)
        print(f'0x{ts:08x}', url, header, cs)

    ts = 0x11111111
    url = 'hello'
    for c in 'abcdef':
        header = c * 6
        cs = script.exports.callh(ts, url, header)
        print(f'0x{ts:08x}', url, header, cs)
ts url header cs
0x000000ff hello everhu 028f 0b 00 5a f0 33 00 27 f0 77 6U 601d8960 77ac
0x0000ff00 hello everhu 028f 0b 00 5a 0f 33 f0 27 00 77 6U dee8bd70 77ac
0x00ff0000 hello everhu 028f 0b f0 5a 00 33 00 27 0f 77 6U 7cbbaebc 77ac
0x01234567 hello everhu 028f 0b 31 5a 65 33 40 27 72 77 6U 12a566c1 77ac
0x10325476 hello everhu 028f 0b 20 5a 74 33 51 27 63 77 6U 8446767b 77ac
0x11111111 hello everhu 028f 0b 11 5a 11 33 11 27 11 77 6U 0b64c40a 77ac
0x11111111 aaaaa everhu 028f 0b 11 5a 11 33 11 27 11 77 6U a2a9352f 77ac
0x11111111 bbbbb everhu 028f 0b 11 5a 11 33 11 27 11 77 6U 776491c4 77ac
0x11111111 ccccc everhu 028f 0b 11 5a 11 33 11 27 11 77 6U 817b3dbe 77ac
0x11111111 ddddd everhu 028f 0b 11 5a 11 33 11 27 11 77 6U 247ea924 77ac
0x11111111 eeeee everhu 028f 0b 11 5a 11 33 11 27 11 77 6U d4f29b3c 77ac
0x11111111 fffff everhu 028f 0b 11 5a 11 33 11 27 11 77 6U cc70a6c4 77ac
0x11111111 hello aaaaaa 028f 3f 11 be 11 86 11 60 11 77 6U 0b64c40a 77ac
0x11111111 hello bbbbbb 028f d8 11 d7 11 80 11 24 11 77 6U 0b64c40a 77ac
0x11111111 hello cccccc 028f 1c 11 a9 11 6b 11 b3 11 77 6U 0b64c40a 77ac
0x11111111 hello dddddd 028f 6a 11 f5 11 99 11 73 11 77 6U 0b64c40a 77ac
0x11111111 hello eeeeee 028f 0b 11 71 11 f2 11 cd 11 77 6U 0b64c40a 77ac
0x11111111 hello ffffff 028f ef 11 fc 11 58 11 63 11 77 6U 0b64c40a 77ac

記錄如上表所示勤揩。

image-20220120145028155

在第一組中,當(dāng)時(shí)間戳為0x111111110x01234567時(shí)秘蛔,我們可以清晰地分辨出哪些位置是由時(shí)間戳構(gòu)成的陨亡,是由時(shí)間戳的哪一位構(gòu)成的∩钤保可以看出6-7, 10-11, 14-15, 18-19位都是由時(shí)間戳成的负蠕。此外,當(dāng)時(shí)間戳變化時(shí)倦畅,第24-31位也在變化遮糖,說(shuō)明此處有時(shí)間戳參與計(jì)算。

在第二組中叠赐,時(shí)間戳和header都不變欲账,當(dāng)url變化時(shí),第24-31位也在變化芭概,說(shuō)明此處有url參與計(jì)算赛不,結(jié)合第一組得出,第24-31位有時(shí)間戳和url一起參與計(jì)算罢洲。

在第三組中踢故,時(shí)間戳和url都不變,當(dāng)header變化時(shí)惹苗,cs的4-5, 8-9, 12-13, 16-17都在變化殿较,說(shuō)明這些位置是由header計(jì)算得出。

其余位置無(wú)變化桩蓉,可能與app版本或者手機(jī)環(huán)境本身相關(guān)淋纲,也有可能直接就是固定的。

unidbg實(shí)現(xiàn)

接下來(lái)是用unidbg來(lái)調(diào)用生成cs院究,先搭個(gè)框架帚戳。

public class Soul extends AbstractJni {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;

    public static String pkgName = "cn.soulapp.android";
    public static String apkPath = "unidbg-android/src/test/java/com/soul/soul3830.apk";
    public static String soPath = "";

    public Soul() {
        emulator = AndroidEmulatorBuilder.for32Bit().setProcessName(pkgName).build();
        Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File(apkPath));
        vm.setJni(this);
        vm.setVerbose(true);
        DalvikModule dm = vm.loadLibrary("soulpower", true);
        module = dm.getModule();
        dm.callJNI_OnLoad(emulator);
    }

    public void call_h() {
        List<Object> list = new ArrayList<>(10);
        list.add(vm.getJNIEnv());
        list.add(0);
        list.add(vm.addLocalObject(vm.resolveClass("android/content/Context").newObject(null)));
        list.add(0x01234567);
        String url = "hello";
        list.add(vm.addLocalObject(new StringObject(vm, url)));
        String header = "everhu";
        list.add(vm.addLocalObject(new StringObject(vm, header)));

        Number ret = module.callFunction(emulator, 0xaa73c, list.toArray());
        System.out.println("ret h: " + vm.getObject(ret.intValue()).getValue().toString());
    }

    public static void main(String[] args) {
        Soul test = new Soul();
        System.out.println("=== Start call h");
        test.call_h();
    }
}

然后就是報(bào)錯(cuò)+補(bǔ)環(huán)境

image-20220120150519066
@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
    switch (signature) {
        case "android/os/Build$VERSION->RELEASE:Ljava/lang/String;": {
            return new StringObject(vm, "7.1.2");
        }
    }
    return super.getStaticObjectField(vm, dvmClass, signature);
}
image-20220120150651113
// wrong case
case "android/os/Build$VERSION->SDK:Ljava/lang/String;": {
    return new StringObject(vm, "23");
}

然后出現(xiàn)了讓我無(wú)法理解的情況玷或。

image-20220120150848225

這個(gè)報(bào)錯(cuò)讓我不知道怎么補(bǔ)環(huán)境了。片任。開(kāi)啟全部日志之后,也不知道怎么找到問(wèn)題蔬胯。最終是一通亂拳对供,把"23"改成不存在的SDK版本(如"ff")才得以繼續(xù),這讓我很不理解氛濒,有誰(shuí)知道什么原因可以分享一下产场。

case "android/os/Build$VERSION->SDK:Ljava/lang/String;": {
    return new StringObject(vm, "ff");
}
image-20220120151517512

這個(gè)需要返回一個(gè)byte,那具體要返回多少呢舞竿?可以使用objection和hluwa/Wallbreaker來(lái)獲取

clone下來(lái)后京景,啟動(dòng)objection,然后加載插件

plugin load E:\Wallbreaker

然后dump整個(gè)類的實(shí)例

plugin wallbreaker classdump cn.soulapp.andro id.soulpower.InfoGather
image-20220120161513916

所以應(yīng)該返回119(實(shí)際上骗奖,隨便一個(gè)值也能返回結(jié)果)确徙。

也可以查看android.os.Build $VERSION

plugin wallbreaker classdump android.os.Build$VERSION
image-20220120161717699
@Override
public byte getStaticByteField(BaseVM vm, DvmClass dvmClass, String signature) {
    switch (signature) {
        case "cn/soulapp/android/soulpower/InfoGather->aa:B": {
            return (byte) 119;
        }
    }
    return super.getStaticByteField(vm, dvmClass, signature);
}
image-20220120155318862

注意0x77 = 119

結(jié)果出來(lái)了,但是和hook到的結(jié)果不完全一樣执桌。幸運(yùn)的是鄙皇,不一樣的部分是之前觀察到的不變的部分,可能由于補(bǔ)的環(huán)境和正常環(huán)境不一樣仰挣,所以這些部分和正常的不一樣伴逸。不過(guò)和簽名相關(guān)的部分是一樣的,這樣的話膘壶,我們只需要把不一樣的部分改一下错蝴,應(yīng)該就能用了?

完整實(shí)現(xiàn)

package com.soul;

import com.github.unidbg.AndroidEmulator;
import com.github.unidbg.Module;
import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;
import com.github.unidbg.memory.Memory;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

public class Soul extends AbstractJni {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;

    public static String pkgName = "cn.soulapp.android";
    public static String apkPath = "unidbg-android/src/test/java/com/soul/soul3830.apk";
    public static String soPath = "";

    public Soul() {
        emulator = AndroidEmulatorBuilder.for32Bit().setProcessName(pkgName).build();
        Memory memory = emulator.getMemory();
        memory.setLibraryResolver(new AndroidResolver(23));
        vm = emulator.createDalvikVM(new File(apkPath));
        vm.setJni(this);
        vm.setVerbose(true);
        DalvikModule dm = vm.loadLibrary("soulpower", true);
        module = dm.getModule();
        dm.callJNI_OnLoad(emulator);
    }

    @Override
    public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
        switch (signature) {
            case "android/os/Build$VERSION->RELEASE:Ljava/lang/String;": {
                return new StringObject(vm, "7.1.2");
            }
            case "android/os/Build$VERSION->SDK:Ljava/lang/String;": {
                return new StringObject(vm, "ff");
            }
        }
        return super.getStaticObjectField(vm, dvmClass, signature);
    }

    @Override
    public byte getStaticByteField(BaseVM vm, DvmClass dvmClass, String signature) {
        switch (signature) {
            case "cn/soulapp/android/soulpower/InfoGather->aa:B": {
                return (byte) 119;
            }
        }
        return super.getStaticByteField(vm, dvmClass, signature);
    }

    public void call_h() {
        List<Object> list = new ArrayList<>(10);
        list.add(vm.getJNIEnv());
        list.add(0);
        list.add(vm.addLocalObject(vm.resolveClass("android/content/Context").newObject(null)));
        list.add(0x01234567);
        String url = "hello";
        list.add(vm.addLocalObject(new StringObject(vm, url)));
        String header = "everhu";
        list.add(vm.addLocalObject(new StringObject(vm, header)));

        Number ret = module.callFunction(emulator, 0xaa73c, list.toArray());
        System.out.println("ret h: " + vm.getObject(ret.intValue()).getValue().toString());
    }

    public static void main(String[] args) {
        Soul test = new Soul();
        System.out.println("=== Start call h");
        test.call_h();
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末颓芭,一起剝皮案震驚了整個(gè)濱河市顷锰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌畜伐,老刑警劉巖馍惹,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異玛界,居然都是意外死亡万矾,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門慎框,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)良狈,“玉大人,你說(shuō)我怎么就攤上這事笨枯⌒蕉。” “怎么了遇西?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)严嗜。 經(jīng)常有香客問(wèn)我粱檀,道長(zhǎng),這世上最難降的妖魔是什么漫玄? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任茄蚯,我火速辦了婚禮,結(jié)果婚禮上睦优,老公的妹妹穿的比我還像新娘渗常。我一直安慰自己,他們只是感情好汗盘,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開(kāi)白布皱碘。 她就那樣靜靜地躺著,像睡著了一般隐孽。 火紅的嫁衣襯著肌膚如雪癌椿。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,730評(píng)論 1 289
  • 那天缓醋,我揣著相機(jī)與錄音如失,去河邊找鬼。 笑死送粱,一個(gè)胖子當(dāng)著我的面吹牛褪贵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播抗俄,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼脆丁,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了动雹?” 一聲冷哼從身側(cè)響起槽卫,我...
    開(kāi)封第一講書(shū)人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎胰蝠,沒(méi)想到半個(gè)月后歼培,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡茸塞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年躲庄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片钾虐。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡噪窘,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出效扫,到底是詐尸還是另有隱情倔监,我是刑警寧澤直砂,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站浩习,受9級(jí)特大地震影響静暂,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜瘦锹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一籍嘹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧弯院,春花似錦、人聲如沸泪掀。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)异赫。三九已至椅挣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間塔拳,已是汗流浹背鼠证。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留靠抑,地道東北人量九。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像颂碧,于是被迫代替她去往敵國(guó)和親荠列。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容