版权声明:本文为博主原创文章,未经博主容许不得转载。javascript
今天咱们开始apk破解的另一种方式:动态代码调试破解,以前其实已经在一篇文章中说到如何破解apk了:android
Android中使用静态方式破解Apk 主要采用的是静态方式,步骤也很简单,首先使用apktool来反编译apk,获得smail源码,而后分析smail代码,采用代码注入技术来跟踪代码,而后找到关键方法进行修改,进而破解,同时还可使用一些开源的hook框架,好比:Xposed和Cydia Substrate,来进行关键方法的hook。因此这里咱们能够看到咱们破解的第一步是使用apktool来进行成功的反编译,而后是须要了解smali语法,不过关于smali语法其实很简单,网上有不少教程。算法
那么今天咱们就用另一种方式来破解apk:动态方式,关于动态方式其实很广义的,由于动态方式相对于静态方式来讲,难度大一点,可是他比静态方式高效点,可以针对更过的破解范围。固然动态方式不少,因此这里就分为三篇文章来说解这块:ubuntu
一、动态方式破解apk前奏篇(Eclipse动态调试smail源码)api
二、动态方式破解apk升级篇(IDA动态调试so源码)数组
三、动态方式破解apk终极篇(应对加固的apk破解方法)安全
从这三篇文章可以让咱们破解通常的apk没有任何问题,不过不能表明可以破解全部的apk,由于没有绝对的安全,也是没有绝对的破解,两方都在进步,咱们只能具体问题具体分析。好了,下面咱们就来看第一篇文章,也是今天的重点:Eclipse动态调试smali源码服务器
首先须要解释一下,这里为何说是调试smali源码,不是Java源码,由于咱们弄过反编译的人知道,使用apktool反编译apk以后,会有一个smali文件夹,这里就存放了apk对应的smali源码,关于smali源码这里不解释了,网上有介绍。微信
由于这一篇是一个教程篇,因此不能光说,那样会很枯燥的,因此这里用一个例子来介绍一下:
咱们就用阿里2014年安全挑战赛的第一题:AliCrack_one.apk
看到这张图了,阿里还挺会制造氛围的,那么其实很简单,咱们输入密码就能够破解了,下面咱们就来看看如何获取这个密码。
java -jar apktool_2.0.0rc4.jar d -d AliCraceme_1.apk -o out
这里的命令不作解释了,可是有一个参数必须带上,那就是:-d
由于这个参数表明咱们反编译获得的smali是java文件,这里说的文件是后缀名是java,若是不带这个参数的话,后缀名是smali的,可是Eclipse中是不会识别smali的,而是识别java文件的,因此这里必定要记得加上这个参数。
反编译成功以后,咱们获得了一个out目录,以下:
源码都放在smali文件夹中,咱们进入查看一下文件:
看到了,这里全是Java文件的,其实只是后缀名为java了,内容仍是smali的:
上面咱们反编译成功了,下面咱们为了后续的调试工做,因此仍是须要作两件事:
1》修改AndroidManifest.xml中的Android:debuggable="true"
关于这个属性,咱们前面介绍run-as命令的时候,也提到了,他标识这个应用是不是debug版本,这个将会影响到这个应用是否能够被调试,因此这里必须设置成true。
2》在入口处添加waitForDebugger代码进行调试等待。
这里说的入口处,就是程序启动的地方,就是咱们通常的入口Activity,查找这个Activity的话,方法太多了,好比咱们这里直接从上面获得的AndroidManifest.xml中找到,由于入口Activity的action和category是固定的。
固然还有其余方式,好比aapt查看apk的内容方式,或者是安装apk以后用adb dumpsys activity top命令查看都是能够的。
找到入口Activity以后,咱们直接在他的onCreate方法的第一行加上waitForDebugger代码便可,找到对应的MainActivity的smali源码:
而后添加一行代码:
invoke-static {}, Landroid/os/Debug;->waitForDebugger()V
这个是smali语法的,其实对应的Java代码就是:android.os.Debug.waitForDebugger();
这里把Java语言翻译成smali语法的,不难,网上有smali的语法解析,这里不想再解释了。
java -jar apktool_2.0.0rc4.jar b -d out -o debug.apk
仍是使用apktool进行回编译
编译完成以后,将获得debug.apk文件,可是这个apk是没有签名的,因此是不能安装的,那么下面咱们须要在进行签名,这里咱们使用Android中的测试程序的签名文件和sign.jar工具进行签名:
关于签名的相关知识,能够看这篇文章:Android中的签名机制详解
java -jar .\sign\signapk.jar .\sign\testkey.x509.pem .\sign\testkey.pk8 debug.apk debug.sig.apk
签名以后,咱们就能够进行安装了。
这里咱们新建一个Java工程,记住不是Android工程,由于咱们最后调试实际上是借助于Java的调试器,而后勾选掉Use default location选项,选择咱们的smali源码目录,也就是咱们上面反编译以后的out目录,点击完成
咱们导入源码以后的项目工程结构:
主要看MainActivity类:
这一步咱们看到,其实说的比较广义了,这个要具体问题具体分析了,好比这个例子中,咱们知道当咱们输入密码以后,确定要点击按钮,而后触发密码的校验过程,那么这里咱们知道找到这个button的定义的地方,而后进入他的点击事件中就能够了。这里分为三步走:
1》使用Eclipse自带的View分析工具找到Button的ResId
点击以后,须要等待一会,分析View以后的结果:
看到了,这里咱们可以看到整个当前的页面的所有布局,已经每一个控件的属性值,咱们须要找到button的resource-id
这里咱们看到定义是@+id/button这个值。
2》咱们获得这个resId以后,可否在smali工程中全局搜索这个值,就能够定位到这个button的定义的地方呢?
而后咱们看看搜到的结果:
这时候咱们实际上是在资源文件中搜到了这个id的定义,这个id值对应的是0x7F05003E。
固然除了这种方式,咱们还有一种方式能快速找到这个id对应的整型值,那就是在反编译以后的values/public.xml文件中:
这个文件颇有用的,他是真个apk中全部资源文件定义的映射内容,好比drawable/string/anim/attr/id 等这些资源文件定义的值,名字和整型值对应的地方:
这个文件很重要,是咱们在寻找突破口的重要关键,好比咱们有时候须要经过字符串内容来定位到关键点,这里就能够经过string的定义来找到对应的整型值便可。
当咱们找到了button对应的id值了以后,咱们就能够用这个id值在一次全局搜索一下,由于咱们知道,Android中编译以后的apk,在代码中用到的resId都是用一个整型值代替的,这个整型值就是在R文件中作了定义,将资源的id和一个值对应起来,而后代码里面通常使用R.id.button这样的值,在编译出apk的时候,这个值就会被替换成对应的整型值,因此在全局搜索0x7F05003E
搜索的结果以下:
看到了,这里就定位到了代码中用到的这个button,咱们进入代码看看:
在这里,看到了,使用了findViewById的方式定义Button,咱们在往下面简单分析一下smali语法,下面是给button添加一个按钮事件,这里用的是内部类MainActivity$1,咱们到这个类看看,他确定实现了OnClickListener接口,那么直接搜onClick方法:
在这里咱们就能够下个断点了,这里就是触发密码校验过程。
在第五步中,咱们找到了关键点,而后打上断点,下面咱们就来运行程序,而后在Eclipse中设置远程调试的工程
首先咱们运行程序,由于咱们加入了waitForDebug的代码,因此启动的时候会出现一个Wait debug的对话框。不过,我测试的时候,个人手机没有出现这个对话框,而是一个白屏,不过这个不影响,程序运行起来以后,咱们看看如何在Eclipse中设置远程调试工程,首先咱们找到须要调试的程序对应远程调试服务端对应的端口:
这里咱们看到有几个点:
1》在程序等待远程调试服务器的时候,前面会出现一个红色的小蜘蛛
2》在调试服务端这里咱们会看到两个端口号:8600/8700,这里须要解释一下,为何会有两个端口号呢?
首先在这里的端口号,表明的是,远程调试服务器端的端口,下面在简单来看一下,Java中的调试系统:
这里咱们看到,这里有三个角色:
111》JDB Client端(被调试的客户端),这里咱们能够认为咱们须要破解的程序就是客户端,若是一个程序能够被调试,当启动的时候,会有一个jdwp线程用来和远程调试服务端进行通讯
这里咱们看到,咱们须要破解的程序启动了JDWP线程,注意这个线程也只有当程序是debug模式下才有的,也就是AndroidManifest.xml中的debug属性值必须是true的时候,也就是一开始为何咱们要修改这个值的缘由。
222》JDWP协议(用于传输调试信息的,好比调试的行号,当前的局部变量的信息等),这个就能够说明,为何咱们在一开始的时候,反编译成java文件,由于为了Eclipse导入可以识别的Java文件,而后为何可以调试呢?由于smali文件中有代码的行号和局部变量等信息,因此能够进行调试的。
333》JDB Server端(远程调试的服务端,通常是有JVM端),就是开启一个JVM程序来监听调试端,这里就能够认为是本地的PC机,固然这里必须有端口用来监听,那么上面的8600端口就是这个做用,并且这里端口是从8600开始,后续的程序端口后都是依次加1的,好比其余调试程序:
那么有了8600端口,为何还有一个8700端口呢?他是干什么的?
其实他的做用就是远程调试端备用的基本端口,也就是说好比这里的破解程序,咱们用8600端口能够链接调试,8700也是能够的,可是其余程序,好比demo.systemapi他的8607端口能够链接调试,8700也是能够的:
因此呀,能够把8700端口想象成你们均可以用于链接调试的一个端口,不过,在实际过程当中,仍是建议使用程序独有的端口号8600,咱们能够查看8600和8700端口在远程调试端(本地pc机)的占用状况:
看到了,这里的8600端口和8700端口号都是对应的javaw程序,其实javaw程序就是启动一个JVM来进行监听的。
好了,到这里咱们就弄清楚了,Java中的调试系统以及远程调试的端口号。
注意:
其实咱们可使用adb jdwp命令查看,当前设备中能够被调试的程序的进程号信息:
下面继续,咱们知道了远程调试服务端的端口:8600,以及ip地址,这里就是本地ip:localhost/127.0.0.1
咱们能够在Eclipse中新建一个远程调试项目,将咱们的smali源码工程和设备中须要调试的程序关联起来:
右击被调试的项目=》选择Debug Configurations:
而后开始设置调试项目
选择Romote Java Application,在Project中选择被调试的smali项目,在Connection Type中选择SocketAttach方式,其实还有一种方式是Listener的,关于这两种方式其实很好理解:
Listner方式:是调试客户端启动就准备好一个端口,当调试服务端准备好了,就链接这个端口进行调试
Attach方式:是调试服务端开始就启动一个端口,等待调试端来链接这个端口
咱们通常都是选择Attach方式来进行操做的。
好了,咱们设置完远程调试的工程以后,开始运行,擦发现,设备上的程序仍是白屏,这是为何呢?看看DDMS中调试程序的状态:
擦,关联到了这个进程,缘由也很简单,咱们是上面使用的是8700端口号,这时候咱们选中了这个进程,因此就把smali调试工程关联到了这个进程,因此破解的进程没反应了,咱们立马改一下,用8600端口:
好了,这下成功了,咱们看到红色的小蜘蛛变成绿色的了,说明调试端已经链接上远程调试服务端了。
注意:
咱们在设置远程调试项目的时候,必定要注意端口号的设置,否则没有将调试项目源码和调试程序关联起来,是没有任何效果的
下面咱们就开始操做了,在程序的文本框中输入:gggg内容,点击开始:
好了,到这里咱们看到期待已久的调试界面出来了,到了咱们开始的时候加的断点处,这时候咱们就能够开始调试了,使用F6单步调试,F5单步跳入,F7单步跳出进行操做:
看到了,这里使用v3变量保存了咱们输入的内容
这里有一个关键的地方,就是调用MainActivity的getTableFromPic方法,获取一个String字符串,从变量的值来看,貌似不是规则的字符串内容,这里先不用管了,继续往下走:
这里又遇到一个重要的方法:getPwdFromPic,从字面意义上看,应该是获取正确的密码,用于后面的密码字符串比对。
查看一下密码的内容,貌似也是一个不规则的字符串,可是咱们能够看到和上面获取的table字符串内容格式很像,接着往下走:
这里还有一个信息就是,调用了系统的Log打印,log的tag就是v6保存的值:lil
这时候,咱们看到v3是保存的咱们输入的密码内容,这里使用utf-8获取他的字节数组,而后传递给access$0方法,咱们使用F5进入这个方法:
在这个方法中,还有一个bytesToAliSmsCode方法,使用F5进入:
那么这个方法其实看上去仍是很简单的,就是把传递进来的字节数组,循环遍历,取出字节值,而后转化成int类型,而后在调用上面获取到的table字符串的chatAt来获取指定的字符,使用StringBuilder进行拼接,而后返回便可。
按F7跳出,查看,咱们返回来加密的内容是:日日日日,也就是说gggg=>日日日日
最后再往下走,能够看到是进行代码比对的工做了。
那么上面咱们就分析完了全部的代码逻辑,还不算复杂,咱们来梳理一下流程:
A>调用MainActivity中的getTableFromPic方法,获取一个table字符串
咱们能够进入看看这个方法的实现:
这里能够大致了解了,他是读取asset目录下的一个logo.png图片,而后获取图片的字节码,在进行操做,获得一个字符串,那么咱们从上面的分析能够知道,其实这里的table字符串相似于一个密钥库。
B>经过MainActivity中的getPwdFromPic方法,获取正确的密码内容
C>获取咱们输入内容的utf-8的字节码,而后调用access$0方法,获取加密以后的内容
D>access$0方法中在调用bytesToAliSmsCode方法,获取加密以后的内容
这个方法是最核心的,咱们经过分析知道,他的逻辑是,经过传递进来的字节数组,循环遍历数组,拿到字节转化成int类型,而后在调用密钥库字符串table的charAt获得字符,使用StringBuilder进行拼接。
经过上面的分析以后,咱们知道获取加密以后的输入内容和正确的密码内容作比较,那么咱们如今有的资源是:
密钥库字符串和正确的加密以后的密码,以及加密的逻辑
那么咱们的破解思路其实很简单了,至关于,咱们知道了密钥库字符串,也知道了,加密以后的字符组成的字符串,那么能够经过遍历加密以后的字符串,循环遍历,获取字符,而后再去密钥库找到指定的index,而后在转成byte,保存到字节数组,而后用utf-8获取一个字符串,那么这个字符串就是咱们要的密码。
下面咱们就用代码来实现这个功能:
代码逻辑,很简单吧,其实这个函数至关于上面加密函数的bytesToAliSmsCode的反向实现,运行结果:
OK,获得了正确的密码,下面来验证一下:
哈哈,不要太激动,成功啦啦~~。破解成功。
补充:
刚刚咱们在断点调试的时候,看到了代码中用了Log来打印日志,tag是lil,那么咱们能够打印这个log看看结果:
看到了,这里table是密钥库,pw是正确的加密以后的密码,enPassword是咱们输入以后加密的密码。
因此从这里能够看到,这个例子,其实咱们在破解apk的时候,有时候日志也是一个很是重要的信息。
破解须要的资料,我已经上传了,下载地址:http://download.csdn.net/detail/jiangwei0910410003/9526113
一、咱们经过apktool工具进行apk的反编译,获得smali源码和AndroidManifest.xml,而后修改AndroidManifest.xml中的debug属性为true,同时在入口处加上waitForDebug代码,进行debug等待,通常入口都是先找到入口Activity,而后在onCreate方法中的第一行这里须要注意的是,apktool工具必定要加上-d参数,这样反编译获得的文件是java文件,这样才可以被Eclipse识别,进行调试。
二、修改完成AndroidManifest.xml和添加waitForDebug以后,咱们须要在使用apktool进行回编译,回编译以后获得的是一个没有签名的apk,咱们还须要使用signapk.jar来进行签名,签名文件直接使用测试程序的签名文件就能够,最后在进行安装。
三、而后咱们将反编译以后的smali源码导入到Eclipse工程中,找到关键点,进行下断点,这里的关键点,通常是咱们先大体了解程序运行的结构,而后找到咱们须要破解的地方,使用View分析工具,或者是使用jd-gui工具直接查看apk源码(使用dex2jar将dex文件转化成jar文件,而后用jd-gui进行查看),找到代码的大致位置。而后下断点,这里咱们能够借助Eclipse的DDMS自带的View分析工具找到对应控件的resid,而后在全局搜索这个控件的resid,或者直接在values/public.xml中查找,最终定位到这个控件位置,在查看他的点击事件便可。
四、设置远程调试工程,首先运行须要调试程序,而后在DDMS中找到对应的调试服务端的端口号,而后在Debug Configurations中设置远程调试项目,设置对应的调试端口和ip地址(通常都是本机pc,那就是localhost),而后红色小蜘蛛变成绿色的,表示咱们的远程调试项目链接关联上了调试程序,这里须要注意的是,必定须要关联正确,否则是没有任何效果的,关联成功以后,就能够进行操做。
五、操做的过程当中,会进入到关键的断点处,经过F6单步,F5单步进入,F7单步跳出,来进行调试,找到关键方法,而后经过分析smali语法,了解逻辑,若是逻辑复杂的,能够经过查看具体的环境变量的值来观察,这里也是最重要的,也是最复杂的,同时这里也是没有规章可寻的,这个和每一个人的逻辑思惟以及破解能力有关系,分析关键的加密方法是须要功底的,固然这里还须要注意一个信息,就是Log日志,有时候也是很重要的一个信息。
六、最后通常当咱们知道了核心方法的逻辑,要想获得正确的密码,仍是须要本身用语言去实现逻辑的,好比本文中的加密方法,咱们须要手动的code一下加密的逆向方法,才能获得正确的密码。
一、使用apktool工具进行反编译有时候并非那么顺利,好比像这样的报错:
这个通常都是apktool中解析出现了错误,其实这个都是如今apk为了抵抗apktool,作的apk加固策略,这个后面会写一篇文章如何应对这些加固策略,如何进行apk修复,其实原理就是分析apktool源码,找到指定的报错位置,进行apktool代码修复便可。
二、本文中说到了Java的调试系统,可是为了篇幅限制,没有详细的讲解了整个内容,后面会写一篇文章具体介绍Java中的调试系统以及Android的调试系统。
三、有时候咱们还会遇到回编译成功了,而后遇到运行不起来的错误,这个就须要使用静态方式先去分析程序启动的逻辑,看看是否是程序作了什么运行限制,好比咱们在静态分析那篇文章中,提到了应用为了防止反编译在回编译运行,在程序的入口处做了签名校验,若是校验失败,直接kill掉本身的进程,退出程序了,因此这时候咱们仍是须要使用静态方式去分析apk。
四、如何作到不修改AndroidManifest.xml中的debug属性就能够进行调试:
1》 修改boot.img,从而打开系统调试,这样就能够省去给app添加android:debuggable="true",再重打包的步骤了。
2.》直接修改系统属性,使用setpropex工具在已经root的设备上修改只读的系统属性。使用此工具来修改ro.secure和ro.debuggable的值。
这个也会在后面详细介绍这两种方法
这篇文章咱们就介绍了如何使用Eclipse去动态调试反编译以后的smali源码,这种方式比静态方式高效不少的,好比本文中的这个例子,其实咱们也可使用静态方式进行破解的,可是确定效率没有动态方式高效,因此之后咱们又学会了一个技能,就是动态的调试smali源码来跟踪程序的核心点,可是如今市场上的大部分应用没有这么简单就破解了,好比核心的加密算法放到了native层去作,那么这时候就须要咱们去动态调试so文件跟踪,这个是咱们下一篇文章的内容,也有的时候,apk进行加固了,直接在apktool进行反编译就失败了,这时候咱们就须要先进行apk修复,而后才能后续的操做,这个是咱们下下篇的文章,如何应对apk的加固策略。经过这篇文章咱们能够看到动态方式破解比静态方式高效的多,可是有时候咱们还须要使用静态方式先作一些准备工做,因此在破解apk的时候,动静结合,才能作到完美的破解。
更多内容:点击这里
关注微信公众号,最新Android技术实时推送