首页
社区
课程
招聘
[原创]某黑产app的so文件无法找到的分析
发表于: 2024-11-25 10:45 3166

[原创]某黑产app的so文件无法找到的分析

2024-11-25 10:45
3166
第一步:jadx分析,看起来是dex加壳的,直接frIDA脱掉,这些和本文的主题无关就不再详述

第二步:解压apk,发现lib里面只有一个so文件,很奇怪,ida不能分析,通过file指令查看,是一个data文件

第三步:继续frida hook dlopen,查看so的加载,发现加载了很多自带的so,但是apk里面没有,所以怀疑是自定义的so加载方式

android_dlopen_ext probe /data/user/0/com.hptfludyjx.syjqeafkll/files/HJfOTtKmmn/libflutter.so
android_dlopen_ext probe /vendor/lib/hw/gralloc.sdm845.so
android_dlopen_ext probe /data/user/0/com.hptfludyjx.syjqeafkll/files/HJfOTtKmmn/libkiwi.so
android_dlopen_ext probe /vendor/lib/hw/android.hardware.graphics.mapper@2.0-impl-qti-display.so
android_dlopen_ext probe /data/dalvik-cache/arm/system@product@app@[email]webview@webview.apk[/email]@classes.dex
android_dlopen_ext probe libwebviewchromium.so
android_dlopen_ext probe /system/product/app/webview/webview.apk!/lib/armeabi-v7a/libwebviewchromium.so
android_dlopen_ext probe /system/lib/libwebviewchromium_plat_support.so

第四步:继续分析代码,发现了端倪,有一段可疑代码会对asset的文件进行解密解压,得出so文件。


if (list[i2].endsWith("_hmc")) {
 
                    MfLogUtils.e(Constant.METHOD_DEBUG, "找到hmc文件");
 
                    String str = list[i2];
 
                    String replace = list[i2].replace("_hmc", "");
 
                    File filesDir = context.getFilesDir();
 
                    String str2 = filesDir.getAbsolutePath() + PsuedoNames.PSEUDONAME_ROOT + replace;
 
                    if (!new File(str2 + PsuedoNames.PSEUDONAME_ROOT + str).exists()) {
 
                        MfLogUtils.e(Constant.METHOD_DEBUG, "filename_assetsso不存在");
 
                        copyAssetsFileToDataDir(context, str, str2 + PsuedoNames.PSEUDONAME_ROOT + str);
 
                        MfLogUtils.e(Constant.METHOD_DEBUG, "将assets压缩文件复制到data目录");
 
                        if (new File(str2 + PsuedoNames.PSEUDONAME_ROOT + str).exists()) {
 
                            MfLogUtils.e(Constant.METHOD_DEBUG, "data文件存在:" + str2 + PsuedoNames.PSEUDONAME_ROOT + str);
 
                        }
 
                        int[] iArr = null;
 
                        Enumeration<? extends ZipEntry> entries = new ZipFile(str2 + PsuedoNames.PSEUDONAME_ROOT + str).entries();
 
                        while (true) {
 
                            if (!entries.hasMoreElements()) {
 
                                break;
 
                            }
 
                            ZipEntry nextElement = entries.nextElement();
 
                            MfLogUtils.e(Constant.METHOD_DEBUG, "遍历到文件:" + nextElement.getName());
 
                            String comment = nextElement.getComment();
 
                            if (comment != null && !comment.equals("")) {
 
                                MfLogUtils.e(Constant.METHOD_DEBUG, "查询到密钥:" + comment);
 
                                JSONArray jSONArray = new JSONArray(comment);
 
                                int[] iArr2 = new int[jSONArray.length()];
 
                                for (int i3 = 0; i3 < jSONArray.length(); i3++) {
 
                                    iArr2[i3] = ((Integer) jSONArray.get(i3)).intValue();
 
                                }
 
                                iArr = iArr2;
 
                            }
 
                        }
 
                        MfLogUtils.e(Constant.METHOD_DEBUG, "已经成功获取密钥了:" + iArr.toString());
 
                        ZipFileUtils.unZip(str2 + PsuedoNames.PSEUDONAME_ROOT + str, str2);
 
                        MfLogUtils.e(Constant.METHOD_DEBUG, "解压文件");
 
                        for (File file : new File(str2).listFiles()) {
 
                            MfLogUtils.e(Constant.METHOD_DEBUG, "解压生成文件:" + file.getAbsolutePath());
 
                            if (file.getName().contains(".so")) {
 
                                desfile(file.getAbsolutePath(), iArr);
 
                                MfLogUtils.e(Constant.METHOD_DEBUG, "解密单个文件:" + file.getAbsolutePath());
 
                            }
 
                        }
 
                        MfLogUtils.e(Constant.METHOD_DEBUG, "解密文件完成");
 
                    }



第五步:分析代码可知,这个就是将hmc后缀的zip文件解压,然后获取so文件的comment内容,作为key,比较简单所以直接在java中实现key的获取和so解密。



import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.*; 
   
public class solution { 
 
    public static byte[] encDesSimple(byte[] bArr, int[] iArr) {
        int length = bArr.length;
        for (int i = 0; i < length; i++) {
            if (bArr[i] >= 0) {
                bArr[i] = (byte) iArr[bArr[i]];
            }
        }
        return bArr;
    }
 
 
    public static void writeFileBytes(File file, byte[] bArr) {
        try {
            FileOutputStream fileOutputStream = new FileOutputStream(file);
            fileOutputStream.write(bArr);
            fileOutputStream.flush();
            fileOutputStream.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
 
   public static byte[] readFileBytes(File file) throws IOException {
        byte[] bArr = new byte[1024];
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        FileInputStream fileInputStream = new FileInputStream(file);
        while (true) {
            int read = fileInputStream.read(bArr);
            if (read != -1) {
                byteArrayOutputStream.write(bArr, 0, read);
            } else {
                fileInputStream.close();
                return byteArrayOutputStream.toByteArray();
            }
        }
    }
 
    public static void main(String args[]) 
    { 
        try { 
            // Create a Zip File 
            Enumeration<? extends ZipEntry> entries 
                = new ZipFile("test").entries(); 
                int i = 0;
                String soname = "libkiwi_enc.so";
                while (true) {
                    if (!entries.hasMoreElements()) {
                        break;
                    }
                    ZipEntry nextElement = entries.nextElement();
                    String comment = nextElement.getComment();
 
                    if (comment != null && !comment.equals("")) {
 
                    System.out.println("comment = "
                    + comment);
                    i++;
                    }
                    System.out.println("count = "
                    + i);
                }
            // Display the comment 
            // of the zip file 
            // using getComment() function 
 
            int [] iArr = {73,76,74,98,77,115,124,103,68,99,75,104,85,126,105,83,117,93,112,121,96,102,111,114,66,94,81,125,127,113,90,123,84,88,65,107,87,116,91,120,118,106,101,110,69,78,122,82,89,70,100,72,67,64,80,97,119,109,92,108,79,95,86,71,53,34,24,52,8,44,49,63,51,0,2,10,1,4,45,60,54,26,47,15,32,12,62,36,33,48,30,38,58,17,25,61,20,55,3,9,50,42,21,7,11,14,41,35,59,57,43,22,18,29,23,5,37,16,40,56,39,19,46,31,6,27,13,28};
            writeFileBytes(new File(soname.replace("_enc", "")), encDesSimple(readFileBytes(new File(soname)), iArr));
 
        } 
        catch (Exception e) { 
            System.out.println(e.getMessage()); 
        } 
    } 
}

第六步:将解密后的示例so文件拖入ida中,可以正常解析,至此第一关算是结束,通过进一步查看代码发现他们还会动态的更新so文件做一些事情,但是更新的域名链接无法访问。



小结:黑产的app一般都是不考虑性能的,所以这种解压的方式在正经的app上应该是不存在的,另外这个app经过向反zha举报了很久,还是依旧活得很好,只能呵呵。后续会继续看so的加固,估计还会有更多的加固方式吧,毕竟不用考虑性能,仅当做新人的学习。



[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 2
支持
分享
最新回复 (12)
雪    币: 10
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
期待后续更新
2024-11-25 18:44
0
雪    币: 353
活跃值: (1847)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
这个应该是个 带颜色的app 而且用了 游戏盾
2024-11-25 21:09
0
雪    币: 197
活跃值: (1796)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
4
做过  这个有盾
2024-11-25 21:15
0
雪    币: 241
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
5
woainihehe 做过 这个有盾
有文章或者交流一下的地方吗?做的结果如何?
2024-11-26 08:20
0
雪    币: 241
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
6
湘子zyx 这个应该是个 带颜色的app 而且用了 游戏盾
是的,攻破了吗?感觉这个活得很好
2024-11-26 08:25
0
雪    币: 2393
活跃值: (10552)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
7
网站的UI改了吗,现在文章的文字不会换行了。。。。
2024-11-26 09:41
0
雪    币: 241
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
8
你瞒我瞒 网站的UI改了吗,现在文章的文字不会换行了。。。。
应该是我复制的问题,在其他地方先发表的文章
2024-11-26 10:00
0
雪    币: 353
活跃值: (1847)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
9
wx_ZXC_301 是的,攻破了吗?感觉这个活得很好
做过几个类似的 算法直接flutter里面扣 游戏盾 找齐了参数调用就行了
2024-11-26 19:04
0
雪    币: 2186
活跃值: (2912)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
10
感谢分享
2024-11-26 20:25
0
雪    币: 197
活跃值: (1796)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
11
wx_ZXC_301 有文章或者交流一下的地方吗?做的结果如何?
没有文章  全是淌着水过河
2024-11-27 02:16
0
雪    币: 739
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
12
flutter
2024-11-27 07:33
0
雪    币: 241
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
13
湘子zyx 做过几个类似的 算法直接flutter里面扣 游戏盾 找齐了参数调用就行了
好吧,也就是libapp的逆向
2024-11-27 08:36
0
游客
登录 | 注册 方可回帖
返回
//