学逆向论坛

找回密码
立即注册

只需一步,快速开始

发新帖

2万

积分

41

好友

1171

主题
发表于 2020-8-6 23:38:19 | 查看: 2420| 回复: 3
0x0 样本信息
样本:17年BD加固
NativeHook:基于Substrate的Nativehook
Android系统: 6.0.1
0x1 IDAPython脚本辅助应对花指令
上一个样本中,分析壳的initarray时候,就发现有花指令和大循环,那时候没有处理,而是直接找了别的办法去脱出dex,这个样本,又一次遇到了带大量简单花指令的initarray,在老大的要求下,决定对抗一波。

简单的脚本界面

脱壳成长之路(3)-初识VMP

脱壳成长之路(3)-初识VMP

log_saver:主要是保存动态调试保存so的日志
init_array:快速定位到so的initarray起始,提高一下每次调试的效率
最后一个就是辅助调试花指令了
花指令详情:

脱壳成长之路(3)-初识VMP

脱壳成长之路(3)-初识VMP

汇编代码中有大量的形似与上面的指令,都是无效指令,分析的时候十分费时间,由于这个花指令特征非常明显,所以可以写脚本去辅助分析。
我的思路:
遍历整个代码匹配花指令的头,然后在花指令后面2句最终BEQ或者B的地址继续循环,直到不是花指令为止,下断点。这里面有可能会遇到真正有用的代码在花指令中,这种情况,可以try一下,catch的时候在头部地址下段即可

核心代码:
[pre]# 脚本一定要在线程中执行,否则会卡住主线程,导致界面卡死
def loop_flower_code(self, cur_addr):
    dsm = idaapi.generate_disasm_line(cur_addr)
    dsm = str(dsm)
    if '0x3F0C' in dsm and 'LDR' in dsm:
        try:
            print 'flower code start'
            self.flower_start = True
            addrbe = cur_addr + 0x18
            addrB = cur_addr + 0x1A
            dsmBe = idaapi.generate_disasm_line(addrbe)
            dsmB = idaapi.generate_disasm_line(addrB)
            f_addr = dsmBe.split('loc_')[1]
            s_addr = dsmB.split('loc_')[1]
            f_addr = int(f_addr, 16)
            s_addr = int(s_addr, 16)
            # 继续循环后面跳转的地址
            self.loop_flower_code(f_addr)
            self.loop_flower_code(s_addr)
        except BaseException:
            # 这里检测到不是标准的花指令,说明中间有有用的代码,所以在头部下断点
            idc.add_bpt(cur_addr, 1)
    elif self.flower_start:
        # 检测到已经不是花指令了,下断点
        print 'bpt:' + str(hex(cur_addr))
        self.flower_start = False
        idc.add_bpt(cur_addr, 1)[/pre]
后面继续分析initarray,发现有so抹头,初始化反调试、和解密so的操作,篇幅原因就不截图了,印象笔记有完整的分析笔记,有需要的可以私我一哈。
0x2 dexdump,并修复大偏移
标准操作,dexdump,并且修复大偏移,这个操作就不再重复写了,有需求可以看我之前的文章。

贴一个dump出的dex

脱壳成长之路(3)-初识VMP

脱壳成长之路(3)-初识VMP

会发现,apk中的所有activity的onCreate函数被抽取了,然后调用了native的函数,现在仔细分析一下native函数

hook register_natvie 拿到动态注册函数的地址,然后进行分析

最终发现so中频繁调用JNI接口,于是简单的hook了一波

find cls android/app/Activity|onCreate
find cls com/nfbazi/xuankong/activity_register | setContentView
find cls android/widget/EditText
find cls com/nfbazi/xuankong/a/a
setText
setFocusable
setFocusableInTouchMode
find cls android/widget/Button
find cls com/nfbazi/xuankong/cr

find cls com/nfbazi/xuankong/cq

find cls android/widget/TextView
发现,之前java实现的功能,都用JNI实现了。于是开始了漫长的分析(过程十分漫长,我反复看了很多遍so)最终在老大的提示下,定位到下图结构体

脱壳成长之路(3)-初识VMP

脱壳成长之路(3)-初识VMP

脱壳成长之路(3)-初识VMP

脱壳成长之路(3)-初识VMP

codeitem相关:

脱壳成长之路(3)-初识VMP

脱壳成长之路(3)-初识VMP

置换:

正常
206F 001A 0054 invoke-super {v4, v5},

处理后
2086 001A 0054 invoke-super {v4, v5},

所以这里面0x86 对应 smali opcode 6F
0x3 修复nativeMethodhook点:

脱壳成长之路(3)-初识VMP

脱壳成长之路(3)-初识VMP
dump code_item脚本[pre]void *(*old_func)(void *baidu, int index);


void *new_func(void *baidu, int index) {

    //这里会Crash。不过不要紧,我们紧紧要他的code
    int startAddress = (int) baidu;
    BDList *list = (BDList *) startAddress;
    log("%x", list->onCreateCodeStart[0xA]);
    CreateCode *code = (CreateCode *) list->onCreateCodeStart[0xA];
    log("%x", code->index);
    int codeSize = code->codeSize;
    log("code size is %d", codeSize);
    FILE *codeFile =                          fopen("/data/data/com.nfbazi.xuankong/cache/code.dex", "w+");
    char *CodeItem = (char *) code->codeItem;
    fwrite(CodeItem + 2, 1, codeSize * 2, codeFile);
    fclose(codeFile);
    exit(0);
    return old_func(baidu, index);
}

/**
* register函数启始地址
* @param address
*/
void dumpCodeItem(void *address) {

    int addr = (int) address;
    int offest = 0x1785C;
    void *p_addr = (void *) (addr + offest);
    log("%p", p_addr);
    if (old_func == 0) {
        elf_hook_Direct(p_addr, (void *) &new_func, (void **) &old_func);
    }
}
本地修复dex
#include <iostream>
#include <cstdio>

#define BYTE unsigned char
/**
* 每个opcode长度
*/
static unsigned char OpLen[256] =
        {
                0x02, 0x02, 0x04, 0xFF, 0xFF, 0x04, 0xFF, 0x02, 0x04, 0xFF, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
                0x02, 0x02, 0x02, 0x04, 0x06, 0x04, 0x04, 0x06, 0x0A, 0x04, 0x04, 0xFF, 0x04, 0x02, 0x02, 0x04,
                0x04, 0x02, 0x04, 0x04, 0x06, 0x06, 0x06, 0x02, 0x02, 0x04, 0xFF, 0x06, 0x06, 0x04, 0x04, 0x04,
                0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0xFF, 0xFF,
                0xFF, 0xFF, 0xFF, 0xFF, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
                0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
                0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, 0x06,
                0x06, 0x06, 0x06, 0xFF, 0x06, 0x06, 0x06, 0x06, 0x06, 0xFF, 0xFF, 0x02, 0xFF, 0x02, 0xFF, 0x02,

                0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
                0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
                0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
                0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
                0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
                0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
                0x04, 0x04, 0x04, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x06, 0xFF,
                0x06, 0xFF, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0xFF, 0xFF, 0xFF, 0xFF
        };

//置换表
int Res_OpBaidu[256] =
        {
                //0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,          0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,
                0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,          0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,       //      0
                0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,          0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x00,       //      1
                0x0F,0x0E,0x11,0x10,0x18,0x19,0x1A,0x1B,          0x1C,0x12,0x13,0x14,0x15,0x16,0x17,0x01,       //      2
                0x02,0x03,0x08,0x09,0x04,0x05,0x06,0x07,          0x0A,0x0B,0x0C,0x0D,0x1D,0x1E,0x1F,0x23,       //      3
                0x24,0x20,0x21,0x22,0x25,0xFF,0x27,0x28,          0x29,0x2A,0x34,0x35,0x36,0x37,0xFF,0x49,       //      4
                0x4E,0x51,0x52,0x53,0x54,0x55,0xFF,0x2D,          0x31,0x32,0x33,0x38,0x39,0x3A,0x2E,0x2F,       //      5
                0x30,0x3B,0x3C,0x3D,0x44,0x45,0x46,0x4A,          0x4B,0x4C,0x4D,0x47,0x48,0x4F,0x50,0x56,       //      6
                0x5C,0x5D,0x5E,0x57,0x58,0x59,0x5A,0x5B,          0x62,0x63,0x64,0x65,0x5F,0x60,0x61,0x66,       //      7

                0x67,0x6A,0x6B,0x6C,0x6D,0x6E,0x6F,0x68,          0x69,0x70,0x71,0x72,0x82,0x83,0x84,0x85,       //      8
                0x86,0x74,0x78,0x7B,0x7C,0x7D,0x7E,0x7F,          0x75,0x76,0x77,0x80,0x81,0x87,0x88,0x89,       //      9
                0x8A,0x8B,0x93,0x94,0x95,0xA7,0xA8,0xA9,          0xAA,0xAB,0xAC,0x96,0x97,0x98,0x99,0x9A,       //      A
                0x9B,0x9C,0x9D,0xB4,0xB5,0xB6,0xC6,0xC7,          0xC8,0xC9,0x9E,0x9F,0xA0,0xA1,0xA2,0x8C,       //      B
                0x8D,0x8E,0x8F,0x90,0x91,0x92,0xA3,0xA4,          0xA5,0xA6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,       //      C
                0xBD,0xBE,0xBF,0xAD,0xAE,0xAF,0xB0,0xB1,          0xB2,0xB3,0xCA,0xCB,0xCC,0xD5,0xD6,0xD7,       //      D
                0xD8,0xD9,0xDA,0xC0,0xC1,0xC2,0xC3,0xC4,          0xC5,0xDD,0xDE,0xDF,0xE0,0xE1,0xE2,0xFF,       //      E
                0xFF,0xFF,0xCD,0xCE,0xCF,0xD0,0xD1,0xD2,          0xD3,0xD4,0xDB,0xDC,0xFF,0xFF,0xFF,0xFF      //    F
        };

int main() {
    using namespace std;
    int i = Res_OpBaidu[0x86];
    cout << hex << i << endl;
    int length = OpLen;
    cout << hex << length << endl;
    FILE *codeFile = fopen("/Users/roy/CLionProjects/FixCodeItem/code.dex", "r");
    void *saveCode = malloc(214);
    fread(saveCode, 1, 214, codeFile);
    fclose(codeFile);

    FILE *fixFile = fopen("/Users/roy/CLionProjects/FixCodeItem/code_fix.dex", "w+");
    BYTE *codeChar = (BYTE *) saveCode;
    int len = 0;
    for (int j = 0; j < 214; ++j) {
        BYTE code = codeChar[j];
        int realopcode = Res_OpBaidu[pre];
        if (j == len) {
            printf("real opcode is %x\n", code);
            codeChar[j] = realopcode;
            len = len + OpLen[realopcode];
        }

        printf("%x\n", code);
    }
    fwrite(codeChar, 1, 214, fixFile);
    fclose(fixFile);
//    fread()
    return 0;
}[/pre]

0x4 完成

脱壳成长之路(3)-初识VMP

脱壳成长之路(3)-初识VMP

感谢我的老大!
感谢大家观看
游客,如果您要查看本帖隐藏内容请回复

温馨提示:
1.如果您喜欢这篇帖子,请给作者点赞评分,点赞会增加帖子的热度,评分会给作者加学币。(评分不会扣掉您的积分,系统每天都会重置您的评分额度)。
2.回复帖子不仅是对作者的认可,还可以获得学币奖励,请尊重他人的劳动成果,拒绝做伸手党!
3.发广告、灌水回复等违规行为一经发现直接禁言,如果本帖内容涉嫌违规,请点击论坛底部的举报反馈按钮,也可以在【投诉建议】板块发帖举报。
论坛交流群:672619046

    发表于 2020-8-7 23:18:02
    啥也不说了,加入收藏!

      发表于 2020-8-18 12:33:29
      非常不错啊,感谢楼主无私的共享精神!

        发表于 2020-8-23 08:48:01
        用心讨论,共获提升!

        小黑屋|手机版|站务邮箱|学逆向论坛 ( 粤ICP备2021023307号 )|网站地图

        GMT+8, 2024-12-28 00:13 , Processed in 0.144882 second(s), 53 queries .

        Powered by Discuz! X3.4

        Copyright © 2001-2021, Tencent Cloud.

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