栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

frida脱壳

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

frida脱壳

frida脱壳 1. 原理 apk加壳的原理
壳apk的dex文件读取源apk的 classes.dex 文件,加密后写入新的dex文件,组成新的apk
加壳apk的加载过程
新apk被点击, 壳apk会把源classes.dex文件读取出来,解密后, 把源classes.dex给加载起来
frida脱壳的思路
 在壳apk把源classes.dex文件读取出来解密之后, 将源classes.dex文件内容从内存中dump出来, 写入自定义dex文件中
2. 工具
1. python  3.6.1
	安装frida模块
	pip install frida==12.8.0
2. 安卓设备导入frida-server  版本12.8.0  与模块版本相对应
3. ida  用来查看hook方法的导出方法名
3. 需要用到的frida模块及方法
1. Module 模块 (操作内存地址)
	    案例: hello.so  ——函数——> printhello()
	    findExportByName(moduleName|null, exportName)
	        moduleName: lib名字 (so文件名称)
	        exportName: 函数名称
	        返回exportName的地址指针
	        用法: var pointer = Module.findExportByName('hello.so', "printhello")
	        
2.   Interceptor模块
	    attach(target, callback) (不会修改函数逻辑)
			    target: 指针地址 pointer (方法地址)
			    callback: 回调函数
			        onEnter: hook住一个函数之后会进入onEnter, 主要用于打印  如: 加密前的变量值
			        onLeave: 当被hook的函数执行结束之后会进入onLeave, 用于打印加密之后的秘文

3. Memory 模块
	a. 把内存里的值转成字符串
		Memory.readUtf8String()
	b. 把内存里的值转换成整型
		Memory.readInt()
	c. 以begin为起始位置,从内存中读length长度的数据出来 返回ArrayBuffer类型
		Memory.readByteArray(begin, length)  
    		begin: 起始位置
    		length: 读取的长度(十进制)
  
  4. File 模块 写文件流程
     new File(filepath, mode)
     write(data)
     flush()
     close()
     用法:
		file = new File("yuanrenxue.dex", "wb")
		//data 是字符串或者 arrayBuffer // readByteArray() 返回的arrayBuffer
		file.write(data)
		file.flush()
		file.close()
4. 需要用到的js方法
1. 把十进制地址转换成NativePointer类型 frida里操作内存地址需要NativePointer类型
	ptr()

2. 把其它进制转换成10进制
parseInt(num, radix)
    num: 需要转化的数值
    radix: 进制数(2, 16...)
5. dex文件结构
dex文件在内存中的大致规格("每一行都表示一个内存单元"):
    8个字节   magic ----> dex 035 (固定)
    4个字节   校验位
    20个字节  签名
    4个字节   dex文件大小
    数据
    预留位

说明:
1. 假设源apk的dex文件在内存中的起始地址为 begin_addr
2. 则 begin_addr + 32 之后,改地址中存放的就是该dex文件的大小
3. 已知dex文件的起始位置 以及 dex文件的大小,则使用frida中的Memory.readByteArray就可以将其从内存中读取出来
6. hook 方法
安卓5,6,7,8,9, 使用art虚拟机
源dex文件最终会加载进内存的方法:
    Hook加载Dex的函数,把Dex从内存中dump出来
    Hook DexFile::OpenMemory() 
    DexFile::OpenMemory(const uint8_t* base,
                                 size_t size,
                                 const std::string& location,
                                 uint32_t location_checksum,
                                 MemMap* mem_map,//nullptr
                                 const OatDexFile* oat_dex_file,
                                 std::string* error_msg)
    该方法的位置System/Lib/libart.so文件中
    注意:
        1. hook前需要使用ida打开该so文件,查看OpenMemory的导出方法名,该方法名为最终的hook目标
        2. 第一个参数为源classes.dex文件的起始位置
        3. 安卓其他版本:
            安卓4: ,默认使用davlink虚拟机    libdvm.so    dexFileParse 函数
            安卓5, 6, 7, 8, 9:  libart.so    OpenMemory  或  OpenCommon  函数
            安卓10及以后: libdexfile.so   ArtDexFileLoader  函数
7. 案例 脱壳特来电
import sys
import frida

#  特来电app,运行起来之后使用frida-ps 查看app包名
package = 'com.qdtevc.teld.app'

jsCode = """
// 获取OpenMemory在so文件中的地址
var openMemory_address = Module.findExportByName('libart.so', '_ZN3art7DexFile10OpenMemoryEPKhjRKNSt3__112basic_stringIcNS3_11char_traitsIcEENS3_9allocatorIcEEEEjPNS_6MemMapEPKNS_10OatDexFileEPS9_');
send(openMemory_address);
Interceptor.attach(openMemory_address, {
     onEnter: function(args){
            // openMemory 方法的第一个参数为源apk在内存中的起始位置
            var dex_begin_address = args[1];
            send('dex_begin_address: ' + dex_begin_address);
            
            // dex 文件的前8个字节为magic字段,查看dex的格式说明(可使用dex的起始位置地址进行查看)
            // 打印magic会打印"dex 035"三个字符可以验证是否为dex文件
            send('magic: ' + Memory.readUtf8String(dex_begin_address));
            
            // 起始地址的32个字节之后, 表示dex文件的大小
            // 所以若要拿到dex文件的大小,需要将起始位置地址(16进制)转化成十进制之后+32
            var dex_size_address = parseInt(dex_begin_address, 16) + 32;
            
            // 将dex_size_address十进制地址转化成frida可以识别的NativePointer类型
            // 将改地址指向的值转化成十进制就是dex文件的大小
            var dex_size = Memory.readInt(ptr(dex_size_address));
            send('dex size: ' + dex_size);
            
            // 已知dex文件的起始位置/文件大小
            // 读取并写入
            var timestamp = new Date().getTime();
            var file = new File('/data/data/%s/' + timestamp + '.dex', 'wb');
            
            // Memory.readByteArray(begin, length)
            // 把内存中的数据读取出来, 从begin开始读, 取length长度
            file.write(Memory.readByteArray(dex_begin_address, dex_size));
            file.flush();
            file.close();
                        
        },
    onLeave: function(retval){
        }
});
"""%(package)

def on_message(message, data):
    if message["type"] == "send":
        print(u"[*] {0}".format(message["payload"]))
    else:
        print(message)

device = frida.get_remote_device()
pid = device.spawn(package)
process = device.attach(pid)

script = process.create_script(jsCode)
script.on("message", on_message)
print("【*】start")
script.load()
device.resume(pid)   # 表示app重启
sys.stdin.read()

说明:除了jsCode 其他为frida hook的固定写法
8. 执行方式
1. 手机usb链接电脑
2. 启动设备中的frida-server,并在cmd中做端口转发
	./frida-server
	 adb forward tcp:27043 tcp:27043
     adb forward tcp:27042 tcp:27042
3. 启动需要脱壳的app
4. 在cmd 中 frida-ps -U   查看指定app的包名
5. 将包名写入代码中的package 
6. 在pycharm中运行frida代码
7. 手机上的app会自动重启,最终脱壳后的dex文件会被保存在/data/data/包名/时间戳.dex
8. 使用 adb pull  安卓地址  本地地址   将dex文件从安卓设备中pull到本地    脱壳完成
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/724058.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号