qimao小說sign字段逆向及unidbg實現(xiàn)

qimao小說sign字段逆向及unidbg實現(xiàn)

Java層

image-20220123212829789

apk是加固的,這次脫殼使用的是frida_dump

frida -U --no-pause -f com.kmxs.reader -l dump_dex.js

然后再重新打包

import pathlib
import zipfile


def get_files(dex_dir):
    fdir = pathlib.Path(dex_dir)
    infos = {}
    for item in fdir.glob('*.dex'):
        size = item.stat().st_size
        infos[size] = item
    
    fdict = {}
    for idx, key in enumerate(sorted(infos, reverse=True)):
        name = 'classes{}.dex'.format(str(idx) if idx else '')
        fdict[name] = infos[key]

    return fdict

def pack(apk_path, dex_dir):
    dst = apk_path + '.pack.apk'
    with zipfile.ZipFile(apk_path) as zf, zipfile.ZipFile(dst, 'w') as zout:
        for item in zf.infolist():
            if item.filename.startswith('classes') and item.filename.endswith('.dex'):
                print('Ignore:', item.filename)
            else:
                buffer = zf.read(item.filename)
                zout.writestr(item, buffer)

        for filename, fpath in get_files(dex_dir).items():
            print('Add:', fpath)
            zinfo = zipfile.ZipInfo(filename)
            with open(fpath, 'rb') as fin:
                zout.writestr(zinfo, fin.read())

if __name__ == '__main__':
    pack(r"E:\workspace\qimao\qimao613.apk", r"E:\workspace\qimao\dump_dex_com.kmxs.reader")

然后jadx打開搜索"sign"

image-20220122235915472

可以看到url和header里面的sign都是調(diào)用同一個加密函數(shù)。

com.km.repository.net.config.interceptor.HeaderInterceptor.b

image-20220123000109559

com.qimao.qmsdk.tools.encryption.Encryption.sign

image-20220123000150079

com.km.encryption.api.Security.sign

image-20220123000236135

先hook看看

android hooking watch class_method com.km.encryption.api.Security.sign --dump-args --dump-return
image-20220123140659715
image-20220123140752489

雖然找到了native函數(shù)甫煞,但是看不出是在哪個so里面注冊的鱼的。

a函數(shù)查找用例

image-20220123141149646

com.qimao.qmsdk.tools.encryption.Encryption.init

image-20220123141229042

init函數(shù)查找用例

image-20220123141321652

defpackage.qf.run

image-20220123141406108

看來就是在libcommon-encryption.so注冊的慈鸠。

so層

由于手機(jī)和app都支持64位指令逝段,所以分析的是64位so

函數(shù)窗口搜索java

image-20220123002352558

有點奇怪私股,其他幾個函數(shù)都有了刷允,唯獨少了sign函數(shù)冤留。每個都點進(jìn)去看看

image-20220123003710978

Java_com_km_encryption_api_Security_token這個函數(shù)里看到了Java_com_km_encryption_api_Security_sign,難道它們是同一個函數(shù)恃锉?看看這個函數(shù)

image-20220123003830781

從它的實現(xiàn)來看搀菩,就是在Java層的輸入后面加了個keyData,然后做個MD5破托,這很大概率就是sign函數(shù)肪跋,因為簽名就是32位長度的。

看看MessageDigestAlgorithm::MessageDigestAlgorithm函數(shù)

image-20220123004312636

看看MessageDigestAlgorithm::init

image-20220123004426099

hook一下MessageDigestAlgorithm::init函數(shù)

function dump(name, addr, length) {
    console.log("======================== " + name + " ============");
    console.log(hexdump(addr, {length:length||32}));
}

function hook_key(){
    var bptr = Module.findBaseAddress("libcommon-encryption.so");
    Interceptor.attach(bptr.add(0x19394), {
        onEnter: function(args) {
            console.log(args[0], args[1], args[2]);
            dump("input", args[1], parseInt(args[2]));
        },
        onLeave: function(retval) {
        }
    })
}
image-20220123142052258

只有第一次的是輸入土砂,其他的是算法的填充州既。cyberchef上驗證一下是不是標(biāo)準(zhǔn)MD5

image-20220123142115586

沒有問題,是對的萝映。

header的sign調(diào)用的也是這個函數(shù)吴叶,只是輸入不一樣。

unidbg實現(xiàn)

習(xí)慣性選擇調(diào)用32位的so序臂,按照慣例蚌卤,搭個框架

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

    public static String pkgName = "com.kmxs.reader";
    public static String apkPath = "unidbg-android/src/test/java/com/qimao/qimao613.apk";
    public static String soPath = "";

    public Qimao() {
        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("common-encryption", true);
        module = dm.getModule();
        dm.callJNI_OnLoad(emulator);
    }
    
    public static void main(String[] args) {
        Qimao test = new Qimao();
    }
}

然后就是報錯和補環(huán)境

image-20220123194410678
@Override
public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
    switch (signature) {
        case "java/lang/Class->getClassLoader()Ljava/lang/ClassLoader;": {
            return new ClassLoader(vm, signature);
        }
    }
    return super.callObjectMethodV(vm, dvmObject, signature, vaList);
}
image-20220123195042845

開始正式調(diào)用

public void call_sign() {
    DvmClass clz = vm.resolveClass("com/km/encryption/api/Security");
    String methodSign = "sign([B)Ljava/lang/String;";
    StringObject ret = clz.callStaticJniMethodObject(emulator, methodSign, new ByteArray(vm, "book_privacy=1cache_ver=1642759975gender=2read_preference=2tab_type=2".getBytes(StandardCharsets.UTF_8)));
}
public static void main(String[] args) {
    Qimao test = new Qimao();
    test.call_sign();
}
image-20220123214354010

日志里可以看到sign函數(shù)的地址,跳轉(zhuǎn)過去也是Java_com_km_encryption_api_Security_token這個函數(shù)

image-20220123214536950

繼續(xù)補環(huán)境

@Override
public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
    switch (signature) {
        case "com/km/encryption/generator/KeyGenerator->assetManager:Landroid/content/res/AssetManager;": {
            return new AssetManager(vm, signature);
        }
    }
    return super.getStaticObjectField(vm, dvmClass, signature);
}
image-20220123195727498

報錯了奥秆,但看不出什么逊彭。不過如果對AssetManager在native層的實現(xiàn)有所了解的話,就知道它是通過libandroid.so實現(xiàn)的构订∥甓#可惜的是unidbg并沒有實現(xiàn)這個so,不過它提供了一個Android VirtualModule悼瘾,實現(xiàn)了libandroid.so中的幾個函數(shù)囊榜。

image-20220123202647299

從打印的日志也可以看到审胸,libcommon-encryption.so依賴了libandroid.so

public Qimao() {
    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);
    new AndroidModule(emulator, vm).register(memory);  // Load AndroidModule
    DalvikModule dm = vm.loadLibrary("common-encryption", true);
    module = dm.getModule();
    dm.callJNI_OnLoad(emulator);
}

再次運行

image-20220123203430887

還是報錯了卸勺,這次跳轉(zhuǎn)到0xfd29看看砂沛。

image-20220123203814508

可以看到調(diào)用了幾個AAsset_*函數(shù),可惜的是目前unidbg并沒有實現(xiàn)其中的AAsset_seek函數(shù)

image-20220123204048124

所以需要自己實現(xiàn)一下孔庭,在unidbg-android/src/main/java/com/github/unidbg/virtualmodule/android/AndroidModule.java添加代碼

@Override
protected void onInitialize(Emulator<?> emulator, final VM vm, Map<String, UnidbgPointer> symbols) {
    // ..
    symbols.put("AAsset_seek", svcMemory.registerSvc(is64Bit ? new Arm64Svc() {
        @Override
        public long handle(Emulator<?> emulator) {
            return seek(emulator, vm);
        }
    } : new ArmSvc() {
        @Override
        public long handle(Emulator<?> emulator) {
            return seek(emulator, vm);
        }
    }));
}

private static int seek(Emulator<?> emulator, VM vm) {
    RegisterContext context = emulator.getContext();
    UnidbgPointer pointer = context.getPointerArg(0);
    int offset = context.getIntArg(1);
    int whence = context.getIntArg(2);
    if (log.isDebugEnabled()) {
        log.debug("AAset_seek pointer=" + pointer + ", offset=" + offset + ", whence=" + whence + ", LR=" + context.getLRPointer());
    }
    final int SEEK_SET = 0;
    final int SEEK_CUR = 1;
    final int SEEK_END = 2;
    if ((whence == SEEK_SET && offset >= 0) || whence == SEEK_CUR || whence == SEEK_END) {
        Asset asset = vm.getObject(pointer.toIntPeer());
        return asset.seek(offset, whence);
    }
    throw new BackendException("offset=" + offset + ", whence=" + whence + ", LR=" + context.getLRPointer());
}

unidbg-android/src/main/java/com/github/unidbg/linux/android/dvm/api/Asset.java添加代碼

public int seek(int offset, int whence) {
    Pointer pointer = memoryBlock.getPointer();
    int index = pointer.getInt(0);
    int length = pointer.getInt(4);

    final int SEEK_SET = 0;
    final int SEEK_CUR = 1;
    final int SEEK_END = 2;

    if (whence == SEEK_SET) {
        index = offset;
    }
    else if (whence == SEEK_CUR) {
        index = index + offset;
    }
    else if (whence == SEEK_END) {
        index = length + offset;
    }
    pointer.setInt(0, index);
    return index;
}

重新運行

image-20220123205804729

恢復(fù)正常的報錯了尺上,getKey()需要返回一個字符串,jadx看看這個類圆到。

image-20220123205952471

可以看到是返回成員變量key怎抛,可以使用objection + Wallbreaker查看

plugin wallbreaker classdump com.km.encryption.generator.KeyGenerator
image-20220123210219431

所以返回"8w1"

@Override
public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
    switch (signature) {
        case "com/km/encryption/generator/KeyGenerator->getKey()Ljava/lang/String;": {
            return new StringObject(vm, "8w1");
        }
    }
    return super.callStaticObjectMethodV(vm, dvmClass,  signature, vaList);
}
image-20220123210350433

和抓包結(jié)果一樣。

完整代碼

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

    public static String pkgName = "com.kmxs.reader";
    public static String apkPath = "unidbg-android/src/test/java/com/qimao/qimao613.apk";
    public static String soPath = "";

    public Qimao() {
        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);
        new AndroidModule(emulator, vm).register(memory);
        DalvikModule dm = vm.loadLibrary("common-encryption", true);
        module = dm.getModule();
        dm.callJNI_OnLoad(emulator);
    }

    @Override
    public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
        switch (signature) {
            case "java/lang/Class->getClassLoader()Ljava/lang/ClassLoader;": {
                return new ClassLoader(vm, signature);
            }
        }
        return super.callObjectMethodV(vm, dvmObject, signature, vaList);
    }

    @Override
    public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
        switch (signature) {
            case "com/km/encryption/generator/KeyGenerator->assetManager:Landroid/content/res/AssetManager;": {
                return new AssetManager(vm, signature);
            }
        }
        return super.getStaticObjectField(vm, dvmClass, signature);
    }

    @Override
    public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
        switch (signature) {
            case "com/km/encryption/generator/KeyGenerator->getKey()Ljava/lang/String;": {
                return new StringObject(vm, "8w1");
            }
        }
        return super.callStaticObjectMethodV(vm, dvmClass,  signature, vaList);
    }

    public void call_sign() {
        DvmClass clz = vm.resolveClass("com/km/encryption/api/Security");
        String methodSign = "sign([B)Ljava/lang/String;";
        StringObject ret = clz.callStaticJniMethodObject(emulator, methodSign, new ByteArray(vm, "book_privacy=1cache_ver=1642759975gender=2read_preference=2tab_type=2".getBytes(StandardCharsets.UTF_8)));
//        System.out.println("sign:" + ret.getValue());
    }

    public static void main(String[] args) {
        Qimao test = new Qimao();
        test.call_sign();
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末芽淡,一起剝皮案震驚了整個濱河市马绝,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挣菲,老刑警劉巖富稻,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異白胀,居然都是意外死亡椭赋,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門或杠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哪怔,“玉大人,你說我怎么就攤上這事向抢∪暇常” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵挟鸠,是天一觀的道長叉信。 經(jīng)常有香客問我,道長艘希,這世上最難降的妖魔是什么硼身? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮覆享,結(jié)果婚禮上鸠姨,老公的妹妹穿的比我還像新娘。我一直安慰自己淹真,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布连茧。 她就那樣靜靜地躺著核蘸,像睡著了一般巍糯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上客扎,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天祟峦,我揣著相機(jī)與錄音,去河邊找鬼徙鱼。 笑死宅楞,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的袱吆。 我是一名探鬼主播厌衙,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绞绒!你這毒婦竟也來了婶希?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蓬衡,失蹤者是張志新(化名)和其女友劉穎喻杈,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狰晚,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡筒饰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了壁晒。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瓷们。...
    茶點故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖讨衣,靈堂內(nèi)的尸體忽然破棺而出换棚,到底是詐尸還是另有隱情,我是刑警寧澤反镇,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布固蚤,位于F島的核電站,受9級特大地震影響歹茶,放射性物質(zhì)發(fā)生泄漏夕玩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一惊豺、第九天 我趴在偏房一處隱蔽的房頂上張望燎孟。 院中可真熱鬧,春花似錦尸昧、人聲如沸揩页。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽爆侣。三九已至萍程,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間兔仰,已是汗流浹背茫负。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留乎赴,地道東北人忍法。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像榕吼,于是被迫代替她去往敵國和親饿序。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,779評論 2 354

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

  • 唯品會OAuth api_sign逆向分析 Java層 抓包 jadx打開apk友题,搜索api_sign 進(jìn)入看看 ...
    ever_hu閱讀 1,432評論 3 1
  • 京東sign unidbg逆向 環(huán)境 jdk 1.8.0_311 app 9.2.2 unidbg還原 新建包和類...
    ever_hu閱讀 3,997評論 0 3
  • 分析第一步當(dāng)然是抓包嗤堰。 很輕松抓到連接,開始分析其數(shù)據(jù)加解密度宦。 小說內(nèi)容解密:通過調(diào)用棧發(fā)現(xiàn)其調(diào)用了jni方法進(jìn)行...
    啊b閱讀 1,216評論 0 2
  • 唯品會登錄edata字段分析 Java層 jadx搜索"edata" 對EDATA查找用例 com.achievo...
    ever_hu閱讀 539評論 0 0
  • 篇幅有限 完整內(nèi)容及源碼關(guān)注公眾號:ReverseCode踢匣,發(fā)送 沖 抓包 Charles本地證書 安卓8 安卓...
    布丁和尚閱讀 379評論 0 2