首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Android安全
发新帖
28
6
[原创]《安卓逆向这档事》六、校验的N次方-签名校验对抗、PM代理、IO重定向
发表于: 2023-7-30 22:13
15232
[原创]《安卓逆向这档事》六、校验的N次方-签名校验对抗、PM代理、IO重定向
正己
2023-7-30 22:13
15232
 # 一、课程目标 --- 1.了解APK文件签名 2.了解APK常见校验及校验对抗方法 3.了解PM代{过}{滤}理和IO重定向 # 二、工具 --- 1.教程Demo(更新) 2.MT管理器/NP管理器 3.雷电模拟器 4.Jadx-gui(第三课课件里有) 5.算法助手(第四课课件里有) # 三、课程内容 --- ## 1.什么是校验 是开发者在数据传送时采用的一种校正数据的一种方式 常见的校验有:签名校验(最常见)、dexcrc校验、apk完整性校验、路径文件校验等 ## 2.什么是APK签名 通过对 Apk 进行签名,开发者可以证明对 Apk 的所有权和控制权,可用于安装和更新其应用。而在 Android 设备上的安装 Apk ,如果是一个没有被签名的 Apk,则会被拒绝安装。在安装 Apk 的时候,软件包管理器也会验证 Apk 是否已经被正确签名,并且通过签名证书和数据摘要验证是否合法没有被篡改。只有确认安全无篡改的情况下,才允许安装在设备上。 简单来说,APK 的签名主要作用有两个: 1. 证明 APK 的所有者。 2. 允许 Android 市场和设备校验 APK 的正确性。 Android 目前支持以下四种应用签名方案: v1 方案:基于 JAR 签名。 v2 方案:APK 签名方案 v2(在 Android 7.0 中引入) v3 方案:APK 签名方案 v3(在 Android 9 中引入) v4 方案:APK 签名方案 v4(在 Android 11 中引入) V1 签名的机制主要就在 META-INF 目录下的三个文件,MANIFEST.MF,ANDROID.SF,ANDROID.RSA,他们都是 V1 签名的产物。 (1)MANIFEST.MF:这是摘要文件。程序遍历Apk包中的所有文件(entry),对非文件夹非签名文件的文件,逐个用SHA1(安全哈希算法)生成摘要信息,再用Base64进行编码。如果你改变了apk包中的文件,那么在apk安装校验时,改变后的文件摘要信息与MANIFEST.MF的检验信息不同,于是程序就不能成功安装。  (2)ANDROID.SF:这是对摘要的签名文件。对前一步生成的MANIFEST.MF,使用SHA1-RSA算法,用开发者的私钥进行签名。在安装时只能使用公钥才能解密它。解密之后,将它与未加密的摘要信息(即,MANIFEST.MF文件)进行对比,如果相符,则表明内容没有被异常修改。  (3)ANDROID.RSA文件中保存了公钥、所采用的加密算法等信息。  在某些情况下,直接对apk进行v1签名可以绕过apk的签名校验 v2方案会将 APK 文件视为 blob,并对整个文件进行签名检查。对 APK 进行的任何修改(包括对 ZIP 元数据进行的修改)都会使 APK 签名作废。这种形式的 APK 验证不仅速度要快得多,而且能够发现更多种未经授权的修改。 ## 3.什么是签名校验 如何判断是否有签名校验? 不做任何修改,直接签名安装,应用闪退则说明大概率有签名校验 一般来说,普通的签名校验会导致软件的闪退,黑屏,卡启动页等 当然,以上都算是比较好的,有一些比较狠的作者,则会直接rm -rf /,把基带都格掉的一键变砖。 ``` kill/killProcess-----kill/KillProcess()可以杀死当前应用活动的进程,这一操作将会把所有该进程内的资源(包括线程全部清理掉).当然,由于ActivityManager时刻监听着进程,一旦发现进程被非正常Kill,它将会试图去重启这个进程。这就是为什么,有时候当我们试图这样去结束掉应用时,发现它又自动重新启动的原因. system.exit-----杀死了整个进程,这时候活动所占的资源也会被释放。 finish----------仅仅针对Activity,当调用finish()时,只是将活动推向后台,并没有立即释放内存,活动的资源并没有被清理 ``` 在我个人见过最恶心的签名校验中,当属三角校验(低调大佬教的)最烦人。 所谓三角校验,就是so检测dex,动态加载的dex(在软件运行时会解压释放一段dex文件,检测完后就删除)检测so,dex检测动态加载的dex  普通获取签名校验代码: ```java private boolean SignCheck() { String trueSignMD5 = "d0add9987c7c84aeb7198c3ff26ca152"; String nowSignMD5 = ""; try { // 得到签名的MD5 PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(),PackageManager.GET_SIGNATURES); Signature[] signs = packageInfo.signatures; String signBase64 = Base64Util.encodeToString(signs[0].toByteArray()); nowSignMD5 = MD5Utils.MD5(signBase64); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } return trueSignMD5.equals(nowSignMD5); } ``` 系统将应用的签名信息封装在 PackageInfo 中,调用 PackageManager 的 getPackageInfo(String packageName, int flags) 即可获取指定包名的签名信息 ## 4.签名校验对抗 方法一:核心破解插件,不签名安装应用 方法二:一键过签名工具,例如MT、NP、ARMPro、CNFIX、Modex的去除签名校验功能 方法三:具体分析签名校验逻辑(手撕签名校验) 方法四:io重定向--VA&SVC:ptrace+seccomp [SVC的TraceHook沙箱的实现&无痕Hook实现思路](https://bbs.pediy.com/thread-273160.htm) 方法五:去作者家严刑拷打拿到.jks文件和密码 ## 5.手动实现PM代{过}{滤}理 ### 1.什么是PMS 思路源自:<a href="elink@3bfK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6X3L8%4g2J5j5Y4u0G2N6r3S2W2M7W2)9J5c8V1S2G2L8$3E0b7L8i4y4e0K9h3N6F1j5i4c8#2M7X3f1`.">Android中Hook 应用签名方法</a> PackageManagerService(简称PMS),是Android系统核心服务之一,处理包管理相关的工作,常见的比如安装、卸载应用等。 ### 2.实现方法以及原理解析 HOOK PMS代码: ```java package com.zj.hookpms; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import android.content.Context; import android.content.pm.PackageManager; import android.util.Log; public class ServiceManagerWraper { public final static String ZJ = "ZJ595"; public static void hookPMS(Context context, String signed, String appPkgName, int hashCode) { try { // 获取全局的ActivityThread对象 Class<?> activityThreadClass = Class.forName("android.app.ActivityThread"); Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread"); Object currentActivityThread = currentActivityThreadMethod.invoke(null); // 获取ActivityThread里面原始的sPackageManager Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager"); sPackageManagerField.setAccessible(true); Object sPackageManager = sPackageManagerField.get(currentActivityThread); // 准备好代{过}{滤}理对象, 用来替换原始的对象 Class<?> iPackageManagerInterface = Class.forName("android.content.pm.IPackageManager"); Object proxy = Proxy.newProxyInstance( iPackageManagerInterface.getClassLoader(), new Class<?>[]{iPackageManagerInterface}, new PmsHookBinderInvocationHandler(sPackageManager, signed, appPkgName, 0)); // 1. 替换掉ActivityThread里面的 sPackageManager 字段 sPackageManagerField.set(currentActivityThread, proxy); // 2. 替换 ApplicationPackageManager里面的 mPM对象 PackageManager pm = context.getPackageManager(); Field mPmField = pm.getClass().getDeclaredField("mPM"); mPmField.setAccessible(true); mPmField.set(pm, proxy); } catch (Exception e) { Log.d(ZJ, "hook pms error:" + Log.getStackTraceString(e)); } } public static void hookPMS(Context context) { String Sign = "原包的签名信息"; hookPMS(context, Sign, "com.zj.hookpms", 0); } } ``` ActivityThread的静态变量sPackageManager ApplicationPackageManager对象里面的mPM变量 ## 6.IO重定向 什么是IO重定向? 例:在读A文件的时候指向B文件 <a href="elink@be1K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6$3K9i4u0B7j5i4u0d9j5i4c8W2L8q4)9J5c8Y4u0S2N6r3g2D9i4K6u0V1j5$3!0J5k6b7`.`.">平头哥的核心代码</a> <a href="elink@6fdK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6S2M7@1I4G2k6s2W2Q4x3V1k6h3K9i4u0@1N6h3q4D9b7i4m8H3">Virtual Engine for Android(Support 12.0 in business version)</a> IO重定向可以干嘛? 1,可以让文件只读,不可写 2,禁止访问文件 3,路径替换 具体实现: 过签名检测(读取原包) 风控对抗(例:一个文件记录App启动的次数) 过Root检测,Xposed检测(文件不可取) ``` using namespace std; string packname; string origpath; string fakepath; int (*orig_open)(const char *pathname, int flags, ...); int (*orig_openat)(int,const char *pathname, int flags, ...); FILE *(*orig_fopen)(const char *filename, const char *mode); static long (*orig_syscall)(long number, ...); int (*orig__NR_openat)(int,const char *pathname, int flags, ...); void* (*orig_dlopen_CI)(const char *filename, int flag); void* (*orig_dlopen_CIV)(const char *filename, int flag, const void *extinfo); void* (*orig_dlopen_CIVV)(const char *name, int flags, const void *extinfo, void *caller_addr); static inline bool needs_mode(int flags) { return ((flags & O_CREAT) == O_CREAT) || ((flags & O_TMPFILE) == O_TMPFILE); } bool startsWith(string str, string sub){ return str.find(sub)==0; } bool endsWith(string s,string sub){ return s.rfind(sub)==(s.length()-sub.length()); } bool isOrigAPK(string path){ if(path==origpath){ return true; } return false; } //该函数的功能是在打开一个文件时进行拦截,并在满足特定条件时将文件路径替换为另一个路径 //fake_open 函数有三个参数: //pathname:一个字符串,表示要打开的文件的路径。 //flags:一个整数,表示打开文件的方式,例如只读、只写、读写等。 //mode(可选参数):一个整数,表示打开文件时应用的权限模式。 int fake_open(const char *pathname, int flags, ...) { mode_t mode = 0; if (needs_mode(flags)) { va_list args; va_start(args, flags); mode = static_cast<mode_t>(va_arg(args, int)); va_end(args); } //LOGI("open, path: %s, flags: %d, mode: %d",pathname, flags ,mode); string cpp_path= pathname; if(isOrigAPK(cpp_path)){ LOGI("libc_open, redirect: %s, --->: %s",pathname, fakepath.data()); return orig_open("/data/user/0/com.zj.wuaipojie/files/base.apk", flags, mode); } return orig_open(pathname, flags, mode); } //该函数的功能是在打开一个文件时进行拦截,并在满足特定条件时将文件路径替换为另一个路径 //fake_openat 函数有四个参数: //fd:一个整数,表示要打开的文件的文件描述符。 //pathname:一个字符串,表示要打开的文件的路径。 //flags:一个整数,表示打开文件的方式,例如只读、只写、读写等。 //mode(可选参数):一个整数,表示打开文件时应用的权限模式。 //openat 函数的作用类似于 open 函数,但是它使用文件描述符来指定文件路径,而不是使用文件路径本身。这样,就可以在打开文件时使用相对路径,而不必提供完整的文件路径。 //例如,如果要打开相对于当前目录的文件,可以使用 openat 函数,而不是 open 函数,因为 open 函数只能使用绝对路径。 // int fake_openat(int fd, const char *pathname, int flags, ...) { mode_t mode = 0; if (needs_mode(flags)) { va_list args; va_start(args, flags); mode = static_cast<mode_t>(va_arg(args, int)); va_end(args); } LOGI("openat, fd: %d, path: %s, flags: %d, mode: %d",fd ,pathname, flags ,mode); string cpp_path= pathname; if(isOrigAPK(cpp_path)){ LOGI("libc_openat, redirect: %s, --->: %s",pathname, fakepath.data()); return orig_openat(fd,fakepath.data(), flags, mode); } return orig_openat(fd,pathname, flags, mode); } FILE *fake_fopen(const char *filename, const char *mode) { string cpp_path= filename; if(isOrigAPK(cpp_path)){ return orig_fopen(fakepath.data(), mode); } return orig_fopen(filename, mode); } //该函数的功能是在执行系统调用时进行拦截,并在满足特定条件时修改系统调用的参数。 //syscall 函数是一个系统调用,是程序访问内核功能的方法之一。使用 syscall 函数可以调用大量的系统调用,它们用于实现操作系统的各种功能,例如打开文件、创建进程、分配内存等。 // static long fake_syscall(long number, ...) { void *arg[7]; va_list list; va_start(list, number); for (int i = 0; i < 7; ++i) { arg[i] = va_arg(list, void *); } va_end(list); if (number == __NR_openat){ const char *cpp_path = static_cast<const char *>(arg[1]); LOGI("syscall __NR_openat, fd: %d, path: %s, flags: %d, mode: %d",arg[0] ,arg[1], arg[2], arg[3]); if (isOrigAPK(cpp_path)){ LOGI("syscall __NR_openat, redirect: %s, --->: %s",arg[1], fakepath.data()); return orig_syscall(number,arg[0], fakepath.data() ,arg[2],arg[3]); } } return orig_syscall(number, arg[0], arg[1], arg[2], arg[3], arg[4], arg[5], arg[6]); } //函数的功能是获取当前应用的包名、APK 文件路径以及库文件路径,并将这些信息保存在全局变量中 //函数调用 GetObjectClass 和 GetMethodID 函数来获取 context 对象的类型以及 getPackageName 方法的 ID。然后,函数调用 CallObjectMethod 函数来调用 getPackageName 方法,获取当前应用的包名。最后,函数使用 GetStringUTFChars 函数将包名转换为 C 字符串,并将包名保存在 packname 全局变量中 //接着,函数使用 fakepath 全局变量保存了 /data/user/0/<packname>/files/base.apk 这样的路径,其中 <packname> 是当前应用的包名。 //然后,函数再次调用 GetObjectClass 和 GetMethodID 函数来获取 context 对象的类型以及 getApplicationInfo 方法的 ID。然后,函数调用 CallObjectMethod 函数来调用 getApplicationInfo 方法,获取当前应用的 ApplicationInfo 对象。 //它先调用 GetObjectClass 函数获取 ApplicationInfo 对象的类型,然后调用 GetFieldID 函数获取 sourceDir 字段的 ID。接着,函数使用 GetObjectField 函数获取 sourceDir 字段的值,并使用 GetStringUTFChars 函数将其转换为 C 字符串。最后,函数将 C 字符串保存在 origpath 全局变量中,表示当前应用的 APK 文件路径。 //最后,函数使用 GetFieldID 和 GetObjectField 函数获取 nativeLibraryDir 字段的值,并使用 GetStringUTFChars 函数将其转换为 C 字符串。函数最后调用 LOGI 函数打印库文件路径,但是并没有将其保存在全局变量中。 extern "C" JNIEXPORT void JNICALL Java_com_zj_wuaipojie_util_SecurityUtil_hook(JNIEnv *env, jclass clazz, jobject context) { jclass conext_class = env->GetObjectClass(context); jmethodID methodId_pack = env->GetMethodID(conext_class, "getPackageName", "()Ljava/lang/String;"); auto packname_js = reinterpret_cast<jstring>(env->CallObjectMethod(context, methodId_pack)); const char *pn = env->GetStringUTFChars(packname_js, 0); packname = string(pn); env->ReleaseStringUTFChars(packname_js, pn); //LOGI("packname: %s", packname.data()); fakepath= "/data/user/0/"+ packname +"/files/base.apk"; jclass conext_class2 = env->GetObjectClass(context); jmethodID methodId_pack2 = env->GetMethodID(conext_class2,"getApplicationInfo","()Landroid/content/pm/ApplicationInfo;"); jobject application_info = env->CallObjectMethod(context,methodId_pack2); jclass pm_clazz = env->GetObjectClass(application_info); jfieldID package_info_id = env->GetFieldID(pm_clazz,"sourceDir","Ljava/lang/String;"); auto sourceDir_js = reinterpret_cast<jstring>(env->GetObjectField(application_info,package_info_id)); const char *sourceDir = env->GetStringUTFChars(sourceDir_js, 0); origpath = string(sourceDir); LOGI("sourceDir: %s", sourceDir); jfieldID package_info_id2 = env->GetFieldID(pm_clazz,"nativeLibraryDir","Ljava/lang/String;"); auto nativeLibraryDir_js = reinterpret_cast<jstring>(env->GetObjectField(application_info,package_info_id2)); const char *nativeLibraryDir = env->GetStringUTFChars(nativeLibraryDir_js, 0); LOGI("nativeLibraryDir: %s", nativeLibraryDir); //LOGI("%s", "Start Hook"); //启动hook void *handle = dlopen("libc.so",RTLD_NOW); auto pagesize = sysconf(_SC_PAGE_SIZE); auto addr = ((uintptr_t)dlsym(handle,"open") & (-pagesize)); auto addr2 = ((uintptr_t)dlsym(handle,"openat") & (-pagesize)); auto addr3 = ((uintptr_t)fopen) & (-pagesize); auto addr4 = ((uintptr_t)syscall) & (-pagesize); //解除部分机型open被保护 mprotect((void*)addr, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC); mprotect((void*)addr2, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC); mprotect((void*)addr3, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC); mprotect((void*)addr4, pagesize, PROT_READ | PROT_WRITE | PROT_EXEC); DobbyHook((void *)dlsym(handle,"open"), (void *)fake_open, (void **)&orig_open); DobbyHook((void *)dlsym(handle,"openat"), (void *)fake_openat, (void **)&orig_openat); DobbyHook((void *)fopen, (void *)fake_fopen, (void**)&orig_fopen); DobbyHook((void *)syscall, (void *)fake_syscall, (void **)&orig_syscall); } ``` ``` sget-object p10, Lcom/zj/wuaipojie/util/ContextUtils;->INSTANCE:Lcom/zj/wuaipojie/util/ContextUtils; invoke-virtual {p10}, Lcom/zj/wuaipojie/util/ContextUtils;->getContext()Landroid/content/Context; move-result-object p10 invoke-static {p10}, Lcom/zj/wuaipojie/util/SecurityUtil;->hook(Landroid/content/Context;)V ``` ## 7.其他常见校验 ### root检测: 反制手段 1.算法助手、对话框取消等插件一键hook 2.分析具体的检测代码 3.利用IO重定向使文件不可读 4.修改Andoird源码,去除常见指纹 ``` fun isDeviceRooted(): Boolean { return checkRootMethod1() || checkRootMethod2() || checkRootMethod3() } fun checkRootMethod1(): Boolean { val buildTags = android.os.Build.TAGS return buildTags != null && buildTags.contains("test-keys") } fun checkRootMethod2(): Boolean { val paths = arrayOf("/system/app/Superuser.apk", "/sbin/su", "/system/bin/su", "/system/xbin/su", "/data/local/xbin/su", "/data/local/bin/su", "/system/sd/xbin/su", "/system/bin/failsafe/su", "/data/local/su", "/su/bin/su") for (path in paths) { if (File(path).exists()) return true } return false } fun checkRootMethod3(): Boolean { var process: Process? = null return try { process = Runtime.getRuntime().exec(arrayOf("/system/xbin/which", "su")) val bufferedReader = BufferedReader(InputStreamReader(process.inputStream)) bufferedReader.readLine() != null } catch (t: Throwable) { false } finally { process?.destroy() } } ``` 定义了一个 `isDeviceRooted()` 函数,该函数调用了三个检测 root 的方法:`checkRootMethod1()`、`checkRootMethod2()` 和 `checkRootMethod3()`。 `checkRootMethod1()` 方法检查设备的 `build tags` 是否包含 `test-keys`。这通常是用于测试的设备,因此如果检测到这个标记,则可以认为设备已被 root。 `checkRootMethod2()` 方法检查设备是否存在一些特定的文件,这些文件通常被用于执行 root 操作。如果检测到这些文件,则可以认为设备已被 root。 `checkRootMethod3()` 方法使用 `Runtime.exec()` 方法来执行 `which su` 命令,然后检查命令的输出是否不为空。如果输出不为空,则可以认为设备已被 root。 ### 模拟器检测 ``` fun isEmulator(): Boolean { return Build.FINGERPRINT.startsWith("generic") || Build.FINGERPRINT.startsWith("unknown") || Build.MODEL.contains("google_sdk") Build.MODEL.contains("Emulator") || Build.MODEL.contains("Android SDK built for x86") || Build.MANUFACTURER.contains("Genymotion") || Build.HOST.startsWith("Build") || Build.PRODUCT == "google_sdk" } ``` 通过检测系统的 `Build` 对象来判断当前设备是否为模拟器。具体方法是检测 `Build.FINGERPRINT` 属性是否包含字符串 `"generic"`。 <a href="elink@2f6K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6A6L8$3&6A6P5X3g2V1i4K6u0V1j5X3q4Y4i4K6u0V1k6o6M7H3i4K6u0W2L8X3!0@1K9h3!0F1i4K6u0W2M7$3W2@1k6g2)9J5c8U0l9@1k6r3u0S2k6U0x3&6x3o6V1I4k6U0b7J5y4e0p5&6j5U0p5@1k6r3g2U0k6o6u0S2z5o6N6X3k6r3f1%4">模拟器检测对抗</a> ### 反调试检测 安卓系统自带调试检测函数 ``` fun checkForDebugger() { if (Debug.isDebuggerConnected()) { // 如果调试器已连接,则终止应用程序 System.exit(0) } } ``` debuggable属性 ``` public boolean getAppCanDebug(Context context)//上下文对象为xxActivity.this { boolean isDebug = context.getApplicationInfo() != null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; return isDebug; } ``` ptrace检测 ```cpp int ptrace_protect()//ptrace附加自身线程 会导致此进程TracerPid 变为父进程的TracerPid 即zygote { return ptrace(PTRACE_TRACEME,0,0,0);;//返回-1即为已经被调试 } ``` 每个进程同时刻只能被1个调试进程ptrace ,主动ptrace本进程可以使得其他调试器无法调试 调试进程名检测 ```cpp int SearchObjProcess() { FILE* pfile=NULL; char buf[0x1000]={0}; pfile=popen("ps","r"); if(NULL==pfile) { //LOGA("SearchObjProcess popen打开命令失败!\n"); return -1; } // 获取结果 //LOGA("popen方案:\n"); while(fgets(buf,sizeof(buf),pfile)) { char* strA=NULL; char* strB=NULL; char* strC=NULL; char* strD=NULL; strA=strstr(buf,"android_server");//通过查找匹配子串判断 strB=strstr(buf,"gdbserver"); strC=strstr(buf,"gdb"); strD=strstr(buf,"fuwu"); if(strA || strB ||strC || strD) { return 1; // 执行到这里,判定为调试状态 } } pclose(pfile); return 0; } ``` [[原创]对安卓反调试和校验检测的一些实践与结论](https://bbs.pediy.com/thread-268155.htm) ### frida检测 <a href="elink@76cK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6^5P5s2t1H3M7%4y4Q4x3V1k6m8L8Y4c8A6c8Y4u0A6k6r3p5`.">一些Frida检测手段</a> ## 9.反思 签名的博弈日新月异,善用工具,拥抱开源!!! 通过系统自带的api去获取签名很容易被伪造,不妨试试通过SVC的方式去获取(参考MT开源的方法) 隐式签名校验 有一些则比较隐晦,在发现apk被修改后,会偷偷修改apk的部分功能,例如在某些多开定位软件中,会暗改ip的经纬网等,跟实际产生一定的偏差。 PS:推荐学习芽衣大神的手撕签名校验教程 <a href="elink@8a3K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6Z5L8$3#2W2i4K6u0W2M7r3S2H3i4K6y4r3L8h3!0V1i4K6y4p5M7%4m8S2j5$3g2Q4x3U0k6#2K9h3c8Q4x3@1b7^5y4K6b7I4y4e0c8Q4x3U0k6V1L8#2)9K6c8s2c8Z5M7X3g2S2k6q4)9J5y4Y4k6A6k6i4N6Q4x3@1c8E0k6g2)9J5y4X3k6J5L8$3#2Q4x3@1c8K6M7r3q4U0k6b7`.`.">点击学习</a> # 四、课后小作业 --- 1.手动实现PM代{过}{滤}理 2.移植我的重定向代码(要学会偷代码!) 3.完成作业demo的校验 <a href="elink@409K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4q4A6i4K6u0W2L8r3q4F1P5X3!0#2j5W2)9J5k6h3y4G2L8g2)9J5c8X3W2w2N6o6g2s2x3r3A6C8N6X3S2@1j5H3`.`.">点击下载</a> <a href="elink@6c6K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6X3L8%4u0#2L8g2)9J5k6i4m8Z5M7q4)9K6c8X3#2G2k6q4)9K6c8s2k6A6k6i4N6@1K9s2u0W2j5h3c8Q4x3U0k6@1K9h3c8Q4x3@1b7I4y4K6x3I4x3U0l9K6">作业反馈贴</a> # 五、答疑 --- 待更新 # 六、视频及课件地址 --- <a href="elink@ad3K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3j5h3&6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8g2)9J5c8Y4y4Q4x3V1j5I4j5@1k6i4g2p5I4F1x3e0c8B7k6g2N6X3M7q4S2^5L8s2R3K6M7%4W2k6N6#2)9K6c8Y4m8%4k6q4)9K6c8r3&6I4N6e0V1`.">百度云</a> <a href="elink@ad1K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2S2L8r3W2&6N6h3&6V1M7X3W2$3k6g2)9J5k6h3y4G2L8g2)9J5c8Y4y4Q4x3V1k6f1d9X3!0w2e0f1D9$3k6s2f1$3P5l9`.`.">阿里云</a> <a href="elink@c1aK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2T1K9h3I4A6j5X3W2D9K9g2)9J5k6h3y4G2L8g2)9J5c8Y4k6A6k6r3g2G2i4K6u0r3b7W2j5I4N6#2b7@1x3e0q4z5y4%4y4h3i4K6u0r3i4K6y4r3M7%4m8E0i4K6g2X3K9h3c8Q4y4h3k6X3M7X3!0E0i4K6y4p5x3K6x3K6i4K6u0W2y4K6R3^5i4K6t1$3N6X3c8Q4y4h3k6K6L8%4g2J5j5$3g2Q4x3@1b7$3k6r3c8W2x3e0k6V1j5K6j5@1y4K6W2X3x3o6l9$3z5e0c8T1j5h3q4X3y4K6y4S2x3U0t1J5y4e0b7#2x3R3`.`.">哔哩哔哩</a> PS:解压密码都是52pj,阿里云由于不能分享压缩包,所以下载exe文件,双击自解压 # 七、其他章节 --- <a href="elink@bcbK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4U0V1#2x3e0b7I4i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9">《安卓逆向这档事》一、模拟器环境搭建</a> <a href="elink@5c9K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4U0V1#2y4K6V1$3i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9">《安卓逆向这档事》二、初识APK文件结构、双开、汉化、基础修改</a> <a href="elink@822K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4K6l9I4x3K6f1K6i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9">《安卓逆向这档事》三、初识smail,vip终结者</a> <a href="elink@05bK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4K6l9$3y4U0V1I4i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9">《安卓逆向这档事》四、恭喜你获得广告&弹窗静默卡</a> <a href="elink@7e1K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4K6p5@1y4K6t1%4i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9">《安卓逆向这档事》五、1000-7=?&动态调试&Log插桩</a> <a href="elink@537K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4K6x3I4x3e0R3I4i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9">《安卓逆向这档事》六、校验的N次方-签名校验对抗、PM代{过}{滤}理、IO重定向</a> <a href="elink@503K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4K6b7H3z5e0b7@1i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9">《安卓逆向这档事》七、Sorry,会Hook真的可以为所欲为-Xposed快速上手(上)模块编写,常用Api</a> <a href="elink@687K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4K6b7^5x3o6R3I4i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9">《安卓逆向这档事》八、Sorry,会Hook真的可以为所欲为-xposed快速上手(下)快速hook</a> <a href="elink@b2fK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4K6j5J5x3U0t1#2i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9">《安卓逆向这档事》九、密码学基础、算法自吐、非标准加密对抗</a> <a href="elink@c11K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4K6R3%4y4U0j5%4i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9">《安卓逆向这档事》十、不是我说,有了IDA还要什么女朋友?</a> <a href="elink@896K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4z5o6l9&6y4U0b7$3i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9">《安卓逆向这档事》十二、大佬帮我分析一下</a> <a href="elink@023K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4z5o6p5@1z5e0p5%4i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9">《安卓逆向这档事》番外实战篇1-某电影视全家桶</a> # 八、参考文档 --- <a href="elink@289K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6T1L8r3!0Y4i4K6u0W2j5%4y4V1L8W2)9J5k6h3&6W2N6q4)9J5c8Y4N6W2K9i4S2A6L8W2)9#2k6U0b7$3y4e0j5&6x3o6f1&6i4K6u0r3j5i4u0@1K9h3y4D9k6g2)9J5c8X3c8W2N6r3q4A6L8s2y4Q4x3V1j5I4x3U0l9K6x3o6M7I4y4o6b7`.">APK 签名:v1 v2 v3 v4</a> <a href="elink@d2eK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6Y4N6r3j5K6y4g2)9J5c8X3S2G2N6#2)9J5k6s2c8G2i4K6u0V1j5$3S2W2j5$3E0Q4x3X3c8K6K9h3N6F1">如何把签名校验做到极致</a> <a href="elink@190K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3g2B7K9h3q4F1M7$3S2#2i4K6u0W2j5$3!0E0i4K6u0r3M7q4)9J5c8V1x3#2y4e0V1^5y4e0u0U0y4o6R3%4z5l9`.`.">Android PMS HOOK</a> [[实战破解]白描-动态代{过}{滤}理Hook签名校验](<mark class="encrypted">f73K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6@1K9s2u0W2j5h3c8Q4x3X3b7I4y4e0t1$3z5o6f1@1i4K6u0V1x3g2)9J5k6o6q4Q4x3X3g2Z5N6r3#2D9i4K6t1&6</mark> [[原创]对安卓反调试和校验检测的一些实践与结论](https://bbs.pediy.com/thread-268155.htm) <a href="elink@3f8K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6Y4K9i4c8Z5N6h3u0Q4x3X3g2U0L8$3#2Q4x3V1k6x3i4K6u0V1d9V1W2z5b7V1W2z5i4K6u0r3b7i4m8C8f1$3W2Y4L8X3q4@1N6i4u0W2d9$3W2D9L8r3g2J5c8i4R3`.">新版MT去签及对抗</a> <a href="elink@f02K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6%4N6%4N6Q4x3X3f1#2x3Y4m8G2K9X3W2W2i4K6u0W2j5$3&6Q4x3V1k6X3L8%4u0#2L8g2)9J5k6i4m8Z5M7q4)9K6c8X3#2G2k6q4)9K6c8s2k6A6k6i4N6@1K9s2u0W2j5h3c8Q4x3U0k6@1K9h3c8Q4x3@1b7I4x3U0R3^5y4o6b7K6">【小白教程】正则匹配的写法 多行匹配 批量赋值 smali逆向 简单实用</a>
冰与火的战歌:Windows内核攻防实战高级班!从零到实战,融合AI与Windows内核攻防全技术栈,打造具备自动化能力的内核开发高手。
最后于
2023-7-30 22:50 被正己编辑 ,原因:
#基础理论
#逆向分析
#NDK分析
#工具脚本
#其他
收藏
・
28
点赞
・
6
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
mb_yfxezfnn
为你点赞!
2026-4-20 08:45
简单的简单
为你点赞~
2023-10-25 16:30
山间清爽的风
为你点赞~
2023-10-25 16:11
Forgo7ten
为你点赞~
2023-8-3 15:21
zhczf
为你点赞~
2023-7-31 08:47
NPC2000
为你点赞~
2023-7-30 22:49
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
5
)
正己
雪 币:
6591
活跃值:
(5253)
能力值:
( LV7,RANK:110 )
在线值:
发帖
18
回帖
61
粉丝
501
关注
私信
正己
2
楼
小小搬运一下,看雪的佬轻喷
2023-7-30 22:20
0
NPC2000
雪 币:
4164
活跃值:
(5909)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
17
粉丝
3
关注
私信
NPC2000
3
楼
TQL
2023-7-30 22:49
0
秋狝
雪 币:
10065
活跃值:
(34126)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
1571
粉丝
39
关注
私信
秋狝
4
楼
mark
2023-7-31 09:07
1
iheqing
雪 币:
0
活跃值:
(45)
能力值:
( LV2,RANK:10 )
在线值:
发帖
0
回帖
3
粉丝
0
关注
私信
iheqing
5
楼
学习了
最后于
2025-2-24 09:43 被iheqing编辑 ,原因:
2025-2-24 09:43
0
mb_sgywdbxb
雪 币:
能力值:
( LV1,RANK:0 )
在线值:
发帖
0
回帖
1
粉丝
0
关注
私信
mb_sgywdbxb
6
楼
666
2025-12-5 17:08
0
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
正己
18
发帖
61
回帖
110
RANK
关注
私信
他的文章
[原创]《安卓逆向这档事》第二十六课、Unidbg之补完环境我就睡(下)
19170
[原创]《安卓逆向这档事》第二十五课、Unidbg之补完环境我就睡(中)
18464
《安卓逆向这档事》第二十四课、Unidbg之补完环境我就睡(上)
11067
《安卓逆向这档事》第二十三课、黑盒魔法之Unidbg
65807
[原创]《安卓逆向这档事》第二十二课、抓包学得好,牢饭吃得饱(下)
41695
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部