长沙好的做网站品牌,男科免费咨询,长春seo排名,房产网站怎么推广一、前言 在前两篇破解的文章中#xff0c;我们介绍了如何使用动态调试来破解apk#xff0c;一个是通过调试smali源码#xff0c;一个是通过调试so代码来进行代码的跟踪破解#xff0c;那么今天我们就这两篇文章的破解方法#xff0c;来看看Android中开发应用的过程中如何…一、前言 在前两篇破解的文章中我们介绍了如何使用动态调试来破解apk一个是通过调试smali源码一个是通过调试so代码来进行代码的跟踪破解那么今天我们就这两篇文章的破解方法来看看Android中开发应用的过程中如何对我们的应用做一层安全保护当然现在市场中大部分的应用已经做了一些防护策略但是没有绝对的安全破解只是时间上的问题。所以攻破和防护是相生相克永不停息的战争没有绝对的安全也没有万能的破解之道。 下面我们就来看看如何做到我们的应用更安全我们主要从这五个方面来看看怎么操作 1、混淆策略 2、应用的签名 3、修改Native函数名 4、反调试异常检测 5、应用的加固策略 当然还有其他防护方法我们今天就介绍这五种后续还有的话继续补充 二、技术原理 第一种方式混淆策略 混淆策略是每个应用必须增加的一种防护策略同时他不仅是为了防护也是为了减小应用安装包的大小所以他是每个应用发版之前必须要添加的一项功能现在混淆策略一般有两种 1、对代码的混淆 我们在反编译apk之后看到的代码类名方法名已经代码格式看起来不像正常的Android项目代码那么这时候就会增加阅读难度增加破解难度像这样的代码混淆 我们一般现在的破解查看Java层代码就是两种方式 一种是直接先解压classes.dex文件出来使用dex2jar工具转化成jar文件然后再用jd-gui工具进行查看类结构 一种是使用apktool工具直接反编译apk得到smali源码阅读smali源码 不过这种代码混淆有时候在一定程度上能够增加混淆策略但是有时候也不是很安全因为我们知道我们在破解的过程中一般是找程序的入口那么这些入口一般都是Application或者是MainActivity之类的但是这些Android中的组件类是不能进行混淆的所以我们还是有入口可寻能够找到入口代码然后进行跟踪。 2、对工程资源的混淆 我们上面说到了对代码的混淆能够增加一定的代码阅读难度有时候我们为了防止资源的保护也是可以做混淆的这个资源混淆原理这里就不多解释了微信团队已经将这个功能开源不了解的同学可以转战github查看 https://github.com/shwenzhang/AndResGuard 当然资源混淆还有一个很大的好处就是减小apk包的大小当然这个不是本文讨论的知识点这里我们讨论的是混淆资源增加破解查找资源的难度先来看一下混淆资源之后的结果 这里我们可以看到一个混淆资源的应用反编译之后查看他的string.xml内容发现他的name全是简单的混淆字母那么这个对于我们之前的那种可以通过name的值来查找对应的字符串内容来获取消息这个将是很蛋疼的一件事因为你这时候如果全局搜索一个name值的话比如这里的namea那么得搜出多少个这样的name查找也是很好时间的其实在没有混淆之前一般string中的name都是比较唯一的一种值查找的话不会有那么多查找结果而且查找时间也是很短的。 破解之道 但是对于这种混淆资源也是绝对的防护安全因为我们知道一般在反编译之后的Java代码中看到的获取资源值的时候并不是资源的name值了而是资源对应的int类型的值比如这样 这里获取一个字符串的值那么这些int类型的值我们可以在反编译之后的res/values/pulblic.xml中找到 比如这里的2131230929变成16进制就是0x0x7f0800d1我们在public.xml中查找找到了nameey‘的一项然后再去string.xml中进行name查找 好吧还是找到了这个字符串的值反编译之后的public.xml中记录了所有资源的id和整型值对应值混淆之后的代码中看到的都是资源id的整型值那么这么一看混淆并没有什么用途。只能偏偏小白了。 从上面的两处混淆策略看到混淆对于破解并没有什么太大的阻碍也是只是一个障眼法不过混淆的另外一个功能就是减少apk包的大小这个也是每个应用添加混淆的最主要原因。 第二种方式应用的签名 我们知道Android中的每个应用都是有一个唯一的签名如果一个应用没有被签名是不允许安装到设备中的一般我们在运行debug程序的时候也是有默认的签名文件的只是IDE帮我们做了签名工作一般在应用发版的时候会用唯一的签名文件进行签名那么我们在以往的破解中可以看到我们有时候需要在反编译应用之后然后从新签名在打包运行这个又给了很多二次打包团队谋取利益的一种手段就是反编译市场中的包然后添加一些广告代码最后使用自家的签名在此从新打包发布到市场中因为签名在反编译之后是获取不到的所以只能用自己的签名文件去签名但是在已经安装了应用设备再去安装一个签名不一致的应用也是安装失败的这样也有一个问题就是有些用户安装了这些二次打包的应用之后无法再安装正规的应用了只有卸载重装。那么这时候我们可以利用应用的签名是唯一的特性做一层防护。 我们为了防止应用被二次打包或者是需要破解我们的apk的操作在入口处添加签名验证如果发现应用的签名不正确就立即退出程序我们可以在应用启动的时候获取应用的签名值然后和正规的签名值作比对如果不符合就直接退成程序即可这里我们做一个简单的案例测试一下 这里定义一个简单的工具类用于比较应用的签名这里只是简单处理正常情况下这里应该比对签名的MD5值这里为了简单就忽略了然后我们在程序的入口处做一次比对如果不正确就退出程序 那么我们得到上面的apk之后下面来反编译然后从新签名安装(关于这里如何反编译和签名不做解释了使用apktool和signapk工具即可签名文件是自己的)然后运行 发现程序根本运行不起来一点击就闪退这里就做到了防止应用被二次签名打包的安全问题策略了。 破解之道 但是这个也不是最安全的因为我们知道既然有签名比对方法的地方那么我只需要反编译apk之后修改smali语法把这个方法调用的地方注释即可 只需要使用#把这行代码注释然后回编译从新打包安装即可。所以这种方式也是只能欺骗一下小白不过这里需要注意的是如何找到这个检测签名的方法的地方还是最关键的比如有的程序在native层做的但是不管在哪里只要是在代码中我们就可以找出来的。 第三种方式修改Naitve函数名 这个方法其实不太常用因为他的安全措施不是很强大的但是也是可以起到一定的障眼法策略在说这个知识点的时候我们先来了解一下so加载的流程 在Android中当程序在java层运行System.loadLibrary(jnitest);这行代码后程序会去载入libjnitest.so文件与此同时产生一个Load事件这个事件触发后程序默认会在载入的.so文件的函数列表中查找JNI_OnLoad函数并执行与Load事件相对当载入的.so文件被卸载时“Unload”事件被触发此时程序默认会去在载入的.so文件的函数列表中查找JNI_OnUnload函数并执行然后卸载.so文件。需要注意的是JNI_OnLoad与JNI_OnUnload这两个函数在.so组件中并不是强制要求的用户也可以不去实现java代码一样可以调用到C组件中的函数之所以在C组件中去实现这两个函数特别是JNI_OnLoad函数往往是做一个初始化工作或“善后”工作。可以这样认为将JNI_ONLoad看成是.so组件的初始化函数当其第一次被装载时被执行window下的dll文件也可类似的机制在_DLL_Main()函数中通过一个swith case语句来识别当前是载入还是卸载。将JNI_OnUnload函数看成是析构函数当其被卸载时被调用。由此看来就不难明白为什么很多jni C组件中会实现JNI_OnLoad这个函数了。 一般情况下在C组件中的JNI_OnLoad函数用来实现给VM注册接口以方便VM可以快速的找到Java代码需要调用的C函数。此外JNI_OnLoad函数还有另外一个功能那就是告诉VM此C组件使用那一个JNI版本如果未实现JNI_OnLoad函数则默认是JNI 1.1版本。 应用层的Java类别通过VM而调用到native函数。一般是通过VM去寻找*.so里的native函数。如果需要连续呼叫很多次每次都需要寻找一遍会多花许多时间。此时C组件开发者可以将本地函数向VM进行注册,以便能加快后续调用native函数的效率.可以这么想象一下假设VM内部一个native函数链表初始时是空的在未显式注册之前此native函数链表是空的每次java调用native函数之前会首先在此链表中查找需要查找需要调用的native函数如果找到就直接使用如果未找到得再通过载入的.so文件中的函数列表中去查找且每次java调用native函数都是进行这样的流程因此效率就自然会下降为了克服这样现象我们可以通过在.so文件载入初始化时即JNI_OnLoad函数中先行将native函数注册到VM的native函数链表中去这样一来后续每次java调用native函数时都会在VM中的native函数链表中找到对应的函数从而加快速度 通过上面的分析之后我们知道原来我们知道so文件加载和卸载的时机同时我们可以显示的手动注册我们自己的native方法那么我们知道一般我们在定义native方法的时候对应的native层的函数名是Java_类名_方法名 这种样式 所以就有两个问题 第一个问题就是我们在IDA工具查看so文件的时候去找到对应的native方法非常容易以为我们知道了Java层的native方法名和类型那么直接可以定位到这个native函数 第二问题就是恶意破解人可以得到这个so文件之后查看这个native方法的参数和返回类型也就是方法签名然后自己在Java层写一个demo程序然后构造一个和so文件中对应的native方法然后就可以执行这个native方法如果我们有一个校验密码或者是获取密码的方法是个native的那么这时候就会很容易的被恶意人执行方法后获取结果。 说的简单点就比如上面的这个isEquals例子 现在有一个人想执行这个我的应用的isEquals方法那么他只需要解压我的apk得到so文件查看so文件中的函数或者是查看上层的Java代码得到这个方法的返回值和签名然后他就可以编写一个简单的程序构造一个类 cn.wjdainkong.encryptdemo.MainActivity 然后在他内部定义一个native方法 public native boolean isEquals(String str); 然后在使用System.loadLibrary加载我的so文件然后在适当的地方执行isEquals方法这样就等于调用了我的so文件中的isEquals方法了。 所以从上面的两个为可以看到如果我们native层的函数遵从这样的格式无疑是给破解者简单的一种方式所以我们可以这么做就是显示的注册我们的JNI方法只需要在我们native层的代码中调用这三个函数即可 第一个函数(*env)-RegisterNatives(env,clazz, methods, methodsLenght) 这个函数就是手动的注册一个native方法这个函数是属于JNIEnv*的参数也比较简单 1》clazz就是需要注册native方法的那个类是jclass类型这个我们可以使用JNIEnv的FindClass方法传递类的名称即可获取这个对象类似于这样 2》methods是一个结构体定义如下 typedef struct { const char* name; const char* signature; void* fnPtr;} JNINativeMethod;第一个变量name是Java中函数的名字。第二个变量signature用字符串是描述了函数的参数和返回值第三个变量fnPtr是函数指针指向C函数。 类似于这样的结构 第二个函数jint JNI_OnLoad(JavaVM* vm, void* reserved) 这个函数就是上面说到的so被加载的时候被调用到同时我们可以看到这里还可以获取JVM参数的一般在这个函数中主要就是执行上面的注册函数功能同时这里还需要获取一个JNIEnv*变量 这里通过JVM来获取JNIEnv变量然后调用注册函数 实现手动的注册函数 第三个函数void JNI_OnUnload(JavaVM* vm, void* reserved)这个函数和JNI_OnLoad是相对应的是在so被卸载的时候调用 通过上面的三个函数我们就可以手动的显示注册我们的native函数方法了那么我们同时就可以修改native层的函数名不要按照之前的那种格式了增加破解者寻找关键的native层函数的难度 这里我们把isEquals函数名变成了jiangwei 然后在修改注册方法的结构体 编译运行在使用IDA查看 这时候破解者不能按照常规的套路找到了native层的函数了那么上面的两个问题就可以避免了。增加安全性 破解之道 但是问题来了现在的破解者一般打开SO文件的时候如果找不到对应的native方法之后就会去找JNI_OnLoad函数然后在通过分析arm汇编代码找到register函数分析注册方法结构体找到对应的native方法那么这种方式还是不靠谱也是只能糊弄一下小白破解者。不过我们通过这个例子也可以得知在JNI_OnLoad中可以做很多事的比如上面说到的签名机制校验我们也可以在JNI_OnLoad中做一次增加安全性 看看equal_sign函数功能 在这个方法中其实用我们用JNIEnv变量调用了Java层的方法来获取应用的签名然后进行比对的 所以我们用这种签名校验方式来做安全性保证也是一个思路至少native层的代码分析比smali代码分析难度大点而且这种签名校验机制必须用静态方式去破解apk也就是通过分析代码来破解因为程序没有运行起来无法通过动态方式破解的。那么应对与静态方式破解的话我们只能增加代码的阅读难度了。 第四种方式反调试异常检测 这种方式其实是为了应对现在很多破解者使用IDA进行动态方式调试so文件从而获取重要的信息如果还不知道如何使用IDA进行动态调试so文件的同学可以查看这篇文章Android中使用IDA进行动态调试so文件 看完这篇文章之后我们可以知道IDA进行so动态调试是基于进程的注入技术然后使用Linux中的ptrace机制进行调试目标进程的那么ptrace机制有一个特点就是如果一个进程被调试了在他进程的status文件中有一个字段TracerPid会记录调试者的进程id值比如 查看文件/proc/[myPid]/status 在第六行有一个TracerPid字段就是记录了调试者的进程id 那么我们就可以这么做来达到反调试的功效了就是我们可以轮训的遍历自己进程的status文件然后读取TracerPid字段值如果发现他大于0那么就代表着自己的应用在被人调试所以就立马退出程序。原理知道了代码实现也很简单这里用pthread创建一个线程然后进行轮训操作 使用pthread_create创建一个线程线程启动之后执行thread_function函数 看看thread_funcation函数 开始轮训读取TracerPid字段的值发现大于0就立马退出程序我们运行结果看看 看到了当我们使用IDA工具进行调试的时候程序立马退出同时IDA的调试页面也退出了。 所以这里我们看到这种轮训机制来实现反调试策略可以应对与一般的破解小白了。 破解之道 但是还是有问题因为现在破解者们他们已经免疫了知道会有这种检测所以就会用IDA工具给JNI_OnLoad函数下断点然后进行调试找到检测轮训代码使用nop指令替换检测指令就相当于把检测代码给注释了功能的夭折所以这种反调试方法还是不好使知道的人多了也没什么意义了但是有总比没有的好。 第五种方式应用的加固策略 关于这种方式那就是现在很多应用都用的一种方式了也是安全性最高的一种防护了他加固主要有三方面 1、对dex文件进行加密 这样我们用dex2jar工具或者apktools等工具反编译失败关于这个dex加密这里也不做太多的介绍了之前有一篇文章已经介绍了dex加固的原理了Android中apk加固原理解析 破解之道 但是可惜的是这种方式也沦陷了因为我们知道不管你dex怎么加密最后都是需要用DVM加载dex文件到内存中的我们知道Android中所有关于DVM的函数都是在libdvm.so文件中的而且这个文件是存在设备的/system/lib目录中的加载dex有一个重要的函数 int dvmDexFileOpenPartial(const void* addr, int len, DvmDex** ppDvmDex)第一个参数就是dex内存起始地址 第二个参数就是dex大小。所以在这个函数下断点可以直接dump出明文dex 所以我们使用IDA调试程序找到模块libdvm.so的内存地址找到其中的函数dvmdexfileopenpartialPKviPP6DvmDex在这个函数下断点即可dump处dex文件了。 关于这个案例后续还会写一篇文章来介绍如何破解现在加固dex的应用。 2、对so文件进行加密 现在很多应用把中号的功能都放到了native层中那么如果我们队so文件进行加密的话那么IDA工具就无法打开so文件从而做到安全保护关于Android中so加密可以参考这篇文章Android中对so加固原理解析 破解之道 但是可惜的是这种方式也被沦陷了看完这篇文章之后我们知道加固so的有一个特点就是你必须在so在被调用的时候需要进行解密不然会影响正常的native层调用那么这个时机很重要一般都是在so文件的入口处也就是用 __attribute__((constructor)) 这个属性来标注一个解密函数这样就能保证解密函数执行的时机比任何一个函数时机都早或者可以理解为是一个类的构造函数的执行时机即可但是一般有这种属性的函数都在so的.inin_array段中的 那么现在的问题就变成了如果我知道了.init_array端的位置然后在查看这个段中的arm指令代码就可以得到解密函数了然后在解读这个解密函数的逻辑即可。那么最后就要看这个加密函数的难度程度怎么样了。 关于这个案例后续还会写一篇文章来介绍如何破解现在加固so的应用。 3、加固资源文件和AndroidManifest.xml文件 这个加固一般是应对与现在最流行的反编译工具apktool了他是开源的比如下面的这个应用 所以看到了这种加固就是利用apktool工具的漏洞来进行加固的不过这个apktool工具也是实时在更新的也是为了解决现在的apk这种资源文件的加固导致反编译失败的问题。 破解之道 所以对于这种反编译失败的问题我们应该自己编译apktool的源码找到指定的保存位置然后修改异常即可不过这个可不是一个简单的工作是需要耐心和经验的。 项目下载http://download.csdn.net/detail/jiangwei0910410003/9534543 三、安全工作流程 1、为了应对与低级破解小白同时也是为了减小apk包的大小我们会对代码和资源的一个混淆增加破解难度 2、为了应对与初级破解小白我们将手动的注册我们的native方法让破解者找不到对应的native方法同时解决一些重要的native方法的被调用问题增加破解难度 3、为了应对与中级破解小白我们会利用应用的签名来防止应用的二次签名打包同时防止动态调试问题增加破解难度 4、为了应对与高级破解小白我们会增加应用的反调试功能来防止应用被动态调试和进程注入问题增加破解难度 5、为了应对与资深破解小白我们会采用应用的加固策略对dexso资源文件进行加固增加反编译工作了和调试难度 四、总结 通过这篇文章我们看到介绍了几种安全防护应用的方法但是我们在每个方法后面也都介绍了破解者如何应对与这种方法所以说这里说的这些安全防护都是可以被破解的只是时间问题随着时间的推移我们看到没有绝对的安全也没有统一的破解之道只有一个安全策略出来了破解之道也就相对应出来的这样相生相克彼此进步。但是个人感觉破解还是要大于防护的因为破解是逆向思维这种要求会更高点特别是对于那种变态的加密算法的破解和逆向尤其蛋疼。最后也希望通过这篇文章能够让你们了解到Android中的破解不是那么容易的安全也不是那么容易的。两者都在进步我们也要进步转载于:https://www.cnblogs.com/chenxibobo/p/6075064.html