Andriod APP逆向——网络请求

Android网络请求常用模块

  • 常见的Web HTTP请求分为GET和POST请求,在python中使用的是urllib,requests模块,在Android里用的是okhttpretrofit模块,其中okhttp和retrofit的关系就和urllib和requests的关系一样,后者均是在前者的基础上做了二次封装,使其使用起来更加方便。

  • Web HTTP请求的数据包可以分为两种形式,form表单json字符串,在python中这两种形式是通过requests方法的参数来区分的,但在Android中的okhttp也会有相应提交两种形式的方法。

  • 当接收到Web数据包时,接收到的如果时json字符串的话,就需要对数据进行反序列化,python中requests模块帮我们完成了这个工作,但在Android中需要引入Gson模块来实现反序列化。

  • 在浏览器Web应用中,使用cookies文件保存用户的信息,在Android里使用xml文件实现类似的功能,因此还需要学习下Android操作xml文件的方法。

OKHTTP

使用okhttp之前要做如下配置:

implementation “com.squareup.okhttp3:okhttp:4.9.1”
  • 配置,在AndroidManifest.xml中,application标签同级上方添加
    只能发送https请求。

  • 配置,在AndroidManifest.xml中,application标签内部添加 android:networkSecurityConfig=“@xml/network_security_config”

在res目录下新建一个xml文件夹,里面新建一个network_security_config.xml文件,写入以下内容:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <!--禁用掉明文流量请求的检查-->
    <base-config cleartextTrafficPermitted="true" />
</network-security-config>

创建 GET 请求 ---> [创建线程]--> 否则报错

String username = String.valueOf(txtUser.getText());
String password = String.valueOf(txtPwd.getText());

HashMap<String, String> dataMap = new HashMap<String, String>();
dataMap.put("username", username);
dataMap.put("password", password);


// 1. 将用户名和密码 发送到后台 (第三方 OKHttp)
//  1.1 引入依赖 bulid.gradle 文件中 引入 implementation "com.squareup.okhttp3:okhttp:4.9.1"
//  1.2 在Android中,默认不允许发送网络请求; 在AndroidManifest.xml 中 配置 <uses-permission android:name="android.permission.INTERNET"/>
//  1.3 调用 OKHttp包去发送请求。

// 1.3.1 创建 GET 请求 ---> [创建线程]--> 否则报错
// 创建线程并执行 run 方法
new Thread(){
    @Override
    public void run(){
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder().url("https://api.luffycity.com/api/v1/course/actual/?category_id=1").build();
        Call call = client.newCall(request);

        try {
            Response response = call.execute();      //  发送请求
            ResponseBody body = response.body();
            String resString = body.string();

            Log.i("登录界面--->", resString);
            // 获取数据并处理


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}.start();

创建 POST 请求 ---> [创建线程]

form表单格式
  • 数据包以表单格式提交,例如:

username=xxxx&password=123
new Thread(){
    @Override
    public void run(){
        OkHttpClient client = new OkHttpClient();
        // 构建请求体
        FormBody form = new FormBody.Builder().add("username", "xxxx").add("password", "123").build();
        // form ---> post 请求体
        Request request = new Request.Builder().url("https://api.luffycity.com/api/v1/auth/password/login/?loginWay=password").post(form).build();
        Call call = client.newCall(request);

        try {
            Response response = call.execute();      //  发送请求
            ResponseBody body = response.body();
            String resString = body.string();

            Log.i("登录界面--->", resString);
            // 获取数据并处理


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}.start();
json格式
  • 数据包以json格式提交,例如:

{
    username:"xxxx",
    password:123,
}
new Thread() {
    @Override
    public void run() {
        OkHttpClient client = new OkHttpClient();
        // 构建请求体
        // FormBody form = new FormBody.Builder().add("username", "xxxx").add("password", "123").build();

        //JSONObject json = new JSONObject();
        //json.put("username", "dk");
        //json.put("password", "123");
        JSONObject json = new JSONObject(dataMap);
        String jsonString = json.toString();
        RequestBody form = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), toString());

        // form ---> post 请求体
        Request request = new Request.Builder().url("https://api.luffycity.com/api/v1/auth/password/login/?loginWay=password").post(form).build();
        Call call = client.newCall(request);

        try {
            Response response = call.execute();      //  发送请求
            ResponseBody body = response.body();
            String resString = body.string();

            // Json反序列化,字符串转换成对象.
            // json.load(json字符串) --> python
            // Android 需要 GSON  --> 引入 (implementation "com.google.code.gson:gson:2.8.6")
            //
            String responseString = "{\"token\": \"dsajkdhjksald\", \"url\": \"https://www.httpbin.org/post/\",\"dataList\":[{\"id\": 1, \"name\": \"dk\"},{\"id\": 2, \"name\": \"dkk\"}]}";
            HttpResponse res = new Gson().fromJson(responseString, HttpResponse.class);
            Log.e("登录界面--->", res.toString());  //HttpResponse{url='https://www.httpbin.org/post/', origin='101.248.149.62', dataList=[Item{id=1, name='dk'}, Item{id=2, name='dkk'}]}

            // 保存起来:cookies, localstorage
            // andorid -> xml 文件 -> data/data/com.example.myapplication/shared_prefs
            SharedPreferences sharedPreferences = getSharedPreferences("sp_MyApplication", MODE_PRIVATE);
            SharedPreferences.Editor editor= sharedPreferences.edit();
            editor.putString("token", res.token);
            editor.commit();

            // 跳转首页
            Intent in = new Intent(mcontext, IndexActivity.class);
            startActivity(in);


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}.start();
两者区别在于:
  • form表单格式使用的是FormBody构造请求体

  • json字符串格式使用JSONObject和RequestBody两者一起构造请求体

请求拦截器

有一些请求头信息是每一个HTTP请求都会携带的,这些公共的请求头信息在python的scrapy爬虫框架中,用中间件来实现,在Android App的okhttp中,用请求拦截器来实现,下面是拦截器简单的测试代码。

// 4. 发送请求拦截器
Interceptor interceptor = new Interceptor() {
    @NonNull
    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        String sign = "sfsadsadsa";
        // 请求还未发送,在请求体中增加了一个请求头
        Request request = chain.request().newBuilder().addHeader("x-gorgon", sign).build();
        Response response = chain.proceed(request);
        return response;
    }
};
new Thread() {
    @Override
    public void run() {
        // 加入拦截器
        OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
        // 构建请求体
        // FormBody form = new FormBody.Builder().add("username", "xxxx").add("password", "123").build();

        //JSONObject json = new JSONObject();
        //json.put("username", "dk");
        //json.put("password", "123");
        JSONObject json = new JSONObject(dataMap);
        String jsonString = json.toString();
        RequestBody form = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), toString());

        // form ---> post 请求体
        Request request = new Request.Builder().url("https://api.luffycity.com/api/v1/auth/password/login/?loginWay=password").post(form).build();
        Call call = client.newCall(request);

        try {
            Response response = call.execute();      //  发送请求
            ResponseBody body = response.body();
            String resString = body.string();

            // Json反序列化,字符串转换成对象.
            // json.load(json字符串) --> python
            // Android 需要 GSON  --> 引入 (implementation "com.google.code.gson:gson:2.8.6")
            //
            String responseString = "{\"token\": \"dsajkdhjksald\", \"url\": \"https://www.httpbin.org/post/\",\"dataList\":[{\"id\": 1, \"name\": \"dk\"},{\"id\": 2, \"name\": \"dkk\"}]}";
            HttpResponse res = new Gson().fromJson(responseString, HttpResponse.class);
            Log.e("登录界面--->", res.toString());  //HttpResponse{url='https://www.httpbin.org/post/', origin='101.248.149.62', dataList=[Item{id=1, name='dk'}, Item{id=2, name='dkk'}]}

            // 保存起来:cookies, localstorage
            // andorid -> xml 文件 -> data/data/com.example.myapplication/shared_prefs
            SharedPreferences sharedPreferences = getSharedPreferences("sp_MyApplication", MODE_PRIVATE);
            SharedPreferences.Editor editor= sharedPreferences.edit();
            editor.putString("token", res.token);
            editor.commit();

            // 跳转首页
            Intent in = new Intent(mcontext, IndexActivity.class);
            startActivity(in);


        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}.start();
和前面没有拦截器的区别
  • 在创建okhttp前先创建了拦截器

  • 在创建okhttp时,添加拦截器

NO_PROXY

一些App为了防止抓包,会使用okhttp的NO_PROXY参数来禁止Android手机设置系统代理,遇到这类情况可以使用Drony App进行无代理抓包,也可以利用小黄鸟等App进行本地VPN带抓包。

关于无代理抓包可以参考:Python爬虫之对app无代理模式下的抓包分析,以及针对这种的反爬优化方案 - Eeyhan - 博客园 (cnblogs.com)


retrofit

使用retrofit之前要做如下配置:

  • 引入,在build.gradle中 implementation “com.squareup.retrofit2:retrofit:2.9.0”

具体使用方法如下:

  1. 写接口,声明网络请求

package com.example.liyang;

package com.example.liyang;

import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
import retrofit2.http.GET;
import retrofit2.http.Query;

public interface HttpReq {
    // 向/api/v1/post 发送POST请求,表单格式 name=xx&pwd=xxx
    @POST("/api/v1/post")
    @FormUrlEncoded
    Call<ResponseBody> postLogin(@Field("name") String userName, @Field("pwd") String password);

    // 向/api/v2/xxx 发送GET请求,表单格式 ?age=xxx
    @GET("/api/v2/xxx")
    Call<ResponseBody> getInfo(@Query("age") String age);

    // 向/post/users 发送POST请求 json字符串格式 {name:xxxx,age:123}
    @POST("/post/users")
    Call<ResponseBody> postLoginJson(@Body RequestBody body);
}
  1. 调用接口,给接口传参,发送请求

new Thread() {
    @Override
    public void run() {
        Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.31.201:9999/").build();
        HttpReq httpRequest = retrofit.create(HttpReq.class);        
        
        // http://192.168.31.201:9999/api/v1/post  
        // name=xx&pwd=xxx
        Call<ResponseBody> call = httpRequest.postLogin("wupeiqi", "666");
        try {
            ResponseBody responseBody = call.execute().body();
            String responseString = responseBody.string();
            Log.i("登录", responseString);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}.start();
new Thread() {
    @Override
    public void run() {
        // http://192.168.31.201:9999/api/v2/xxx?age=123
        Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.31.201:9999/").build();
        HttpReq req = retrofit.create(HttpReq.class);
        Call<ResponseBody> call = req.getInfo("123");
        try {
            ResponseBody responseBody = call.execute().body();
            String responseString = responseBody.string();
            Log.e("Retrofit返回的结果", responseString);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}.start();
new Thread() {
    @Override
    public void run() {
        Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.31.201:9999/").build();
        HttpReq httpRequest = retrofit.create(HttpReq.class);

        JSONObject json = new JSONObject(dataMap);
        String jsonString = json.toString();
        RequestBody form = RequestBody.create(MediaType.parse("application/json;charset=utf-8"),jsonString);
        
        // http://192.168.31.201:9999/post/users  
        // {username:"root",password:"123456","sign":"xxxxdfsdfsdfsdfdfd"}
        Call<ResponseBody> call = httpRequest.postLoginJson(form);
        try {
            ResponseBody responseBody = call.execute().body();
            String responseString = responseBody.string();
            Log.i("登录", responseString);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}.start();

注意事项:

  • retrofit是在okhttp上的进一步封装,特点是可以把请求的url分成几段,分别在不同的地方来指定,可以更灵活地配置url

  • 当遇到了用retrofit写的接口时,不再是去找实现它的类,而是去找调用它的类,拼接出请求的url和找到传递的参数

  • 代码比较冗长,重点关注定义接口的方式,决定了请求的样式,以及调用接口的方式,决定了传递的参数,两部分合起来决定请求的url


GSON

类似python中的json模块,用于序列化对象和反序列化json字符串的,使用Gson前需要引入

implementation ‘com.google.code.gson:gson:2.8.6’
  • 序列化,对象 -> 字符串类型

class HttpContext{
    public int code;
    public String message;
    
    public HttpContext(int code,String msg){
        this.code = code;
        this.message = msg;
    }
}
HttpContext obj = new HttpContext(1000,"成功");
String dataString = new Gson().toJson(obj); // '{"code":1000,"Message":"成功"}'
  • 反序列化,字符串 -> 对象

// JSON格式
String dataString = "{\"status\": true, \"token\": \"fffk91234ksd\", \"name\": \"武沛齐\"}";
class HttpResponse{
    public boolean status;
    public String token;
    public String name;
}
HttpResponse obj = new Gson().fromJson(dataString,HttpResponse.class);
// obj.status  obj.name  obj.token
  • 如果反序列化时,存在字典嵌套

String responseString = "{\"origin\": \"110.248.149.62\",\"url\": \"https://www.httpbin.org/post\",\"dataList\":[{\"id\":1,\"name\":\"一个小黑\"},{\"id\":2,\"name\":\"eric\"}]}";
class Item {
    public int id;
    public String name;
}
public class HttpResponse {
    public String url;
    public String origin;
    public ArrayList<Item> dataList;
}
HttpResponse obj = new Gson().fromJson(dataString, HttpResponse.class);
// obj.url  obj.origin
Item objItem = obj.dataList.get(1);
// objItem.name

保存到XML文件

保存到手机上的位置:

/data/data/com.example.liyang/shared_prefs/sp_city.xml

保存 token

SharedPreferences sp = getSharedPreferences("sp_city", MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.putString("token","111111");
editor.commit();

删除 token

SharedPreferences sp = getSharedPreferences("sp_city", MODE_PRIVATE);
SharedPreferences.Editor editor = sp.edit();
editor.remove("token");
editor.commit();

读取 token

SharedPreferences sp = getSharedPreferences("sp_city", MODE_PRIVATE);
String token = sp.getString("token","");
  • 记住SharedPreferences对象是用来读写手机App本地的XML文件的

  • XML文件和Cookies很像,要么是本地算法生成的,要么是HTTP请求服务器返回的。


总结

jadx、jeb去反编译安卓代码:

  • 关键字搜索

  • 根据请求的流程逐步排查

  • java调用

注意:小app了解上面内容就能完全将其逆向出来。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 新闻app是一款基于Android平台的小型项目应用程序,它主要用于展示各类新闻内容,为用户提供便捷的阅读体验。该项目的源码包含了应用程序的基本框架和功能实现,方便开发者进行二次开发和定制。 新闻app的源码主要包含以下几个方面的内容: 1. 用户界面设计:源码中包含了新闻app的界面布局和样式,开发者可以根据自己的需要进行修改和美化。用户界面通常包括新闻列表、新闻详情页、分类标签等,开发者可以自由设计并添加其他功能模块。 2. 数据获取与展示:源码中实现了与服务器进行数据交互的功能,通过网络请求获取新闻数据,并在界面上展示出来。开发者可以根据需要修改数据请求接口和解析方式,实现与自己的服务器交互。 3. 新闻分类与搜索:源码中提供了新闻分类和搜索功能的实现,用户可以根据自己的兴趣和需求选择不同的新闻分类进行浏览,也可以通过搜索关键词进行精确定位。 4. 用户交互与分享:源码中包含了用户的登录注册功能和新闻内容的分享功能,用户可以通过登录账号进行个性化设置和收藏喜欢的新闻内容,也可以将新闻分享到社交媒体上与他人交流。 总之,新闻app源码是一个基础框架,开发者可以在此基础上进行二次开发和定制,根据自己的需求添加功能模块和美化界面,实现自己独特的新闻应用。 ### 回答2: Android新闻App是一个基于Android平台开发的小型项目,它的主要功能是提供最新的新闻内容给用户,并且用户可以进行浏览、搜索和分享等操作。下面是这个项目的一些关键特点和所需的源码组成部分: 1. 特点: - 用户界面友好,交互性强,提供舒适的浏览体验; - 支持实时更新,提供最新的新闻内容; - 具备搜索功能,方便用户查找感兴趣的新闻; - 支持新闻分享功能,方便用户将新闻分享给朋友; - 具备图文混排的能力,可以展示新闻的文字和图片。 2. 源码组成部分: - 主界面布局代码:定义了App的整体布局结构,包括顶部导航栏、底部工具栏和新闻显示区域等。 - 数据源代码:负责获取新闻数据,可以通过API接口获取最新的新闻内容,也可以从本地数据库获取已缓存的新闻数据。 - 新闻列表适配器代码:用于将新闻数据展示在界面上,包括标题、描述和图片等。 - 新闻详情界面代码:用于显示单篇新闻的详细内容,包括标题、正文和相关图片等。 - 搜索功能代码:实现了按关键搜索新闻的功能,可以在已有的新闻数据中进行筛选。 - 分享功能代码:集成了社交媒体的分享SDK,方便用户将新闻内容分享给朋友。 - 图片加载和缓存代码:处理了新闻中的图片加载和本地缓存,提高了图片加载速度和用户体验。 通过以上的源码组成部分,可以完成一个基本的新闻App,用户可以在界面上浏览最新的新闻内容,进行搜索和分享操作。这个小项目可以帮助开发者理解Android开发框架和开发方式,提高编码能力和UI设计能力。 ### 回答3: 新闻app是基于Android平台开发的一个小型应用程序,可以提供用户各种最新的新闻资讯。以下是关于这个项目的源码介绍。 该项目源码主要由Java语言编写,使用了Android Studio作为开发工具。代码结构清晰,包含了主要的几个模块。 1. 用户界面模块:这个模块负责显示新闻列表和新闻详情等信息,主要包含布局文件和相应的逻辑代码。列表界面使用RecyclerView控件展示新闻列表,详情界面使用WebView展示新闻内容。 2. 网络请求模块:这个模块负责与后台服务器进行数据交互,使用了Android的HttpURLConnection类来发送请求和接收响应。请求参数可以根据实际需要进行修改,例如可以根据新闻类别进行请求。 3. 数据解析模块:这个模块负责解析从服务器返回的JSON格式的数据,转换成Java对象供应用程序使用。可以使用Android提供的JSON解析库,如Gson。 4. 数据存储模块:这个模块负责缓存新闻数据,以提高应用程序的响应速度。可以使用SharedPreferences或SQLite数据库来存储新闻数据。同时也可以使用图片缓存库,如Glide或Picasso来缓存新闻图片。 5. 用户交互模块:这个模块负责处理用户的交互行为,例如点击新闻列表项跳转到新闻详情界面,下拉刷新获取最新数据等。可以使用Android提供的相关控件和事件监听器来实现用户交互。 除了以上几个主要的模块,还可以根据需要添加其他功能,例如搜索栏、分享按钮等。 总体来说,这个新闻app的源码提供了一个完整的开发框架,初学者可以通过阅读和理解源码来学习Android应用程序的开发流程和一些常用技术。同时,也可以根据实际需求进行二次开发,添加新的功能和改进用户体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值