本帖最后由 delicious 于 2021-09-10 23:24 编辑
概述今天群里有人传了个apk文件,名字叫送给最好的TA 。这是一个整人app,安装并打开会播放一段音频,并循环调节媒体音量,维持最大值。从后台划掉/卸载/关机都可以停止该程序。 网传所谓的截图、获取设备信息并上传等行为均不存在。反编译后看到的截图相关代码实际上并没有被调用,只是打包apk时封装进去的类,关键逻辑位于main.lua中。 apk分析拿到样本之后,别急着安装运行,先看看它的目录结构。 
运行app后会自动播放这个音频文件,先对它进行替换。 这里使用AU对该mp3进行静音处理后,重打包并签名。 (其实也可以替换成自己想要播放的音频) 找到加载lua的关键函数众所周知,lua脚本需要加载,而在加载之前肯定是要先解密的,所以只要找到解密函数,然后就可以把解密后的lua脚本dump出来。 使用IDA 打开libluajava.so ,经过分析找到函数luaL_loadbufferx
luaL_loadbufferx 的第二个参数是加密的字节数组,第三个参数是大小,第四个参数是lua文件位置。 程序在这个函数中加载加密lua脚本,其中对脚本进行了解密操作。 根据第四个参数我们可以区分目前加载的lua脚本名称,从而选择性地dump (即在函数开头下断点,查看第四个参数内容)
luaL_loadbufferx 函数伪代码如下: 
首先对输入的字节数组进行判断,如果以1B开头,且第二位不是0C,则进行解密操作,否则直接调用j_lua_load 加载lua脚本 在第41行下断点即可获取到解密后的字节数组,从而dump 动态调试进行dumpIDA 在第41行断下之后,运行python脚本dump即可
import idaapi data = idaapi.dbg_read_memory(0xf4daff00, 0x3000) fp = open('d:\\dump.lua', 'wb') fp.write(data) fp.close()
此处的0xf4daff00, 0x3000 需要替换成解密后字节的起始地址和长度 长度写大一点也没事,可以再用010Editor 删除(所以我这写的0x3000 ) 分析lua由于dump出来的lua是字节码,并不是源码形式。所以还需要反编译后才能查看源码。(使用unluac_2015_06_13.jar 进行反编译) init.lua中进行包名、版本号、主题、权限等配置 local L0_0 appname = "送给最好的TA" appver = "1.0" appcode = "10" appsdk = "15" path_pattern = "" packagename = "com.sgzh.dt" theme = "Theme_DeviceDefault_Dialog_NoActionBar_MinWidth" app_key = "" app_channel = "" developer = "" description = "" debugmode = false L0_0 = { "INTERNET", "WRITE_EXTERNAL_STORAGE" } user_permission = L0_0
主要功能在main.lua中完成: require("import") import("android.app.*") import("android.os.*") import("android.widget.*") import("android.view.*") import("android.view.View") import("android.content.Context") import("android.media.MediaPlayer") import("android.media.AudioManager") import("com.androlua.Ticker") activity.getSystemService(Context.AUDIO_SERVICE).setStreamVolume(AudioManager.STREAM_MUSIC, 15, AudioManager.FLAG_SHOW_UI) activity.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE) m = MediaPlayer() m.reset() m.setDataSource(activity.getLuaDir() .. "/0.mp3") m.prepare() m.start() m.setLooping(true) ti = Ticker() ti.Period = 10 function ti.onTick() activity.getSystemService(Context.AUDIO_SERVICE).setStreamVolume(AudioManager.STREAM_MUSIC, 15, AudioManager.FLAG_SHOW_UI) activity.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_IMMERSIVE) end ti.start() function onKeyDown(A0_0, A1_1) if string.find(tostring(A1_1), "KEYCODE_BACK") ~= nil then activity.getSystemService(Context.AUDIO_SERVICE).setStreamVolume(AudioManager.STREAM_MUSIC, 15, AudioManager.FLAG_SHOW_UI) end return true end
首先设置音频源文件路径为0.mp3,使用单曲循环模式播放音频。然后使用Ticker 循环调节媒体音量至最大值,同时拦截了返回键。 主要功能其实跟以前那个叫目力的app差不多。(不过今天这个apk的音频就太那啥了...) 静音样本下载https://pan.baidu.com/s/1jTUcjknxr-fwETgtEVpQSg dump出来的luahttps://pan.baidu.com/s/1X0un1jgVB6QAgPRqNu_3Wg
|