分页方法:
打开网络pdf 思路整体还是来源与图片的加载。
android中加载网络图片的框架有很多个。如image-laoder, fresco、glide等,首先都是从内存中找图片,如果内存中没有,接着从本地找,本地没有在从网络下载。
android中加载pdf也是类似,首先从本地找pdf文件,如果本地存在该pdf文件,直接打开,如果本地不存在,将该pdf文件下载到本地在打开。
下载文件用到了retrofit2的库,已经封装到android_pdf中了。
2.依赖android_pdf库方法
2.1 在项目的gradle中增加如下代码:
compile 'com.lidong.pdf:android_pdf:1.0.1'
2.2 一句代码就可以加载网络pdf。
```
pdfView.fileFromLocalStorage(this,this,this,fileUrl,fileName) //设置pdf文件地址
```
2.3对fileFromLocalStorage(this,this,this,fileUrl,fileName)的解析
/**
```
* 加载pdf文件
* @param onPageChangeListener
* @param onLoadCompleteListener
* @param onDrawListener
* @param fileUrl
* @param fileName
*/
public void fileFromLocalStorage(
final OnPageChangeListener onPageChangeListener,
final OnLoadCompleteListener onLoadCompleteListener,
final OnDrawListener onDrawListener,
String fileUrl,
final String fileName)
```
1. OnPageChangeListener onPageChangeListener :翻页回调
2. OnLoadCompleteListener onLoadCompleteListener:加载完成的回调
3. OnDrawListener:页面绘制的回调
4. String fileUrl : 文件的网络地址
5. String fileName 文件名称
3.使用android_pdf库方法
3.1写一个布局文件
```
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.lidong.pdf.androidpdf.MainActivity">
<com.lidong.pdf.PDFView
android:id="@+id/pdfView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</RelativeLayout>
```
3.2在MainActivity中加载
```
import android.graphics.Canvas
import android.os.Environment
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import com.lidong.pdf.PDFView
import com.lidong.pdf.api.ApiManager
import com.lidong.pdf.listener.OnDrawListener
import com.lidong.pdf.listener.OnLoadCompleteListener
import com.lidong.pdf.listener.OnPageChangeListener
import com.lidong.pdf.util.FileUtils
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import okhttp3.ResponseBody
import rx.android.schedulers.AndroidSchedulers
import rx.functions.Action1
import rx.schedulers.Schedulers
public class MainActivity extends AppCompatActivity implements OnPageChangeListener
,OnLoadCompleteListener, OnDrawListener {
private PDFView pdfView
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
pdfView = (PDFView) findViewById( R.id.pdfView )
displayFromFile1("http://file.chmsp.com.cn/colligate/file/00100000224821.pdf", "00100000224821.pdf")
}
/**
* 获取打开网络的pdf文件
* @param fileUrl
* @param fileName
*/
private void displayFromFile1( String fileUrl ,String fileName) {
pdfView.fileFromLocalStorage(this,this,this,fileUrl,fileName) //设置pdf文件地址
}
/**
* 翻页回调
* @param page
* @param pageCount
*/
@Override
public void onPageChanged(int page, int pageCount) {
Toast.makeText( MainActivity.this , "page= " + page +
" pageCount= " + pageCount , Toast.LENGTH_SHORT).show()
}
/**
* 加载完成回调
* @param nbPages 总共的页数
*/
@Override
public void loadComplete(int nbPages) {
Toast.makeText( MainActivity.this , "加载完成" + nbPages , Toast.LENGTH_SHORT).show()
}
@Override
public void onLayerDrawn(Canvas canvas, float pageWidth, float pageHeight, int displayedPage) {
// Toast.makeText( MainActivity.this , "pageWidth= " + pageWidth + "
// pageHeight= " + pageHeight + " displayedPage=" + displayedPage , Toast.LENGTH_SHORT).show()
}
```
之前的两篇文章中我们已经介绍了短视频四小龙的某音,某山,某拍的数据请求加密协议,不了解的同学可以点击查看:某音短视频协议破解,某拍短视频协议破解;那么今天继续最后一个短视频那就是某手,不多说了,还是老规矩直接抓包找入口:看到请求参数分为两部分,一个是在url后面拼接的参数,一部分在body中的。不过这个没多大影响,分析参数信息之后发现在body字段中有一个sig,这个就是签名信息了。所以需要找到这个值的获取方法即可。这里我们使用Jadx打开应用,不过这里可能要注意,应用的包比较大,而且分了四个dex文件,所以打开可能点卡,可以先解压出四个dex文件,依次打开即可。在classes.dex中全局搜索可以看到:
这里看到他内部网络请求使用了retrofit框架,这个框架其实核心原理还是okhttp,只是用注解封装了。那么到这里就发现其实注解功能在逆向中分析起来是比较费劲的。所以我们就换个思路,直接全局搜索那个签名字段:"sig",可惜的是在classes.dex中并没有搜到,在去第二个dex中搜:
看到了,搜到了直接点击进入即可:
这里看到调用了一个方法来获取sig字段信息,直接查看这个类:
这个是接口类型,那么查看他的实现类是哪个?这个变量在构造方法中进行赋值的,看看类型:
到这里遇到问题了,应该是拆分了dex,所以搜索这个类的全局调用会没有结果,这样就跟踪有点麻烦了,不过这里依然采用hook大法,直接hook这个类的构造方法,打印这个参数的类名称即可:
运行代码,看日志信息:
找到这个类了是:com.yxcorp.gifshow.retrofit.c,全局搜这个类,这时候要注意,如果在一个dex中找不到,就去下一个dex中搜索即可:
这个类果然是获取请求参数信息的,有一些公告参数信息,和基础信息,继续往下看:
看到了获取sig字段的方法方实现,其实很简单,直接将传入的两个map参数结构的key和value进行拼接,然后进行排序,最后调用CPU.getClock方法获取加密信息即可。继续看看这个加密方法:
果然还是把加密功能放到了native中做的,参数比较好理解:全局的context,排序好的参数字节数组,系统版本号,那么接下来用IDA简单分析他的native代码,直接打开libcore.so文件即可:
不过可惜的是,会发现没找到这个native函数,但是找到和这个函数可能有关系的,其实这个是他做了一次混淆,后面会出文章单独介绍这种混淆技术。点进去看看:
直接点击X键,查看这个函数的调用地方:
往上查看函数名:
这时候发现了,看到了这个函数了,我们点击F5查看他的大致C代码:
遇到这个警告,这个也是他进行了混淆,不过没关系,可以右键create function即可:
当然这里可以直接使用快捷键P即可,然后就可以顺利查看他的C代码了:
不过,这里不做太多介绍分析了,因为和之前文章一样,我们的目的直接调用这个so来获取加密结果即可。不过静态分析so还是要有的,主要大致看一下他有没有什么防护策略,不过这里大致看了,应该没有。
二、获取请求数据
直接去demo工程继续调用,依然在demo工程中新建一个CPU类:
记得报名和类型都必须保持一致即可,然后就开始构造参数调用这个方法了,这里首先来分析那个传入的两个map结构参数信息,这里依然还是用hook打开,来打印日志分析:
运行,看看打印的结果日志:
再去比对Fiddler中抓包信息的请求参数:
可以很容易发现,第一个参数map是公众参数也就是在url后面的,第二个是基础参数在body中的。那么就简单了,直接构造这两个map结构即可:
这里为了方便,依然将参数写死,后续会进行优化动态获取即可。然后直接调用加密方法,进行网络请求:
运行demo,看看加密信息以及是否能正确请求到数据:
惊奇的发现,尽然成功获取到数据了,说明他native中的加密函数真的没有什么防护策略,这个和之前的某音某拍差距了,我们把这json数据拷贝出来格式化看看结果:
后面我们只需要简单的解析这个json数据即可。
三、继续填坑
可惜的是,到这里算是结束了吗?原以为结束了,因为这里测试我一直用的都是4.4的系统,结果无意中用了5.1的系统测试发现,尽然拿不到数据,原因就是获取签名信息失败了,也就是那个native方法getClock获取失败,那么就恶心了,还得重新回头去看这个函数:
这里我们可以直接静态分析so文件,继续查看getClock函数,到这里会发现5.1的手机的cpu_cnt值是null,这个值在哪里赋值的呢?继续往下查看即可,看到了是个类似于检查cpu属性的函数checkCpuProperty:
而我们知道5.0系统之后cpu架构改变很大的,继续查看这个函数功能:
看到核心点了,这里用a4变量做判断了,看看这个a4是啥:
是这个函数的最后一个值传递进来的。继续往回看,这个参数是怎么传进来的:
这里为了演示方便,就把getClock的没用代码删了,主要看这个参数是怎么传递进来的,发现是getClock的最后一个参数,那么也就是Java层调用这个方法传递的最后一个参数,继续回去看这个方法的调用:
这里看到最后一个参数的确是系统版本,那么就真相大白了:为何4.4的系统可以拿到数据,而5.1的设备拿不到,因为在底层so中对系统做了判断,大致应该是判断cpu信息啥的。那么为了能够让我们始终都能拿到数据,可以非常简单的 *** 作:就是这里的方法最后一个参数直接写死就是4.4对应的api值19,这样永远都可以拿到数据了,那么有同学会觉得奇怪了,这么做是不是有点投机取巧会影响后面的数据读取吗?其实从这里看到应该是应用当初做的一套版本兼容,而我们知道现在4.4系统的占有率还是很高的,应用短期内不可能把这个兼容去掉,而只要兼容不去掉,我们客户端就可以模拟4.4的系统从而顺利的拿到数据了,何乐而不为呢?
严重说明
本文的意图只有一个,就是通过分析app学习更多的逆向技术,如果有人利用本文知识和技术进行非法 *** 作进行牟利,带来的任何法律责任都将由 *** 作者本人承担,和本文作者无任何关系,最终还是希望大家能够秉着学习的心态阅读此文。鉴于安全问题,样本和源码都去编码美丽小密圈自取!点击立即进入小密圈
四、总结
大家看完本文之后,会发现其实应用对协议并没有做太强的加密防护策略,我们很容易就获取到了,只是因为他进行了拆包,所以在跟踪代码的时候比较棘手,不过幸好我们有hook大法,无需痛苦的跟踪代码。那么到这里我们就成功的分析完了短视频四小龙:某音,某山,某拍,某手这四家app的数据请求加密协议了,后面就要开始我们的真正项目了,那么到底是什么项目呢?尽情期待。
Volley适合网络请求比较多,并且都比较小的情况,不适合下载大文件一类的。OKHttp目前我们项目在用,可以配合Picasso一起使用,挺快的,也可以下载大文件。retrofit没有用过,不予置评。AsyncHttp是我们项目之前用的,速度略有点慢。欢迎分享,转载请注明来源:内存溢出
评论列表(0条)