安卓开发

环境搭建

win系统在安装和操作之前,请提前开始电脑的vt-x,虚拟机开启方法如下:

其他win系统请参考: https://mumu.163.com/include/16v1/2016/06/27/21967_625825.html

安装android studio

https://developer.android.google.cn/studio/archive.html

启动和配置

如果电脑已开启VT-X,安装显示:

如果电脑未开启VT-X,安装显示(没关系,可继续操作):

vt-x,用于电脑上创建虚拟设备 or 模拟器,如果没有的话,可以用真机来进行开发测试。

新建项目

环境变量

关于platform-tools和tools目录,在sdk的安装目录下有:

  • platform-tools,内部其实就是adb和fastboot

  • tools,内部可以运行内置模拟器(可选)

C:\AndroiStudio\SDK\tools            【可选】
C:\AndroiStudio\SDK\platform-tools   【之前配置过】

打开SDK安装目录,将以下两个目录添加到环境变量:

运行项目

真机(建议)

手机开启开发者模式 & USB调试,并且用数据线和电脑连接。

当你一插线,手机上会提示授权。

稍等片刻,此时在android studio中会读取到你的手机设备。

如果没有读取到,请在手机上【撤销USB调试授权】,然后再重新插入USB,重新授权。

当读取到设备之后,可以点击运行(第一次慢):

  • 在手机上安装app

  • 运行app

 

模拟器

在 网易mumu 或 雷电模拟器 上去运行我们自己开发的APP。

模拟器启动起来之后,android studio会自动连接并读取到模拟器设备。

如果读取不到,请在终端重启adb,以便于读取到设备列表。

如果没有找到设备,可以重新执行命令:

adb kill-server
adb start-server

特殊的,在win系统上,mumu模拟器可能无法读取到设备,此时可以执行adb命令连接:

adb connect 127.0.0.1:7555

然后,就可以连接上模拟器。

当读取到设备之后,可以点击运行(第一次慢):

  • 在手机上安装app

  • 运行app

关于报错

如果点击运行出现:build-tools-32.0.rc 。。。...

安卓UI和后台逻辑

目标:快速开发一个安卓页面(用户登录&跳转)

安卓UI

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
​
​
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="216dp"
        android:layout_marginTop="150dp"
        android:background="#dddddd"
        android:orientation="vertical">
​
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:text="用户登录"
            android:textAlignment="center"
            android:textSize="25dp"></TextView>
​
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="15dp"
            android:paddingRight="15dp">
​
            <TextView
                android:layout_width="60dp"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="用户名"></TextView>
​
            <EditText
                android:layout_width="match_parent"
                android:layout_height="match_parent">
​
            </EditText>
​
        </LinearLayout>
​
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="15dp"
            android:paddingRight="15dp">
​
            <TextView
                android:layout_width="60dp"
                android:layout_height="match_parent"
                android:gravity="center"
                android:text="密码"></TextView>
​
            <EditText
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:inputType="textPassword">
​
            </EditText>
​
        </LinearLayout>
​
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="60dp"
            android:gravity="center">
​
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="5dp"
                android:text="登录">
​
            </Button>
​
            <Button
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="5dp"
                android:text="重置">
​
            </Button>
​
        </LinearLayout>
​
    </LinearLayout>
​
</LinearLayout>

后台逻辑

package com.nb.liyang;
​
import androidx.appcompat.app.AppCompatActivity;
​
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
​
import com.google.gson.Gson;
​
import java.io.IOException;
import java.net.Proxy;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
​
import okhttp3.Call;
import okhttp3.FormBody;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
​
public class MainActivity extends AppCompatActivity {
​
    private TextView txtUser, txtPwd;
    private Button btnLogin, btnReset;
​
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
​
        initView();
​
        initListener();
​
    }
​
    private void initView() {
        // 先找到所有的有用的标签
        txtUser = findViewById(R.id.txt_user);
        txtPwd = findViewById(R.id.txt_pwd);
        btnLogin = findViewById(R.id.btn_login);
        btnReset = findViewById(R.id.btn_reset);
    }
​
    private void initListener() {
        btnReset.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 点击btn_reset标签,执行方法
                txtUser.setText("");
                txtPwd.setText("");
            }
        });
​
        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                loginForm();
            }
        });
    }
​
    /**
     * 点击登录,执行此方法
     */
    private void loginForm() {
        // 1.获取用户名和密码
        /*
        String username = String.valueOf(txtUser.getText());
        String password = String.valueOf(txtPwd.getText());
        */
​
        // 2.校验用户名和密码不能为空
        // root123
        StringBuilder sb = new StringBuilder();
​
        // {username:"root",password:"123"}
        HashMap<String, String> dataMap = new HashMap<String, String>();
​
        boolean hasEmpty = false;
​
        // {username:txtUser, password:txtPwd}
        HashMap<String, TextView> mapping = new HashMap<String, TextView>();
        mapping.put("username", txtUser);
        mapping.put("password", txtPwd);
        for (Map.Entry<String, TextView> entry : mapping.entrySet()) {
            String key = entry.getKey();
            TextView obj = entry.getValue();
            String value = String.valueOf(obj.getText());
            if (value.trim().isEmpty()) {
                hasEmpty = true;
                break;
            }
​
            dataMap.put(key, value);
            sb.append(value);
        }
​
        if (hasEmpty) {
            Toast.makeText(this, "输入内容不能为空", Toast.LENGTH_SHORT).show();
            return;
        }
​
        // sb="root123"
        // dataMap={username:"root",password:"123"}
​
        // 3.用md5做一个签名
        // dataMap={username:"root",password:"123","sign":"xxxxdfsdfsdfsdfdfd"}
        String signString = md5(sb.toString());
        dataMap.put("sign", signString);
​
        // com.nb.liyang E/加密后的结果:: cb2921a386719d7467412b5573973529
        Log.e("加密后的结果:", signString);
​
​
        // 4.将三个值:用户名、密码、签名 网络请求发送API(校验)
        // okhttp,安装 & 引入 & 使用(创建一个线程去执行)
        // 5.获取返回值
        new Thread() {
            @Override
            public void run() {
                // 线程执行的内容
                // user=xxx&pwd=xxx&sign=xxxx
                OkHttpClient client = new OkHttpClient.Builder().build();
                FormBody form = new FormBody.Builder()
                        .add("user", dataMap.get("username"))
                        .add("pwd", dataMap.get("password"))
                        .add("sign", dataMap.get("sign")).build();
                Request req = new Request.Builder().url("http://192.168.0.6:9999/login").post(form).build();
                Call call = client.newCall(req);
                try {
                    Response res = call.execute();
                    ResponseBody body = res.body();
                    String dataString = body.string();
                    // {"status": true, "token": "dafkauekjsoiuksjdfuxdf", "name": "武沛齐"}
                    
                    // 反序列化
                    HttpResponse obj = new Gson().fromJson(dataString,HttpResponse.class);
                    if(obj.status){
                        // token保存本地xml文件
                        // /data/data/com.nb.liyang
                        SharedPreferences sp = getSharedPreferences("sp_city", MODE_PRIVATE);
                        SharedPreferences.Editor editor = sp.edit();
                        editor.putString("token",obj.token);
                        editor.commit();
​
                    }
                    Log.e("请求发送成功", dataString);
​
                } catch (IOException ex) {
                    Log.e("Main", "网络请求异常");
                }
            }
        }.start();
​
    }
​
​
    /**
     * md5加密
     *
     * @param dataString 待加密的字符串
     * @return 加密结果
     */
    private String md5(String dataString) {
        try {
            MessageDigest instance = MessageDigest.getInstance("MD5");
            byte[] nameBytes = instance.digest(dataString.getBytes());
​
            // 十六进制展示
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < nameBytes.length; i++) {
                int val = nameBytes[i] & 255; 
                if (val < 16) {
                    sb.append("0");
                }
                sb.append(Integer.toHexString(val));
            }
            return sb.toString();
        } catch (Exception e) {
            return null;
        }
​
    }
}

API

pip install flask
from flask import Flask,jsonify,request
​
app = Flask(__name__)
​
​
# http://127.0.0.1:5000/login
@app.route("/login", methods=["POST", "GET"])
def login():
    # 1.接收请求数据
    # 2.用户名和密码校验
    print(request.form)
    # 3.返回用户信息
    return jsonify({"status": True, 'token': "87a0cc3b-440f-4764-81b0-ff179dedd7c4"})
​
​
if __name__ == '__main__':
    # 局域网内,其他设备通过输入网址访问页面
    app.run(host="192.168.0.5")
​

网络请求

1.引入,在build.gradle中 implementation "com.squareup.okhttp3:okhttp:4.9.1"
2.配置,在AndroidManifest.xml中 
<uses-permission android:name="android.permission.INTERNET"/>
3.支持http(仅测试使用)

表单格式

user=wupeiqi&age=99&size18
new Thread() {
    @Override
    public void run() {
        OkHttpClient client = new OkHttpClient();
        
        FormBody form = new FormBody.Builder()
                .add("user", dataMap.get("username"))
                .add("pwd", dataMap.get("password"))
                .add("sign", dataMap.get("sign")).build();
​
        Request req = new Request.Builder().url("http://192.168.0.6:9999/login").post(form).build();
        
        Call call = client.newCall(req);
        try {
            Response res = call.execute();
            ResponseBody body = res.body();
            // 字符串={"status": true, "token": "fffk91234ksdujsdsd", "name": "武沛齐"}
            String dataString = body.string();
​
            // Log.e("MDS", "请求成功获取返回值=" + dataString);
        } catch (IOException ex) {
            Log.e("MDS", "网络请求错误");
        }
    }
}.start();

json格式

{
    name:"wupeiqi",
    age:18,
    size:18
}
new Thread() {
    @Override
    public void run() {
        OkHttpClient client = new OkHttpClient();
			
        // dataMap = {"username":"wupeiqi","password":"123","sign":"用户名和密码的md5值"}
        JSONObject json = new JSONObject(dataMap);
        String jsonString = json.toString();
        
        // RequestBody form = RequestBody.create(MediaType.parse("application/json;charset=utf-8"),jsonString);
         RequestBody form = RequestBody.create(MediaType.parse("application/json;charset=utf-8"),jsonString);

        Request req = new Request.Builder().url("http://192.168.0.6:9999/login").post(form).build();
        Call call = client.newCall(req);
        try {
            Response res = call.execute();
            ResponseBody body = res.body();
            String dataString = body.string();
            Log.i("登录", dataString);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}.start();

请求拦截器

假设开发app,发送10个请求,每个请求中需要携带特殊的请求头:xxxx。

将所有请求公共的操作都放在拦截器里面。

// 创建拦截器
Interceptor interceptor = new Interceptor() {

    @Override
    public Response intercept(Chain chain) throws IOException {

        // 1988812212 + 固定字符串 => md5加密
        Request request = chain.request().newBuilder().addHeader("ts", "1988812212").addHeader("sign", "xxxx").build();

        // 请求前
        Response response = chain.proceed(request);
        // 请求后
        return response;
    }
};


// 4.将三个值:用户名、密码、签名 网络请求发送API(校验)
// okhttp,安装 & 引入 & 使用(创建一个线程去执行)
// 5.获取返回值
new Thread() {
    @Override
    public void run() {
        // 线程执行的内容
        // user=xxx&pwd=xxx&sign=xxxx
        // OkHttpClient client = new OkHttpClient.Builder().build();
        OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();
        FormBody form = new FormBody.Builder()
            .add("user", dataMap.get("username"))
            .add("pwd", dataMap.get("password"))
            .add("sign", dataMap.get("sign")).build();
        Request req = new Request.Builder().url("http://192.168.0.6:9999/login").post(form).build();
        Call call = client.newCall(req);
        try {
            Response res = call.execute();
            ResponseBody body = res.body();
            String dataString = body.string();
            Log.e("请求发送成功", dataString);

        } catch (IOException ex) {
            Log.e("Main", "网络请求异常");
        }
    }
}.start();

逆向时:

  • 传入了好多参数:15参数且与其他请求重合比较多,可能是拦截器中实现。

  • jadx反编译 + 关键字搜索: sign Interceptor

retrofit

内部封装了okhttp,让你用的更加的简单(B站)。

引入

// implementation "com.squareup.okhttp3:okhttp:4.9.1"

implementation "com.squareup.retrofit2:retrofit:2.9.0"

写接口,声明网络请求

package com.nb.mds;

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?age=999
    @GET("/api/v2/xxx")
    Call<ResponseBody> getInfo(@Query("age") String age);
		
    // 向/post/users 发送POST请求 {name:xxxx,age:123}
    @POST("/post/users")
    Call<ResponseBody> postLoginJson(@Body RequestBody body);

    @GET("/index")
    Call<ResponseBody> getIndex(@Query("age") String age);
}

发送请求

new Thread() {
    @Override
    public void run() {
        // http://api.baidu.com/api/v2/xxx?age=123
        Retrofit retrofit = new Retrofit.Builder().baseUrl("http://api.baidu.com/").build();
        HttpReq req = retrofit.create(HttpReq.class);
        Call<ResponseBody> call = req.getInfo("123");
		
        // Call<ResponseBody> call = retrofit.create(HttpReq.class).getInfo("123");
        ResponseBody responseBody = retrofit.create(HttpReq.class).getInfo("123").execute().body();
        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("https://www.httpbin.org/").build();
        HttpRequest httpRequest = retrofit.create(HttpRequest.class);		
		
        // https://www.httpbin.org/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() {
        Retrofit retrofit = new Retrofit.Builder().baseUrl("https://www.httpbin.org/").build();
        HttpRequest httpRequest = retrofit.create(HttpRequest.class);

        JSONObject json = new JSONObject(dataMap);
        String jsonString = json.toString();
        RequestBody form = RequestBody.create(MediaType.parse("application/json;charset=utf-8"),jsonString);
		
        // https://www.httpbin.org/post/users  
        // {username:"root",password:"123","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();

反序列化

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,"成功");

# json.dumps
String dataString = new Gson().toJson(obj); // '{"code":1000,"Message":"成功"}'

反序列化,字符串 -> 对象

v = '{"code":1000,"Message":"成功"}'
String v = "{\"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

 

class HttpResponse{
    public boolean status;
    public String token;
}

String dataString = "{"status":true,"token":"b96efd24-e323-4efd-8813-659570619cde"}";

HttpResponse obj = new Gson().fromJson(dataString,HttpResponse.class);
obj.status
obj.token

保存到XML文件

保存到手机上:/data/data/com.nb.s5day08

保存

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

删除

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

读取

SharedPreferences sp = getSharedPreferences("sp_city", MODE_PRIVATE);
String token = sp.getString("token","");

注意:后期逆向时经常使用,放在xml中的一般都是app刚启动时、刚登录时。

 

posted @ 2023-08-28 22:02  屠魔的少年  阅读(16)  评论(0)    收藏  举报