0x0
样本:
play.google.comLost Sword - Google Play 上的应用
波涛胸涌&华丽冒险传说!好胸的冒...
Free正主:
largosoft.co.kr라르고소프트|LARGOSOFT
라르고소프트는 게임 보안 솔루션 개발에 특화된 기업으로, 게임 환경에서 발생하는 다양한 보안 위협을 효과적으로 방어하는 기술을 제공합니다.
非常非常简单的反作弊,很适合新手入门,完全没任何难度
0x1 整体流程
base.apk里面的 Dalvik字节码 拉起libATG_D.solibATG_D.so解密e8Hk2vi4CH为正常的 Dalvik字节码,然后注入进去,后续执行的是注入进去的 Dalvik字节码- 解密后的
e8Hk2vi4CHDalvik字节码 拉起正常的Unity进程,同时拉起libATG_L.so libATG_L.so根据架构解密拉起ATG_E来进行环境检测,日志上报等等
0x2 libATG_D.so
com/bishopsoft/Presto/SDK/Loader.java 载入 libATG_D.so
public class Loader extends Application // class@00018b from classes.dex
{
private String mAppName;
private Application mDelegate;
private boolean mIsBindReal;
private static ByteBuffer[] Buffers;
private static Context mContext;
static {
System.loadLibrary("ATG_D");
}
System.loadLibrary 会调用 libATG_D.so 的 JNI_OnLoad,随后调用 JNI_OnLoad.54
JNI_OnLoad.54 开头有些垃圾代码狙击IDA的反编译
缓存运行环境信息
找到 Java 类
注册两个 native 方法
Loader.InitBuffer
signature: (Landroid/content/Context;Ljava/lang/String;Ljava/lang/String;Landroid/content/res/AssetManager;)Ljava/lang/Object;
native: 0x79004 -> real body 0x75660
Loader.native_InitLib
signature: (Landroid/content/Context;Landroid/content/res/AssetManager;Z)I
native: 0x77594 -> real body 0x740f8
JNI_OnLoad 返回到 Java,调用 InitBuffer 解密 Dalvik字节码
public native Object InitBuffer(Context p0,String p1,String p2,AssetManager p3);
public void attachBaseContext(Context paramContext){
try{
super.attachBaseContext(paramContext);
AssetManager assets = paramContext.getAssets();
this.native_InitLib(paramContext, assets, false);
Loader.Buffers = this.InitBuffer(paramContext, paramContext.getApplicationInfo().dataDir, paramContext.getApplicationInfo().nativeLibraryDir, assets);
if (Loader.Buffers != null) {
this.loadDex(paramContext);
}
}catch(java.lang.NoSuchFieldException e1){
e1.printStackTrace();
}catch(java.lang.IllegalAccessException e1){
e1.printStackTrace();
}catch(java.lang.NoSuchMethodException e1){
e1.printStackTrace();
}catch(java.lang.reflect.InvocationTargetException e1){
e1.printStackTrace();
}
return;
}
开头依旧狙击IDA反编译
取 e8Hk2vi4CH 的路径
派生密钥
用派生出来的密钥跑AES解密 e8Hk2vi4CH

扫描 embedded Dalvik字节码
生成 + 返回 解密后的buffer
0x3 libATG_L.so
java/stage2/code/com/bishopsoft/Presto/SDK/Presto.java 载入 libATG_L.so
public class Presto extends Application // class@0000ff from classes2.dex
{
private static String EGdataPath;
public static String SDK_version = "2025091801";
static CallBackReLogin callback;
private static int checked_emul = 0;
private static Context mContext;
public static String packageList = "";
private static Presto presto;
public static String rootPath = "";
private static String sData = "";
static boolean scanning = true;
static {
Presto.presto = new Presto();
System.loadLibrary("ATG_L");
}
开头依旧狙击IDA反编译
依旧注册函数查询CPU架构
fork/ptrace 保护+反调试

JNI_OnLoad 返回到 Java 开始 init,最终调用 WorkThread
public void Presto_Init(Context context){
Presto.mContext = context;
Presto.scanning = true;
if (Presto.EGdataPath == null) {
Presto.EGdataPath = Utils.getCPUABI(Presto.mContext);
}
Presto.rootPath = Utils.execByRuntime("pm path "+Presto.mContext.getPackageName());
if (Presto.rootPath != null && !Presto.rootPath.isEmpty()) {
Presto.rootPath = Presto.rootPath.replace("package:", "");
}
Presto.packageList = Utils.execByRuntime("pm list package");
if (Presto.packageList != null && !Presto.packageList.isEmpty()) {
Presto.packageList = Presto.packageList.replaceAll("\npackage:", ",");
}
Log.i("Presto", "SDK_Ver = "+Presto.SDK_version);
Integer[] integerArray = new Integer[0];
new ThreadScan(Presto.mContext, 0).execute(integerArray);
return;
}
public class ThreadScan extends AsyncTask // class@000102 from classes2.dex
{
private Context mContext;
private int option;
private String result;
public static String ResultScan = "";
static {
}
public void ThreadScan(Context context,int _option){
super();
this.mContext = context;
this.option = _option;
}
protected Object doInBackground(Object[] p0){
return this.doInBackground(p0);
}
protected String doInBackground(Integer[] arg0){
int recovery = 1;
this.result = Presto.WorkThread(this.mContext, recovery);
return this.result;
}
}
根据CPU架构选择模块
CpuFamily = android_getCpuFamily();
if ( CpuFamily == ANDROID_CPU_FAMILY_X86_64 )
{
strcpy(&filename[strlen(filename)], "/ATG_E_x86_64.sec");
}
else
{
if ( CpuFamily == ANDROID_CPU_FAMILY_ARM64 )
{
v25 = strlen(filename);
v26 = "/ATG_E_x64.sec";
}
else
{
if ( CpuFamily != ANDROID_CPU_FAMILY_X86 )
{
strcpy(&filename[strlen(filename)], "/ATG_E.sec");
goto LABEL_64;
}
v25 = strlen(filename);
v26 = "/ATG_E_x86.sec";
}
v27 = &filename[v25];
v28 = *v26;
v29 = *(v26 + 7);
*v27 = v28;
*(v27 + 7) = v29;
}
解出临时 ._ATG_B.sec
strcpy(file, g_dataPath);
qmemcpy(v206, "FF8F2A6CDD9B3DB72CD301FB87035E34", sizeof(v206));
strcpy(szHex, "2CD301FB87035E34FF8F2A6CDD9B3DB7");
v30 = strlen(szHex);
v31 = v30;
if ( v30 >= 1 && (v30 & 0xF) == 0 && v30 >= 2 )
{
v32 = v30 >> 1;
v33 = operator new(v32);
memset(v33, 0, v31 >> 1);
v34 = 0;
v35 = szHex;
v36 = v33;
while ( Hex2Char(v35, &s) )
{
v37 = 0xFFFFFFFE - v34 - (~v34 | 0xFFFFFFFE);
v35 += 2;
v34 = (v37 ^ (v34 + 1)) + (v34 | 1) + 2 * (v37 & (v34 + 1));
*v36++ = s.erk[0];
if ( v34 >= v32 )
{
v38 = operator new(v31 >> 1);
memset(v38, 0, v31 >> 1);
*name = WzrBpTHKGekFnEGBsJVCCbpW(char *,int)::iv_key;
aes_set_key(&s, "e38d99fb4434d3d485794c6b34cd5d1fB3B4801D0A7AC103B2BC08C10BC017E6", 0x100);
v39 = 0;
do
{
v40 = &v33[v39];
aes_decrypt(&s, v40, &v38[v39]);
v41 = 0;
do
{
v42 = name[v41];
v43 = ((v41 + v39) ^ -(v41 | v39)) + (v41 | v39) + 2 * ((v41 + v39) & -(v41 | v39));
v44 = v38[v43];
v45 = v44 | v42;
v46 = -(((v44 + v42) ^ -v45) + 2 * ((v44 + v42) & -v45));
v47 = 0xFFFFFFFE - v41 - (~v41 | 0xFFFFFFFE);
v41 = (v47 ^ (v41 + 1)) + (v41 | 1) + 2 * (v47 & (v41 + 1));
v38[v43] = (v46 ^ v45) + 2 * (v46 & v45);
}
while ( v41 < 0x10 );
v39 = (v39 | 0x10) + ~v39 + (v39 | 1) + (v39 & 1) + ((0xFFFFFFEF - v39 - (~v39 | 0xFFFFFFEF)) ^ (v39 + 0x10)) + 2 * ((0xFFFFFFEF - v39 - (~v39 | 0xFFFFFFEF)) & (v39 + 0x10));
*name = *v40;
}
while ( v39 < v32 );
memset(&szHex[v32], 0, v31 - v32);
memcpy(szHex, v38, v31 >> 1);
memset(v33, 0, v31 >> 1);
operator delete(v38);
break;
}
}
operator delete(v33);
}
strcat(file, szHex);
v48 = fopen(filename, "rb");
v49 = fopen(file, "wb");
if ( v48 )
{
v50 = v49;
fseek(v48, 0xFFFFFFFFFFFFFFFCLL, 2);
v51 = ftell(v48);
fread(&ptr, 1u, 8u, v48);
v52 = ptr;
v53 = v51 - v52 + ((v52 - 1) | ~v51);
v54 = ((v53 + 1) ^ (v52 - v51 + (v51 | -v52))) + 2 * (((v53 + 1) & (v52 - v51 + (v51 | -v52))) + (v53 ^ ~(v51 - ptr)) + 2 * ((v51 - ptr) & ~v53));
v55 = calloc(v54, 1u);
v56 = calloc(v51, 1u);
if ( v55 )
{
v57 = v56;
if ( v56 )
{
fseek(v48, 0, 0);
fread(v57, 1u, v51, v48);
fseek(v48, v52, 0);
fread(v55, 1u, v54, v48);
fclose(v48);
if ( v54 >= 1 )
{
v58 = 0;
for ( i = 0; i < v54; v58 = i )
{
v60 = v55[v58];
v61 = v57[v58];
v62 = v61 | v60;
v63 = -(((v61 + v60) ^ -v62) + 2 * ((v61 + v60) & -v62));
v64 = 0xFFFFFFFE - i - (~i | 0xFFFFFFFE);
i = (v64 ^ (i + 1)) + (i | 1) + 2 * (v64 & (i + 1));
v55[v58] = (v63 ^ v62) + 2 * (v63 & v62);
}
}
fwrite(v55, 1u, v54, v50);
fclose(v50);
free(v55);
free(v57);
}
}
}
完整性检查
v277 = 0;
v276 = 0u;
v275 = 0u;
v274 = 0u;
v273 = 0u;
v272 = 0u;
v271 = 0u;
v270 = 0u;
v269 = 0u;
v268 = 0u;
v267 = 0u;
v266 = 0u;
v265 = 0u;
v264 = 0u;
v263 = 0u;
*v262 = 0u;
*filename = 0u;
memcpy(name, "B3B4801D0A7AC103B2BC08C10BC017E6", 0x104u);
strcpy(file, g_dataPath);
szHex[0x20] = 0;
memset(v243, 0, sizeof(v243));
v242 = 0u;
v241 = 0u;
v240 = 0u;
v239 = 0u;
v238 = 0u;
v237 = 0u;
v236 = 0u;
v235 = 0u;
v234 = 0u;
v233 = 0u;
v232 = 0u;
v231 = 0u;
v230 = 0u;
*szHex = v206[1];
*&szHex[0x10] = v206[0];
v65 = strlen(szHex);
v66 = v65;
v67 = v209;
if ( v65 >= 1 && (v65 & 0xF) == 0 && v65 >= 2 )
{
v68 = v65 >> 1;
v69 = operator new(v68);
memset(v69, 0, v66 >> 1);
v70 = 0;
v71 = szHex;
v72 = v69;
while ( Hex2Char(v71, &s) )
{
v73 = 0xFFFFFFFE - v70 - (~v70 | 0xFFFFFFFE);
v71 += 2;
v70 = (v73 ^ (v70 + 1)) + (v70 | 1) + 2 * (v73 & (v70 + 1));
*v72++ = s.erk[0];
if ( v70 >= v68 )
{
v74 = operator new(v66 >> 1);
memset(v74, 0, v66 >> 1);
ptr = WzrBpTHKGekFnEGBsJVCCbpW(char *,int)::iv_key;
aes_set_key(&s, "e38d99fb4434d3d485794c6b34cd5d1fB3B4801D0A7AC103B2BC08C10BC017E6", 0x100);
v75 = 0;
do
{
v76 = &v69[v75];
aes_decrypt(&s, v76, &v74[v75]);
v77 = 0;
do
{
v78 = *(&ptr + v77);
v79 = ((v77 + v75) ^ -(v77 | v75)) + (v77 | v75) + 2 * ((v77 + v75) & -(v77 | v75));
v80 = v74[v79];
v81 = v80 | v78;
v82 = -(((v80 + v78) ^ -v81) + 2 * ((v80 + v78) & -v81));
v83 = 0xFFFFFFFE - v77 - (~v77 | 0xFFFFFFFE);
v77 = (v83 ^ (v77 + 1)) + (v77 | 1) + 2 * (v83 & (v77 + 1));
v74[v79] = (v82 ^ v81) + 2 * (v82 & v81);
}
while ( v77 < 0x10 );
v75 = (v75 | 0x10) + ~v75 + (v75 | 1) + (v75 & 1) + ((0xFFFFFFEF - v75 - (~v75 | 0xFFFFFFEF)) ^ (v75 + 0x10)) + 2 * ((0xFFFFFFEF - v75 - (~v75 | 0xFFFFFFEF)) & (v75 + 0x10));
ptr = *v76;
}
while ( v75 < v68 );
memset(&szHex[v68], 0, v66 - v68);
memcpy(szHex, v74, v66 >> 1);
memset(v69, 0, v66 >> 1);
operator delete(v74);
break;
}
}
operator delete(v69);
}
strcat(file, szHex);
v84 = dlopen(file, 1);
v85 = obja;
if ( v84 )
{
v86 = v84;
v87 = strlen(name);
v88 = v87;
if ( v87 >= 1 && (v87 & 0xF) == 0 && v87 >= 2 )
{
v89 = v87 >> 1;
v90 = operator new(v89);
memset(v90, 0, v88 >> 1);
v91 = 0;
v92 = name;
v93 = v90;
while ( Hex2Char(v92, &s) )
{
v94 = 0xFFFFFFFE - v91 - (~v91 | 0xFFFFFFFE);
v92 += 2;
v91 = (v94 ^ (v91 + 1)) + (v91 | 1) + 2 * (v94 & (v91 + 1));
*v93++ = s.erk[0];
if ( v91 >= v89 )
{
v95 = operator new(v88 >> 1);
memset(v95, 0, v88 >> 1);
ptr = WzrBpTHKGekFnEGBsJVCCbpW(char *,int)::iv_key;
aes_set_key(&s, "e38d99fb4434d3d485794c6b34cd5d1fB3B4801D0A7AC103B2BC08C10BC017E6", 0x100);
v96 = 0;
do
{
v97 = &v90[v96];
aes_decrypt(&s, v97, &v95[v96]);
v98 = 0;
do
{
v99 = *(&ptr + v98);
v100 = ((v98 + v96) ^ -(v98 | v96)) + (v98 | v96) + 2 * ((v98 + v96) & -(v98 | v96));
v101 = v95[v100];
v102 = v101 | v99;
v103 = -(((v101 + v99) ^ -v102) + 2 * ((v101 + v99) & -v102));
v104 = 0xFFFFFFFE - v98 - (~v98 | 0xFFFFFFFE);
v98 = (v104 ^ (v98 + 1)) + (v98 | 1) + 2 * (v104 & (v98 + 1));
v95[v100] = (v103 ^ v102) + 2 * (v103 & v102);
}
while ( v98 < 0x10 );
v96 = (v96 | 0x10) + ~v96 + (v96 | 1) + (v96 & 1) + ((0xFFFFFFEF - v96 - (~v96 | 0xFFFFFFEF)) ^ (v96 + 0x10)) + 2 * ((0xFFFFFFEF - v96 - (~v96 | 0xFFFFFFEF)) & (v96 + 0x10));
ptr = *v97;
}
while ( v96 < v89 );
memset(&name[v89], 0, v88 - v89);
memcpy(name, v95, v88 >> 1);
memset(v90, 0, v88 >> 1);
operator delete(v95);
v67 = v209;
break;
}
}
operator delete(v90);
v85 = obja;
}
启动模块
LABEL_249:
g_result[0] = 0;
memset(&s, 0, 0x104);
if ( p_result )
{
free(p_result);
p_result = nullptr;
}
strcpy(&s, g_dataPath);
*(s.erk + strlen(&s)) = 0x2F;
if ( family == ANDROID_CPU_FAMILY_X86_64 )
{
strcpy(&s + strlen(&s), "ATG_E_x86_64.sec");
}
else
{
if ( family == ANDROID_CPU_FAMILY_ARM64 )
{
v152 = strlen(&s);
v153 = "ATG_E_x64.sec";
}
else
{
if ( family != ANDROID_CPU_FAMILY_X86 )
{
strcpy(&s + strlen(&s), "ATG_E.sec");
goto LABEL_259;
}
v152 = strlen(&s);
v153 = "ATG_E_x86.sec";
}
v154 = (s.erk + v152);
v155 = *v153;
v156 = *(v153 + 6);
*v154 = v155;
*(v154 + 6) = v156;
}
LABEL_259:
if ( g_Ehandle || (g_Ehandle = dlopen(&s, 1)) != nullptr )
{
unlink(&s);
if ( g_isScanning )
return v67->functions->NewStringUTF(v67, g_result);
v157 = dlsym(g_Ehandle, "WorkThread");
if ( v157 )
{
g_isScanning = 1;
m_infect_cnt = v157(v67, v85, m_option);
if ( (m_infect_cnt & 0x80000000) == 0 )
return v67->functions->NewStringUTF(v67, g_result);
memset(&s, 0, 0x104);
v158 = operator new(0xF0u);
v262[0] = v158;
虚拟机检测(非常全的特征,可以抄过来用
)
- Build.BRAND == “generic”
- Build.BRAND == “sdk”
- Build.BRAND == “Microvirt”
- Build.BRAND == “AMIDuOS”
- Build.BRAND == “TTVM”
- Build.MODEL == “AMIDuOS”
- Build.MODEL == “Memu”
- Build.MODEL == “TiantianVM”
- Build.MODEL == “Droid4X”
- Build.MODEL == “vmos”
- Build.HARDWARE == “andy”
- Build.HARDWARE == “vbox86”
- Build.HARDWARE == “nox”
- Build.HARDWARE == “windroye”
- Build.HARDWARE == “goldfish”
- Build.HARDWARE == “ttVM_x86”
- Build.HARDWARE == “android_x86”
- Build.HARDWARE == “android_x86_64”
- Build.BOOTLOADER == “nox”
- 文件存在 /system/bin/droid4x
- 文件存在 /system/bin/droid4x-prop
- 文件存在 /system/bin/androVM-prop
- 文件存在 /system/bin/androVM-vbox-sf
- 文件存在 /fstab.intel
- 文件存在 /fstab.vbox86
- 文件存在 /fstab.vbox64
- 文件存在 /fstab.android_x86
- 文件存在 /fstab.android_x86_64
- 文件存在 /system/app/EmuCoreService/EmuCoreService.apk
- 文件存在 /system/app/EmuInputService/EmuInputService.apk
- 文件存在 /system/app/gpLogin/gpLogin.apk
- 文件存在 /system/app/gpLogin/gpLogin_new.apk
- 文件存在 /system/app/Helper/helper.apk
- 文件存在 /system/app/Helper/NoxHelp_en.apk
- 文件存在 /system/app/Helper/NoxHelp_en_new.apk
- 文件存在 /bin/nox-vbox-sf
- 文件存在 /bin/noxd
- 文件存在 /data/app/com.android.ld.appstore-1/base.apk
- 文件存在 /data/app/com.android.ld.appstore-2/base.apk
- 文件存在 /system/app/Launcher3/Launcher3.apk
- 文件存在 /system/priv-app/LDAppStore/LDAppStore.apk
- 文件存在 /data/user_de/0/com.android.flysilkworm
- 文件存在 /data/misc/profiles/ref/com.android.flysilkworm
- 目录可打开 /mnt/windows/BstSharedFolder
- 文件存在 system/app/IME/IME.apk
- 上一条 APK 的包名等于 com.microvirt.memuime
- 文件存在 /system/app/MuMuAudio/MuMuAudio.apk
- 文件存在 /system/app/com.mumu.store/com.mumu.store.apk
- 文件存在 /system/priv-app/MuMuAudio/MuMuAudio.apk
- 文件存在 /system/priv-app/com.mumu.store_overseas/com.mumu.store_overseas.apk
- 文件存在 /system/app/KiwiIntentSink/KiwiIntentSink.apk
0x3 libATG_E.so
WorkThread 入口塞了一坨特征码

然后启动了两个线程
iCEcaaLIRKlGfNDwLLDLVlwO 还是做虚拟机检测,增加了下面的特征:
- com.gspace.android
- com.excean.gspace
- com.excean.splay
- com.excean.parallelspace
- io.va.exposed
- parallel.space
- com.ludashi.dualspace
- com.ludashi.superboost
- com.app.hider.master.dual.app
- com.app.hider.master.pro
- com.hidespps.apphider
- com.app.calculator.vault.hider
- com.excelliance.multiaccount
- multi.parallel.dualspace.cloner
- do.multiple.cloner
- com.lulu.luluboxpro
- com.cloneapp.parallelspace.dualspace
- com.pan.parallelspace
- com.dualspace.multispace.android
- com.pengyou.cloneapp
做crc32校验
初始化 libData.so,解出来是作弊工具的名称
AlphaGameBooster
CheatEngine
Freedom
Freedom
Freedom
Freedom
Freedom
Freedom
Freedom
Freedom
Freedom
GameCheater
GameCheater
GameCIH
GameGuardian
GameGuardian
GameGuardian
GameGuardian
GameGuardian
GameGuardian
GameHackerSpeed
GameHackerSpeed
GameHacker
GameHacker
GameHacker
GameHacker
GameHacker
GameHacker
GameHacker
GameKiller
GameKiller
GameKiller
GameKiller
GameKiller
GameKiller
GameMaster2
GameMaster2
GameMaster
GameMaster_opda
GameMaster_opda
GameMaster_opda
GameMaster_opda
GGAssistant
HoistMan
HoistMan
HoistMan
HoistMan
Huang
Huang
Huang
Huang
igamecool
igamecool
LuckyPatcher
MemSpector
MemSpector
MuzhiwanGamehelper
MuzhiwanGamehelper
PacketSniffer
PacketSniffer
RootCloakPlus
RootCloakPlus
RootCloak
RootCloak
RootCloak
RootCloak
SlashGameBuster
SlashGameBuster
SlashGameBuster
TcgameGamecheater
Touch18
Touch18
WoodPecker
Xiaojianjian
Xiaojianjian
xxAssistant
xxAssistant
xxAssistant
xxAssistant
Youxia
Youxia
Zhangkongapp
Zhangkongapp
Zhangkongapp
Zhangkongapp
SHBUoTIkyKmlbYGMbbWeIYeB只负责轮询配置
0x4 global-metadata.dat
读进内存变换一下文件头,做一下xor完事
0xF
反作弊力度和LIAPP坐一桌,CrackProof比这玩意稍强
libunity.so和libil2cpp.so不加壳,没有云下发,甚至安全模块包含完整的DWARF

2 个帖子 - 2 位参与者