当前位置:   article > 正文

Andriod APP逆向——网络请求_app逆向是搜索不到关键请求参数

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文件,写入以下内容:

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

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

  1. String username = String.valueOf(txtUser.getText());
  2. String password = String.valueOf(txtPwd.getText());
  3. HashMap<String, String> dataMap = new HashMap<String, String>();
  4. dataMap.put("username", username);
  5. dataMap.put("password", password);
  1. // 1. 将用户名和密码 发送到后台 (第三方 OKHttp)
  2. // 1.1 引入依赖 bulid.gradle 文件中 引入 implementation "com.squareup.okhttp3:okhttp:4.9.1"
  3. // 1.2 在Android中,默认不允许发送网络请求; 在AndroidManifest.xml 中 配置 <uses-permission android:name="android.permission.INTERNET"/>
  4. // 1.3 调用 OKHttp包去发送请求。
  5. // 1.3.1 创建 GET 请求 ---> [创建线程]--> 否则报错
  6. // 创建线程并执行 run 方法
  7. new Thread(){
  8. @Override
  9. public void run(){
  10. OkHttpClient client = new OkHttpClient();
  11. Request request = new Request.Builder().url("https://api.luffycity.com/api/v1/course/actual/?category_id=1").build();
  12. Call call = client.newCall(request);
  13. try {
  14. Response response = call.execute(); // 发送请求
  15. ResponseBody body = response.body();
  16. String resString = body.string();
  17. Log.i("登录界面--->", resString);
  18. // 获取数据并处理
  19. } catch (IOException e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. }.start();

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

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

username=xxxx&password=123
  1. new Thread(){
  2. @Override
  3. public void run(){
  4. OkHttpClient client = new OkHttpClient();
  5. // 构建请求体
  6. FormBody form = new FormBody.Builder().add("username", "xxxx").add("password", "123").build();
  7. // form ---> post 请求体
  8. Request request = new Request.Builder().url("https://api.luffycity.com/api/v1/auth/password/login/?loginWay=password").post(form).build();
  9. Call call = client.newCall(request);
  10. try {
  11. Response response = call.execute(); // 发送请求
  12. ResponseBody body = response.body();
  13. String resString = body.string();
  14. Log.i("登录界面--->", resString);
  15. // 获取数据并处理
  16. } catch (IOException e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. }.start();
json格式
  • 数据包以json格式提交,例如:

  1. {
  2. username:"xxxx",
  3. password:123,
  4. }
  1. new Thread() {
  2. @Override
  3. public void run() {
  4. OkHttpClient client = new OkHttpClient();
  5. // 构建请求体
  6. // FormBody form = new FormBody.Builder().add("username", "xxxx").add("password", "123").build();
  7. //JSONObject json = new JSONObject();
  8. //json.put("username", "dk");
  9. //json.put("password", "123");
  10. JSONObject json = new JSONObject(dataMap);
  11. String jsonString = json.toString();
  12. RequestBody form = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), toString());
  13. // form ---> post 请求体
  14. Request request = new Request.Builder().url("https://api.luffycity.com/api/v1/auth/password/login/?loginWay=password").post(form).build();
  15. Call call = client.newCall(request);
  16. try {
  17. Response response = call.execute(); // 发送请求
  18. ResponseBody body = response.body();
  19. String resString = body.string();
  20. // Json反序列化,字符串转换成对象.
  21. // json.load(json字符串) --> python
  22. // Android 需要 GSON --> 引入 (implementation "com.google.code.gson:gson:2.8.6")
  23. //
  24. String responseString = "{\"token\": \"dsajkdhjksald\", \"url\": \"https://www.httpbin.org/post/\",\"dataList\":[{\"id\": 1, \"name\": \"dk\"},{\"id\": 2, \"name\": \"dkk\"}]}";
  25. HttpResponse res = new Gson().fromJson(responseString, HttpResponse.class);
  26. 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'}]}
  27. // 保存起来:cookies, localstorage
  28. // andorid -> xml 文件 -> data/data/com.example.myapplication/shared_prefs
  29. SharedPreferences sharedPreferences = getSharedPreferences("sp_MyApplication", MODE_PRIVATE);
  30. SharedPreferences.Editor editor= sharedPreferences.edit();
  31. editor.putString("token", res.token);
  32. editor.commit();
  33. // 跳转首页
  34. Intent in = new Intent(mcontext, IndexActivity.class);
  35. startActivity(in);
  36. } catch (IOException e) {
  37. e.printStackTrace();
  38. }
  39. }
  40. }.start();
两者区别在于:
  • form表单格式使用的是FormBody构造请求体

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

请求拦截器

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

  1. // 4. 发送请求拦截器
  2. Interceptor interceptor = new Interceptor() {
  3. @NonNull
  4. @Override
  5. public Response intercept(@NonNull Chain chain) throws IOException {
  6. String sign = "sfsadsadsa";
  7. // 请求还未发送,在请求体中增加了一个请求头
  8. Request request = chain.request().newBuilder().addHeader("x-gorgon", sign).build();
  9. Response response = chain.proceed(request);
  10. return response;
  11. }
  12. };
  13. new Thread() {
  14. @Override
  15. public void run() {
  16. // 加入拦截器
  17. OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
  18. // 构建请求体
  19. // FormBody form = new FormBody.Builder().add("username", "xxxx").add("password", "123").build();
  20. //JSONObject json = new JSONObject();
  21. //json.put("username", "dk");
  22. //json.put("password", "123");
  23. JSONObject json = new JSONObject(dataMap);
  24. String jsonString = json.toString();
  25. RequestBody form = RequestBody.create(MediaType.parse("application/json;charset=utf-8"), toString());
  26. // form ---> post 请求体
  27. Request request = new Request.Builder().url("https://api.luffycity.com/api/v1/auth/password/login/?loginWay=password").post(form).build();
  28. Call call = client.newCall(request);
  29. try {
  30. Response response = call.execute(); // 发送请求
  31. ResponseBody body = response.body();
  32. String resString = body.string();
  33. // Json反序列化,字符串转换成对象.
  34. // json.load(json字符串) --> python
  35. // Android 需要 GSON --> 引入 (implementation "com.google.code.gson:gson:2.8.6")
  36. //
  37. String responseString = "{\"token\": \"dsajkdhjksald\", \"url\": \"https://www.httpbin.org/post/\",\"dataList\":[{\"id\": 1, \"name\": \"dk\"},{\"id\": 2, \"name\": \"dkk\"}]}";
  38. HttpResponse res = new Gson().fromJson(responseString, HttpResponse.class);
  39. 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'}]}
  40. // 保存起来:cookies, localstorage
  41. // andorid -> xml 文件 -> data/data/com.example.myapplication/shared_prefs
  42. SharedPreferences sharedPreferences = getSharedPreferences("sp_MyApplication", MODE_PRIVATE);
  43. SharedPreferences.Editor editor= sharedPreferences.edit();
  44. editor.putString("token", res.token);
  45. editor.commit();
  46. // 跳转首页
  47. Intent in = new Intent(mcontext, IndexActivity.class);
  48. startActivity(in);
  49. } catch (IOException e) {
  50. e.printStackTrace();
  51. }
  52. }
  53. }.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. 写接口,声明网络请求

  1. package com.example.liyang;
  2. package com.example.liyang;
  3. import okhttp3.RequestBody;
  4. import okhttp3.ResponseBody;
  5. import retrofit2.Call;
  6. import retrofit2.http.Body;
  7. import retrofit2.http.Field;
  8. import retrofit2.http.FormUrlEncoded;
  9. import retrofit2.http.POST;
  10. import retrofit2.http.GET;
  11. import retrofit2.http.Query;
  12. public interface HttpReq {
  13. // 向/api/v1/post 发送POST请求,表单格式 name=xx&pwd=xxx
  14. @POST("/api/v1/post")
  15. @FormUrlEncoded
  16. Call<ResponseBody> postLogin(@Field("name") String userName, @Field("pwd") String password);
  17. // 向/api/v2/xxx 发送GET请求,表单格式 ?age=xxx
  18. @GET("/api/v2/xxx")
  19. Call<ResponseBody> getInfo(@Query("age") String age);
  20. // 向/post/users 发送POST请求 json字符串格式 {name:xxxx,age:123}
  21. @POST("/post/users")
  22. Call<ResponseBody> postLoginJson(@Body RequestBody body);
  23. }
  1. 调用接口,给接口传参,发送请求

  1. new Thread() {
  2. @Override
  3. public void run() {
  4. Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.31.201:9999/").build();
  5. HttpReq httpRequest = retrofit.create(HttpReq.class);
  6. // http://192.168.31.201:9999/api/v1/post
  7. // name=xx&pwd=xxx
  8. Call<ResponseBody> call = httpRequest.postLogin("wupeiqi", "666");
  9. try {
  10. ResponseBody responseBody = call.execute().body();
  11. String responseString = responseBody.string();
  12. Log.i("登录", responseString);
  13. } catch (Exception e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. }.start();
  1. new Thread() {
  2. @Override
  3. public void run() {
  4. // http://192.168.31.201:9999/api/v2/xxx?age=123
  5. Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.31.201:9999/").build();
  6. HttpReq req = retrofit.create(HttpReq.class);
  7. Call<ResponseBody> call = req.getInfo("123");
  8. try {
  9. ResponseBody responseBody = call.execute().body();
  10. String responseString = responseBody.string();
  11. Log.e("Retrofit返回的结果", responseString);
  12. } catch (Exception e) {
  13. e.printStackTrace();
  14. }
  15. }
  16. }.start();
  1. new Thread() {
  2. @Override
  3. public void run() {
  4. Retrofit retrofit = new Retrofit.Builder().baseUrl("http://192.168.31.201:9999/").build();
  5. HttpReq httpRequest = retrofit.create(HttpReq.class);
  6. JSONObject json = new JSONObject(dataMap);
  7. String jsonString = json.toString();
  8. RequestBody form = RequestBody.create(MediaType.parse("application/json;charset=utf-8"),jsonString);
  9. // http://192.168.31.201:9999/post/users
  10. // {username:"root",password:"123456","sign":"xxxxdfsdfsdfsdfdfd"}
  11. Call<ResponseBody> call = httpRequest.postLoginJson(form);
  12. try {
  13. ResponseBody responseBody = call.execute().body();
  14. String responseString = responseBody.string();
  15. Log.i("登录", responseString);
  16. } catch (Exception e) {
  17. e.printStackTrace();
  18. }
  19. }
  20. }.start();

注意事项:

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

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

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


GSON

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

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

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

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

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

保存到XML文件

保存到手机上的位置:

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

保存 token

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

删除 token

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

读取 token

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

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


总结

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

  • 关键字搜索

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

  • java调用

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/115223
推荐阅读
相关标签
  

闽ICP备14008679号