最近重新燃起了运动的热情,经常使用到一款软件,里面有些内容需要付费观看,所以决定给它一些特殊的关爱。
重新打包是避不开的,现在大多app都会做防护,所以决定什么都不做只是反编译重打包试下,果然重打包之后登录不了。。。
我这里使用手机号+验证码的方式进行登录
接口请求分析
将apk反编译之后再重签名打包后,发现登录时会提示“非法的请求”,抓包发现登录接口返回码400
这种情况应该是服务端对请求做了校验,需要对请求参数进行分析。
这里使用的是手机号登录,请求body是包含手机号、验证码在内的几个参数
如果只是单单这几个参数,服务端肯定是没办法进行校验的,再看看header
果然,其中有个sign参数,服务端应该就是通过这个参数进行了校验,那么就需要了解这个值的生成逻辑。
代码逆向分析
1、首先选择了用jeb以sign为关键字全局字符串搜索,检索到了上百条结果,但是都没有定位到关键点,这里不再列出,看来只能换种方式去定位sign了。
2、因为已经知道登录接口/account/v3/login/sms,那就以它为入手点,同样先用jeb搜下,不幸的是可以检索到接口名称,但是无法直接定位到代码位置,那就反编译apk然后全局搜索吧,由于文件名已经混淆,最终定位到文件为smali_classes2/l/q/a/c0/c/q/a.smali
为了方便查看再用jeb定位到相应路径, 转换到java代码
从文件的内容可以看出这里是对各个接口的声明,追踪下调用关系,发现只有一处是在VerificationCodeLoginActivity中
可以看到在这个q1方法中,前面几行组装了LoginParams类型的请求参数,那最后一行应该就是发请求操作了,header参数也就是在最后一行的相关调用中生成的了。
最后一行连续调用了多个方法,不过可以先用排除法,从右向左看,匿名内部类看一下其中实现就知道是请求结束之后的回调,i(v0)调用的是上面的接口。
先看下getRestDataSource()方法得到的是什么,是个i类型的对象
看下Ll/q/a/c0/c/i类的实现, 其构造函数调用的a方法中,发现了interceptor,感觉离真相又近了一步,连续三个v0.b()方法,添加了三个拦截器,只是前两个被混淆了从名称看不出来(查看其实现,能看到intercept方法,也可进一步佐证)
逐一查看,在第二个拦截器intercept方法中发现了sign字样
毫无疑问需要看下sign是如何计算得到的,分析下intercept方法
如果对retrofit有一定了解,从这个if判断基本可以推断出v0是Request对象
sign的计算可以划分为三步:
1、拼接字符串
1)取出请求参数拼接成字符串
2)拼接v0.h().c()生成的字符串,这里不太好推断是什么,不过因为跟v0有关,应该也是请求相关的内容
3)追加了两个固定参数(这里暂时没想到这两个固定参数有什么用)
2、把拼接的字符串进行MD5运算
3、MD5值进行加密运算
为了验证推论对进行MD5运算、加密运算的两个方法进行了hook
可以看到拼接的字符串内容为请求参数+接口名+固定字符串,生成的MD5作为了加密方法的入参。参数拼接和生成MD5的运算都没有到什么特殊参数,那么应该是加密运算中有什么操作导致请求变成了非法请求。
加密方法最终调用的是一个native方法,该方法在libcryp.so中,ida查看方法实现
其中包含了签名校验,因为代码是使用“卫语句”进行判断的,所以比较简单的方法就是不等于改成等于,这样就可以绕过签名校验。
对应二进制如下图
beq: 数据跳转指令,标志寄存器中Z标志位等于零时, 跳转到BEQ后标签处,对应二进制0A
bne: 数据跳转指令,标志寄存器中Z标志位不等于零时, 跳转到BNE后标签处,对应二进制1A
解决问题
所以只需把对应位置二进制修改即可,使用010Editor打开文件定位到相应位置修改即可
然后重新打包验证,登录成功!!!
欢迎关注公众号:从技术到艺术