第一、利用mhdlc静态分析native函数
1.isEquals函数分析
函数指令代码:
简单分析指令代码:
1、PUSH {r3-r7,lr}是将r3、r4、r5、r6、r7、lr值保存在存储器中的堆栈; 与此相对应的是POP {r3-r7,pc}
pc :程序寄存器,保持下一个CPU执行的指令
lr:与返回寄存器相连接,是在返回预约函数后,下一个要执行的指令
2、调用strlen、malloc、strcpy之前,一般通过MOV指令传递参数值。 例如,在这里的R5中存储了strlen函数的参数,R0是is_number函数的参数。 在后续的动态调试过程中可以得到函数的入口参数值。
3、每次调用具有返回值的函数时,通常会比较CMP、CBZ或strcmp等命令。 这里是解读的突破点。 无论一般加密多么强大,最后比较的参数必须是正确的密码(或正确的加密后的密码)和我们输入的密码(或加密后的输入密码),因此在此使用正确的密码或
至此,我们分析了朴素层的密码比较函数: isEquals
2.可以使用F5键,查看他的C语言代码:
事实上,我们发现两个函数是中心点:
1is_number函数。 这个函数应该看名字来推测是不是数字。 使用F5键可以看到他对应的c语言代码:
主要看return语句和if判定语句,看到这里有循环,_BYTE*获取这里地址的值,自增加后保存在v2中,如果v3为’\ 0’,则结束循环; 接下来,判断v2-48是否大于9。 在这里,我们知道48对应于ASCII的数字0。 因此,您可以确定在名为_BYTE*的循环路径中存储的字符串是否为几个字符串。
2get_encrypt_str函数。 这个函数可以看名字推测。 他获取我们输入的密码的加密值,然后再次使用F5快捷键查看:
现在,我们首先来看一下用于判断传递的参数是否为NULL的if语句。 如果是那样的话,我就直接还给你。 否则,使用strlen函数获取字符串的长度并将其保存到v2。 然后,使用malloc请求堆内存,并将第一个指针保存到result中。 大小为v2,即所传递字符串的长度1,进入循环,第一个指针指定为result,代入I指针,开始循环。 v3通过v1-1获取。 函数传递的字符串的地址。 于是,v6获取传入字符串的字符值,减去48代入v7。 你可以在这里推测。 这里我想做文字转换,把char转换成int型,往下看。 如果v6==48,向下看v7=1,你会看到我们上面得到的v7的值,被用来取key_src数组的值,现在双击key_src变量,就会跳到他的值那里。 果然,这里保存着文字数组。 如果你看到他的长度正好是18,你就会发现,通过传递的字符串,你循环通过字符串获取了字符,并将其转换为字符,然后返回。
那么,到此为止,我分析完了这两个重要函数的功能。 一个是确定输入的内容是否为数字字符串,另一个是从输入的内容中获取密码内容,并将其与正确的加密密码: ssBCqpBssP进行比较。
第二、mhdlc动态调试
因为这里看到的是没有打印log的函数,所以很难知道具体的参数和寄存器的值,所以有必要在这里开始调试,知道各函数被执行之后的寄存器的值。
1.mhdlc配置
要获取mhdlc的安装目录\dbgsrv\android_server :
我们知道(
run-as命令引出升降权限的安全问题(在Linux上为setuid和setgid ) () ) ) ),如果要调试一个APP应用程序就必须注入到他的内部,那么mhdlc 他需要为调试注入进程(Attach附加) ),但mhdlc自己是android_server,所以需要获取设备的进程信息、具体进程的so存储器地址、调试信息等在3358 www.Sina.com/http://www.Sina.com /上,此时必须使用adb中的命令。
ADB前端tcp:远程设备端口号(调试器端) tcp:本地设置
备端口(被调试程序端)
那么这里,我们就可以把android_server端口转发出去:
然后这时候,我们只要在PC端使用mhdlc连接上23946这个端口就可以了,这里面有人好奇了,为什么远程端的端口号也是23946,因为后面我们在使用mhdlc进行连接的时候,发现mhdlc他把这个端口设置死了,就是23946,所以我们没办法自定义这个端口了。
我们可以使用netstat命令查看端口23946的使用情况,看到是ida在使用这个端口:
2.上面就准备好了android_server,运行成功,下面就来用mhdlc进行尝试连接,获取信息,进行进程附加注入
我们这时候需要在打开一个mhdlc,之前打开一个mhdlc是用来分析so文件的,一般用于静态分析,我们要调试so的话,需要在打开一个mhdlc来进行,所以这里一般都是需要打开两个mhdlc,也叫作双开mhdlc操作。动静结合策略。
这里记得选择go这个选项,就是不需要打开so文件了,进入是一个空白页:
我们选择Debugger选项,选择Attach,看到有很多debugger,所以说mhdlc工具真的很强大,做到很多debugger的兼容,可以调试很多平台下的程序。这里我们选择Android debugger:
这里看到,端口是写死的:23946,不能进行修改,所以上面的adb forward进行端口转发的时候必须是23946。这里PC本地机就是调试端,所以host就是本机的ip地址:127.0.0.1,点击确定:
这里可以看到设备中所有的进程信息就列举出来的,其实都是android_server干的事,获取设备进程信息传递给mhdlc进行展示。
双击进程,即可进入调试页面:
这里为什么会断在libc.so中呢?
android系统中libc是c层中最基本的函数库,libc中封装了io、文件、socket等基本系统调用。所有上层的调用都需要经过libc封装层。所以libc.so是最基本的,所以会断在这里,而且我们还需要知道一些常用的系统so,比如linker:
还有一个就是libdvm.so文件,他包含了DVM中所有的底层加载dex的一些方法:
我们在后面动态调试需要dump出加密之后的dex文件,就需要调试这个so文件了。
3、找到函数地址,下断点,开始调试
我们使用Ctrl+S找到需要调试so的基地址:74FE4000
然后通过另外一个mhdlc打开so文件,查看函数的相对地址:E9C
那么得到了函数的绝对地址就是:74FE4E9C,使用G键快速跳转到这个绝对地址:
跳转到指定地址之后,开始下断点,点击最左边的绿色圆点即可下断点:
然后点击左上角的绿色按钮,运行,也可以使用F9键运行程序;我们点击程序中的按钮,触发native函数的运行:
看到了,进入调试阶段了,这时候,我们可以使用F8进行单步调试,F7进行单步进入调试:
我们点击F8进行单步调试,达到is_number函数调用出,看到R0是出入的参数值,我们可以查看R0寄存器的内容,然后看到是123456,这个就是Java层传入的密码字符串,接着往下走:
这里把is_number函数返回值保存到R0寄存中,然后调用CBZ指令,判断是否为0,如果为0就跳转到locret_74FE4EEC处,查看R0寄存器的值不是0,继续往下走:
看到了get_encrypt_str函数的调用,函数的返回值保存在R1寄存器中,查看内容:zytyrTRA*B了,那么看到,上层传递的:123456=》zytyrTRA*B了,前面我们静态分析了get_encrypt_str函数的逻辑,继续往下看:
看到了,这里把上面得到的字符串和ssBCqpBssP作比较,那么这里ssBCqpBssP就是正确的加密密码了,那么我们现在的资源是:
正确的加密密码:ssBCqpBssP,加密密钥库:zytyrTRA*BniqCPpVs,加密逻辑get_encrypt_str
那么我们可以写一个逆向的加密方法,去解析正确的加密密码得到值即可,这里为了给大家一个破解的机会,这里就不公布正确答案了,这个apk我随后会上传,手痒的同学可以尝试破解一下。
加密apk下载地址:http://download.csdn.net/detail/jiangwei0910410003/9531638
第三、总结破解流程
到这里,我们就分析了如何破解apk的流程,下面来总结一下:
1、我们通过解压apk文件,得到对应的so文件,然后使用mhdlc工具打开so,找到指定的native层函数
2、通过mhdlc中的一些快捷键:F5,Ctrl+S,Y等键来静态分析函数的arm指令,大致了解函数的执行流程
3、再次打开一个mhdlc来进行调试so
1>将mhdlc目录中的android_server拷贝到设备的指定目录下,修改android_server的运行权限,用Root身份运行android_server
2>使用adb forward进行端口转发,让远程调试端mhdlc可以连接到被调试端
3>使用mhdlc连接上转发的端口,查看设备的所有进程,找到我们需要调试的进程。
4>通过打开so文件,找到需要调试的函数的相对地址,然后在调试页面使用Ctrl+S找到so文件的基地址,相加之后得到绝对地址,使用G键,跳转到函数的地址处,下好断点。点击运行或者F9键。
5>触发native层的函数,使用F8和F7进行单步调试,查看关键的寄存器中的值,比如函数的参数,和函数的返回值等信息
总结就是:在调试so的时候,需要双开mhdlc,动静结合分析。
第四、使用mhdlc来解决反调试问题
快三技巧准确率100调试需要dump出加密之后的dex文件,就需要调试这个so文件了。
3、找到函数地址,下断点,开始调试
我们使用Ctrl+S找到需要调试so的基地址:74FE4000
然后通过另外一个mhdlc打开so文件,查看函数的相对地址:E9C
那么得到了函数的绝对地址就是:74FE4E9C,使用G键快速跳转到这个绝对地址:
跳转到指定地址之后,开始下断点,点击最左边的绿色圆点即可下断点:
然后点击左上角的绿色按钮,运行,也可以使用F9键运行程序;我们点击程序中的按钮,触发native函数的运行:
看到了,进入调试阶段了,这时候,我们可以使用F8进行单步调试,F7进行单步进入调试:
我们点击F8进行单步调试,达到is_number函数调用出,看到R0是出入的参数值,我们可以查看R0寄存器的内容,然后看到是123456,这个就是Java层传入的密码字符串,接着往下走:
这里把is_number函数返回值保存到R0寄存中,然后调用CBZ指令,判断是否为0,如果为0就跳转到locret_74FE4EEC处,查看R0寄存器的值不是0,继续往下走:
看到了get_encrypt_str函数的调用,函数的返回值保存在R1寄存器中,查看内容:zytyrTRA*B了,那么看到,上层传递的:123456=》zytyrTRA*B了,前面我们静态分析了get_encrypt_str函数的逻辑,继续往下看:
看到了,这里把上面得到的字符串和ssBCqpBssP作比较,那么这里ssBCqpBssP就是正确的加密密码了,那么我们现在的资源是:
正确的加密密码:ssBCqpBssP,加密密钥库:zytyrTRA*BniqCPpVs,加密逻辑get_encrypt_str
那么我们可以写一个逆向的加密方法,去解析正确的加密密码得到值即可,这里为了给大家一个破解的机会,这里就不公布正确答案了,这个apk我随后会上传,手痒的同学可以尝试破解一下。
加密apk下载地址:http://download.csdn.net/detail/jiangwei0910410003/9531638
第三、总结破解流程
到这里,我们就分析了如何破解apk的流程,下面来总结一下:
1、我们通过解压apk文件,得到对应的so文件,然后使用mhdlc工具打开so,找到指定的native层函数
2、通过mhdlc中的一些快捷键:F5,Ctrl+S,Y等键来静态分析函数的arm指令,大致了解函数的执行流程
3、再次打开一个mhdlc来进行调试so
1>将mhdlc目录中的android_server拷贝到设备的指定目录下,修改android_server的运行权限,用Root身份运行android_server
2>使用adb forward进行端口转发,让远程调试端mhdlc可以连接到被调试端
3>使用mhdlc连接上转发的端口,查看设备的所有进程,找到我们需要调试的进程。
4>通过打开so文件,找到需要调试的函数的相对地址,然后在调试页面使用Ctrl+S找到so文件的基地址,相加之后得到绝对地址,使用G键,跳转到函数的地址处,下好断点。点击运行或者F9键。
5>触发native层的函数,使用F8和F7进行单步调试,查看关键的寄存器中的值,比如函数的参数,和函数的返回值等信息
总结就是:在调试so的时候,需要双开mhdlc,动静结合分析。
第四、使用mhdlc来解决反调试问题