Sec Hotspot 首页  排行榜  收藏本站  技术博客  RSS
统计信息
已收录文章数量:14761 篇
已收录公众号数量:89 个
本站文章为爬虫采集,如有侵权请告知
已收录微信公众号
网信中国 区块链大本营 白说区块链 区块链投资家 区块链官微 区块链铅笔Blockchain HACK学习呀 二道情报贩子 合天智汇 小白帽学习之路 小米安全中心 弥天安全实验室 SAINTSEC SecPulse安全脉搏 TideSec安全团队 360安全卫士 游侠安全网 计算机与网络安全 安全祖师爷 安全学习那些事 腾讯安全联合实验室 黑客技术与网络安全 安全圈 腾讯御见威胁情报中心 Python开发者 Python之禅 编程派 Python那些事 Python程序员 安全威胁情报 吾爱破解论坛 行长叠报 安在 i春秋 嘶吼专业版 E安全 MottoIN 网信防务 网安杂谈 数说安全 互联网安全内参 漏洞战争 安全分析与研究 邑安全 ChaMd5安全团队 天融信阿尔法实验室 安全牛 SecWiki 安全学术圈 信安之路 漏洞感知 浅黑科技 Secquan圈子社区 奇安信集团 奇安信 CERT 国舜股份 雷神众测 盘古实验室 美团安全应急响应中心 瓜子安全应急响应中心 顺丰安全应急响应中心 蚂蚁金服安全响应中心 携程安全应急响应中心 滴滴安全应急响应中心 字节跳动安全中心 百度安全应急响应中心 腾讯安全应急响应中心 网易安全应急响应中心 OPPO安全应急响应中心 京东安全应急响应中心 Bypass CNNVD安全动态 安恒应急响应中心 天融信每日安全简报 奇安信威胁情报中心 看雪学院 黑白之道 水滴安全实验室 安全客 木星安全实验室 云鼎实验室 绿盟科技安全预警 白帽汇 深信服千里目安全实验室 腾讯玄武实验室 长亭安全课堂 FreeBuf 绿盟科技 nmask
初试IDA&FRIDA联合调试简单ollvm保护的加密函数源码
本文来自公众号:看雪学院   2020.07.16 17:58:58


本文为看雪论坛优秀文章

看雪论坛作者 ID:无造



本文为 看雪安卓高研3w班(5月班)优秀学员 作品。


下面先让我们来看看讲师对学员学习成果的点评,以及学员的学习心得吧!


讲师点评


ollvm混淆强度不是很强的时候,可以通过IDA静态分析参数的交叉引用和返回值的来源,定位到与参数相关的函数,然后使用frida hook这些函数,定位到处理参数或生成返回结果的关键函数。


但是ollvm混淆强度比较强的时候,还是需要用到3月学习的trace方法来分析出参数被加密算法计算的过程。




学员感想


这是3W班5月的习题。题目要求是 e 函数需要逆向出源码。

yang老师本意应该是训练下trace的汇编代码阅读能力,但是由于ollvm混淆开得比较低,所以IDA的反汇编代码也是可以阅读,当时偷懒就没有专心去阅读trace了。

通过本题初次尝试了还原so文件代码,在没有开太多混淆的情况下还是可以使用IDA配合frida还原,像之前3月题目混淆开的高一点这种方式就要搞死掉了。



ps. 题目附件请点击“阅读原文”下载。


现在, 看雪 《安卓高级研修班(网课)》9月班 开始招生啦!点击查看详情报名吧~





解题过程



还原代码

#include <stdio.h> /* Swap bytes in 32 bit value.  */#define __builtin_bswap32(x) \     ((((x) & 0xff000000u) >> 24) | (((x) & 0x00ff0000u) >>  8) |         \      (((x) & 0x0000ff00u) <<  8) | (((x) & 0x000000ffu) << 24))#define bswap32(x) ((unsigned int)__builtin_bswap32(x)) void myenc(char* str) {    /**********  程序写死 **********/    char* strWWW = "www.pediy.com&kanxue";    char* stryy = "yy";    unsigned char hexData[264] = {        0x63, 0x7C, 0x77, 0x7B, 0xF2, 0x6B, 0x6F, 0xC5, 0x30, 0x01, 0x67, 0x2B, 0xFE, 0xD7, 0xAB, 0x76,        0xCA, 0x82, 0xC9, 0x7D, 0xFA, 0x59, 0x47, 0xF0, 0xAD, 0xD4, 0xA2, 0xAF, 0x9C, 0xA4, 0x72, 0xC0,        0xB7, 0xFD, 0x93, 0x26, 0x36, 0x3F, 0xF7, 0xCC, 0x34, 0xA5, 0xE5, 0xF1, 0x71, 0xD8, 0x31, 0x15,        0x04, 0xC7, 0x23, 0xC3, 0x18, 0x96, 0x05, 0x9A, 0x07, 0x12, 0x80, 0xE2, 0xEB, 0x27, 0xB2, 0x75,        0x09, 0x83, 0x2C, 0x1A, 0x1B, 0x6E, 0x5A, 0xA0, 0x52, 0x3B, 0xD6, 0xB3, 0x29, 0xE3, 0x2F, 0x84,        0x53, 0xD1, 0x00, 0xED, 0x20, 0xFC, 0xB1, 0x5B, 0x6A, 0xCB, 0xBE, 0x39, 0x4A, 0x4C, 0x58, 0xCF,        0xD0, 0xEF, 0xAA, 0xFB, 0x43, 0x4D, 0x33, 0x85, 0x45, 0xF9, 0x02, 0x7F, 0x50, 0x3C, 0x9F, 0xA8,        0x51, 0xA3, 0x40, 0x8F, 0x92, 0x9D, 0x38, 0xF5, 0xBC, 0xB6, 0xDA, 0x21, 0x10, 0xFF, 0xF3, 0xD2,        0xCD, 0x0C, 0x13, 0xEC, 0x5F, 0x97, 0x44, 0x17, 0xC4, 0xA7, 0x7E, 0x3D, 0x64, 0x5D, 0x19, 0x73,        0x60, 0x81, 0x4F, 0xDC, 0x22, 0x2A, 0x90, 0x88, 0x46, 0xEE, 0xB8, 0x14, 0xDE, 0x5E, 0x0B, 0xDB,        0xE0, 0x32, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x5C, 0xC2, 0xD3, 0xAC, 0x62, 0x91, 0x95, 0xE4, 0x79,        0xE7, 0xC8, 0x37, 0x6D, 0x8D, 0xD5, 0x4E, 0xA9, 0x6C, 0x56, 0xF4, 0xEA, 0x65, 0x7A, 0xAE, 0x08,        0xBA, 0x78, 0x25, 0x2E, 0x1C, 0xA6, 0xB4, 0xC6, 0xE8, 0xDD, 0x74, 0x1F, 0x4B, 0xBD, 0x8B, 0x8A,        0x70, 0x3E, 0xB5, 0x66, 0x48, 0x03, 0xF6, 0x0E, 0x61, 0x35, 0x57, 0xB9, 0x86, 0xC1, 0x1D, 0x9E,        0xE1, 0xF8, 0x98, 0x11, 0x69, 0xD9, 0x8E, 0x94, 0x9B, 0x1E, 0x87, 0xE9, 0xCE, 0x55, 0x28, 0xDF,        0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00    };     /**********  计算长度 **********/    unsigned int lenStr = strlen(str);    unsigned int lenPadding = (-(signed int)lenStr & 0xf);    unsigned int lenEncStr = lenStr + 6 + lenPadding;    unsigned char* bufEnc = (unsigned char*)malloc(lenEncStr);    memset(bufEnc, 0, lenEncStr);     /**********  开始填充 **********/    unsigned char* offBuf = (unsigned char*) (bufEnc + 6);    //memmove(offBuf, strWWW, lenStrWWW);    memmove(offBuf, str, lenStr);    bufEnc[0] = stryy[0];    bufEnc[1] = stryy[1];    bufEnc[2] = 0x3;    bufEnc[3] = lenPadding;    //这里分2次设置 100    bufEnc[4] = 0x00;    bufEnc[5] = 0x01;      /**********  第一步加密 **********/    int i = 0;    for (i = 6; i < lenEncStr; i++) {        unsigned char tmpc = bufEnc[i];        unsigned char changec = hexData[tmpc];        bufEnc[i] = changec;    }     /**********  第二步加密 **********/    //生成第二步秘钥    unsigned int lenStrWWW = strlen(strWWW);    //这里只复制0x10位    unsigned int lenBufKey = 0x10;    unsigned char* bufKeyWWW = (unsigned char*)malloc(lenBufKey);    memset(bufKeyWWW, 0, lenEncStr);    memmove(bufKeyWWW, strWWW, lenBufKey);    for (i = 0; i < lenBufKey; i++) {        unsigned char tmpc = bufKeyWWW[i];        unsigned char changec = hexData[tmpc];        bufKeyWWW[i] = changec;    }     unsigned int* bufKeyWWWInt = (unsigned int*) bufKeyWWW;    for (i = 0; i < 0x10 / 4; i++) {        bufKeyWWWInt[i] = bswap32(bufKeyWWWInt[i]);    }         unsigned int* bufEncInt = (unsigned int*)offBuf;    for (i = 0; i < (lenEncStr - 6) / 4; i++) {        unsigned int part = bswap32(bufEncInt[i]);        unsigned int partchange = part;        if (i % 4 > 0) {            partchange = (part >> 32 - i % 4 * 8) ^ (part << i % 4 * 8);        }        bufEncInt[i] = bswap32(partchange ^ bufKeyWWWInt[i%4]);    }     /**********  打印结果 **********/    for (i = 0; i < lenEncStr; i++) {        if(bufEnc[i] < 0x10)            printf("0", bufEnc[i]);        printf("%x", bufEnc[i]);    }    printf("\nDone");} int main() {    myenc("pediy_imyang_abcdefghij");}

解题思路

本来想使用IDA Trace,拿到日志后,没有直接定位到计算的地方,所以整个还原是使用IDA动态调试和Frida来完成的。

步骤1:查看函数e


函数e中直接使用了sub_1500C,传入v42就是bytes数组,v41就是我们返回的秘钥。

        var addr_1500C = base_native.add(0x1500C);        Interceptor.attach(addr_1500C, {            onEnter: function (args) {                this.args0 = args[0];                console.log("addr_1500C onEnter args0:",hexdump(this.args0));                var args1addr = ptr(args[1]).add(Process.pointerSize*2).readPointer();                console.log("addr_1500C onEnter args1:",args1addr.readCString());            },            onLeave: function (retval) {                var resultaddr = ptr(this.args0).add(Process.pointerSize*5).readPointer();                console.log("addr_1500C onLeave args0:",hexdump(resultaddr));            }        });

使用Frida可验证,这里返回值存储的位置需要注意。

步骤2:查看sub_1500C


sub_1500C中计算了加密字符串长度,然后引入了新字符串www.pediy.com&kanxue。之后直接调用sub_13CE4计算加密。

参数列表
x0 字符串pediy_imyang_abcdefghij
x1 字符串长度
x2 字符串www.pediy.com&kanxue
x3 字符串长度
x4 需要设置的buff


步骤3:查看sub_13CE4


前2步,拷贝字符串pediy_imyang_abcdefghij到buf,设置头为7979("yy")
sub_13808 使用www.pediy.com&kanxue生成一串0x10长度的key,这个后面依次和更改顺序的加密数据异或。



第3,4步,填充头,通过转换表byte_42108转换字符串。


很多sub_172D0是用来转换位置的,比如AABBCCDD改成BBCCDDAA。共3种模式。修改后和上面加密key进行异或。


主要的加密都在sub_13CE4中。



- End -


看雪ID:无造

https://bbs.pediy.com/user-571058.htm

*本文由看雪论坛 无造 原创,转载请注明来自看雪社区。


推荐文章++++

* 记一次so文件动态解密

* 阿里2015第二届安全挑战赛第三题题解

* VSCode搭建轻量驱动开发环境

* 恶意代码分析之反射型DLL注入

* 使用Frida简单实现函数粒度脱壳


好书推荐














公众号ID:ikanxue
官方微博:看雪安全
商务合作:wsc@kanxue.com



ps. 觉得对你有帮助的话,别忘点 分享 点赞 在看 ,支持看雪哦~


“阅读原文 一起来充电吧!