【逆向学习】Java常见加密和安卓网络请求

Java常见加密和安卓网络请求

Java常见加密(逆向)

隐藏字节的情况

有时候我们逆向某个APP算法的时候,比如说有个参数叫ouo,然后我们尝试直接jax搜索ouo搜不出来,这种情况有可能是如下面内容采用字节表示ouo参数的特殊方式,有效避免了关键词泄露

package com.ouo.ouo_skill.study;

import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
import java.util.TreeMap;

public class JavaNormalEncrypt {

    /**
     * 1.隐藏得字节
     * @param args
     */
    public static void main(String[] args) throws UnsupportedEncodingException {
        TreeMap<String,String> map=new TreeMap<>();
        //正常赋值是这样得
//        String a="ouo1";
//        map.put(a,"呜啦啦");
        map.put("OUO2","呜啦啦");
        map.put("OUO3","呜啦啦");
        map.put("OUO4","呜啦啦");
        //隐藏赋值--------隐藏a键值,改为字节存入
        String a=new String(new byte[]{111,117,111,49});
        System.out.println(a);  //ouo1
        map.put(a,"呜啦啦");
    }

}

UUID

Java代码中

/**
 * UUID格式的字符串
 */
public static void main(String[] args) {
    //27a76e35-dc2a-4434-8537-4c2d480894ba
    //a67a6066-0fa2-4ad8-a6fa-70d6dfecb8f8
    String uid= UUID.randomUUID().toString();
    System.out.println(uid);
}

安卓开发如何利用UUID

  • 随机性,动态的不一样(直接用UUID工具生成类就行)
  • 非随机+固定+重装+变化
    • 首次运行APP,检测手机中的某个文件是否有值,没有则生成一个随机的数据保存进去
    • 再次运行app,文件中的数据加载app中。
    • 发送请求的时候,就将那个随机的数据携带上了(只有卸载重装APP才重置)
  • 非随机+固定+重装+固定+换手机+改变
    • 首次运行app,获取手机指纹信息(CPU,硬盘,各种ID,各种信息)发送后端API,设备注册,硬件信息-UUID
    • 以后运行app,指纹信息(CPU,硬盘,各种ID,各种信息)发送后端API,获取到UUID
    • 返回
    • 再次发送请求的时候(UUID还是固定的)
    • 只能考换手机参数才能变动UUID
  • 写死
    • 那最简单了

切记,不要一昧非要逆向找到代码

情景:必须逆向找到设置位置,手机的什么信息发送

  • 关键字搜索:info
  • Java中如何生成UUID
UUID.toString=function(){
    var res=this.toString
    console.log(res)   + //可以输出调用栈
    return res;
}

随机值

  • 数字+26个英文字母 -》16进制字符串
  • 数字+ ABCDEF -》16进制字符串(绝大多数)
    /**
     * 随机值
     */
    public static void main(String[] args) {
        //随机生成80位,10个字节
        BigInteger v4=new BigInteger(80,new SecureRandom());
        System.out.println(v4);
        //让字节以16进制展示
        String res =v4.toString(16);
        System.out.println(res);
    }

python

import random

# py 3.9
# data=random.randbytes(10)
# 3.8报错:module 'random' has no attribute 'randbytes'

data=[random.randint(0,255) for i in range(10)]
print(data)

# ele_list=[]
# for item in data:
#     ele=hex(item)[2:]
#
# res="".join(ele_list)
# print(res)


print([hex(item)[2:] for item in data])

# 少位往前面补0
print([hex(item)[2:].rjust(2,"0") for item in data])

# 合并产生随机值
print("".join([hex(item)[2:].rjust(2,"0") for item in data]))

# 优化写法
print("".join(["%02x" % item for item in data]))

# 继续优化,一行解决
print("".join(["%02x" % random.randint(0,255) for i in range(10)]))

十六进制得字符串

python3.8及其以上版本

转换成十六进制字符串

  • 内置函数

    v1 =hex(199)
    
    '0xc7'
    
  • 字符串格式化

v2="%x" % (199,)
'c7'
v3="%02x" % (5,)
'05'
import random

data=[random.randint(0,255) for i in range(10)]
print("".join(["%02x" % item for item in data]))

时间戳

    /**
     * 时间戳
     * @param args
     */
    public static void main(String[] args) {
        Date date=new Date();
        //毫秒级别
        long time = date.getTime();
        System.out.println(time);
        //秒级别
        long mintime=time/1000;
        System.out.println(mintime);
        //1705391873
        //1705391873304
    }

python中

import time

# 秒
print(time.time())
print(int(time.time()))


# 毫秒
print(int(time.time()*1000))

MD5加密

  • java复杂:算法+字节转换成十六进制字符串
  • python简单 digest/hexdigest
    /**
     * MD5
     */
    public static void main(String[] args) {
        String name="OUO";
        try {
            MessageDigest instance =MessageDigest.getInstance("MD5");
            //方法1
            byte[] bytes = instance.digest(name.getBytes());
            //会修改
            instance.update("names".getBytes());
            //十六进制展示
            StringBuilder sb=new StringBuilder();
            for (int i = 0; i < bytes.length; i++) {
                int val=bytes[i]&255;   //负数转换为正数
                if (val<16){
                    sb.append("0");
                }
                sb.append(Integer.toHexString(val));
            }
            String hexData=sb.toString();
            //2d93661601c39bd554e66f467d2a87bd
            System.out.println(hexData);


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

python版

import hashlib

m=hashlib.md5("ouo".encode('utf-8'))
m.update("ouoX".encode('utf-8'))

v2=m.hexdigest()
print(v2)

提醒:

直接加密

加盐加密

sha-256加密

和上面MD5一样,就是吧里面的md5换成sha-256就行

AES加密

有时候抓包,请求体是一堆乱码。(加密)

  • 加密字节 0100100 ->转换成十六进制
  • 加密字节 +base64编码 +十六进制->字符串

Java代码

    public static void main(String[] args) {

        try {
            String DATA="ouo";
            String key="2d93661601c39bd554e66f467d2a87bd";
            String iv= "77b07a672d57d64c";

            //加密
            byte[] bytes = key.getBytes();
            SecretKeySpec secretKeySpec=new SecretKeySpec(bytes,"AES");
            IvParameterSpec ivParameterSpec=new IvParameterSpec(iv.getBytes());

            Cipher cipher=Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec,ivParameterSpec);
            byte[] encrypted = cipher.doFinal(DATA.getBytes());
            System.out.println(Arrays.toString(encrypted));


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

    }

python代码

from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

KEY="2d93661601c39bd554e66f467d2a87bd"
IV="77b07a672d57d64c"

def aes_encrypt(data_string):
    aes=AES.new(
        key=KEY.encode('utf-8'),
        mode=AES.MODE_CBC,
        iv=IV.encode('utf-8')
    )
    raw=pad(data_string.encode('utf-8'),AES.block_size)
    return aes.encrypt(raw)


data=aes_encrypt("OUO")
print(data)
print([i for i in data])
print("".join([str(i) for i in data]))

gzip压缩

Java代码


    /**
     * GZIP压缩
     * @param args
     */
    public static void main(String[] args) {
        try {
            //被压缩的数据
            String data="OUO";
            //System.out.println(Arrays.toString(data.getBytes()));
            ByteArrayOutputStream bao=new ByteArrayOutputStream();
            GZIPOutputStream gzip=new GZIPOutputStream(bao);
            gzip.write(data.getBytes());
            gzip.close();

            byte[] byteArray = bao.toByteArray();
            //[31, -117, 8, 0, 0, 0, 0, 0, 0, -1, -13, 15, -11, 7, 0, -1, -26, 40, -91, 3, 0, 0, 0]
            System.out.println(Arrays.toString(byteArray));


            //解压缩
            ByteArrayOutputStream outputStream=new ByteArrayOutputStream();
            ByteArrayInputStream in =new ByteArrayInputStream(byteArray);
            GZIPInputStream ungzip=new GZIPInputStream(in);
            byte[] buffer=new byte[256];
            int n;
            while((n= ungzip.read(buffer))>=0){
                outputStream.write(buffer,0,n);
            }
            byte[] array = outputStream.toByteArray();
            //[79, 85, 79]
            System.out.println(Arrays.toString(array));
            //OUO
            System.out.println(outputStream.toString("UTF-8"));

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

    }

Python代码

import gzip

#压缩
s_in="OUO".encode('utf-8')
s_out=gzip.compress(s_in)
print([i for i in s_out])
#[31, 139, 8, 0, 130, 53, 167, 101, 2, 255, 243, 15, 245, 7, 0, 255, 230, 40, 165, 3, 0, 0, 0]
#[31,-117, 8, 0, 0, 0, 0, 0, 0, -1, -13, 15, -11, 7, 0, -1, -26, 40, -91, 3, 0, 0, 0]

# 解压缩
res=gzip.decompress(s_out)
print(res)
print(res.decode('utf-8'))

base64编码

Java代码

    /**
     * BASE64加密
     * @param args
     */
    public static void main(String[] args) {
        String name="OUO";
        //加密
        Base64.Encoder encoder=Base64.getEncoder();
        String res=encoder.encodeToString(name.getBytes());
        System.out.println(res);    //T1VP
        //解密
        Base64.Decoder decoder=Base64.getDecoder();
        byte[] origin=decoder.decode(res);
        String data=new String(origin);
        System.out.println(data);   //OUO
    }

python

import base64

name="OUO霸体护身"

res=base64.b64encode(name.encode('utf-8'))
print(res) #b'T1VP6Zy45L2T5oqk6Lqr'

data=base64.b64decode(res)
origin=data.decode('utf-8')
print(origin) #OUO霸体护身

总结:

​ 1.Java实现的算法总结+跑一下,有点印象就行

​ 2.Python还原算法(网上一定有)

	-	不要百度(大部分广告)
	-	建议谷歌(科学上网)
	-	必应搜索(还是有不少精华的)
	-	尝试在stackoverflow,github上搜资源

​ 3.推荐工具(加速逆向过程+测试)

  • 逆向某个APP: “ouo”+时间戳->MD5加密-》BASE64转码-》字符串的流程

​ 我们可以尝试使用**CyberChef**这款工具快捷测试

安卓端网络请求

想要开发得APP能连上网络

第一步:

在项目下build.gradle或者build.gradle.kts 文件中下方dependencies增加依赖

    implementation("com.squareup.okhttp3:okhttp:4.9.1")
   // implementation("com.squareup.retrofit2:retrofit:2.9.0")

第二步:创建network_security_config.xml

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

第三步:在AndroidManifest.xml新增网络权限,两条语句

1.

2.android:networkSecurityConfig=“@xml/network_security_config”

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">
    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:networkSecurityConfig="@xml/network_security_config"
        android:supportsRtl="true"
        android:theme="@style/Theme.MyApplication"
        tools:targetApi="31">
        <activity
            android:name=".index"
            android:exported="false" />
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

依赖更新步骤如下:

在 Android Studio 的菜单栏中,选择 “File” -> “Sync Project with Gradle Files” 或直接点击工具栏上的 “Sync Now” 按钮(通常呈现为一个刷新图标)。这将触发 Gradle 的同步过程,它会下载和安装新的依赖项。

表单格式

        new Thread(){
            @Override
            public void run() {
                //2.发送网络请求 OKHttp
                OkHttpClient client=new OkHttpClient.Builder().addInterceptor(interceptor).build();
                FormBody body = new FormBody.Builder().add("user", username).add("pwd", password).add("sign",sign).build();

       

                Request req = new Request.Builder().url("http://192.168.1.3:9000/auth").post(body).build();
                Call call=client.newCall(req);
                try {
                    Response response=call.execute();
                    ResponseBody responseBody = response.body();
                    String string = responseBody.string();
                    Log.e("====>",string);
                }catch (Exception e){
                    Log.e("---->","请求失败"+e.toString());
                }
            }
        }.start();

json格式

  TreeMap<String, String> map = new TreeMap<>();
        map.put("user", "测试");
        map.put("pwd", "123456");        
new Thread(){
            @Override
            public void run() {
                //2.发送网络请求 OKHttp
                OkHttpClient client=new OkHttpClient.Builder().addInterceptor(interceptor).build();
//                FormBody body = new FormBody.Builder().add("user", username).add("pwd", password).add("sign",sign).build();

                JSONObject jsonObject=new JSONObject(map);
                String jsonString=jsonObject.toString();

//                Request req = new Request.Builder().url("http://192.168.1.3:9000/auth").post(body).build();
                RequestBody form=RequestBody.create(MediaType.parse("application/json;charset=utf-8"),jsonString);
                Request req = new Request.Builder().url("http://192.168.1.3:9000/auth").post(form).build();
                Call call=client.newCall(req);
                try {
                    Response response=call.execute();
                    ResponseBody responseBody = response.body();
                    String string = responseBody.string();
                    Log.e("====>",string);
                }catch (Exception e){
                    Log.e("---->","请求失败"+e.toString());
                }
            }
        }.start();

请求拦截器

会在给后端得请求头中加入ts以及sign参数

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

            @NonNull
            @Override
            public Response intercept(@NonNull Chain chain) throws IOException {
                Request request=chain.request().newBuilder().addHeader("ts","1988121212").addHeader("sign","xxx").build();
                //请求前
                Response response = chain.proceed(request);
                //请求后
                return response;
            }
        };

retrofit2网络封装包实例

引入依赖,下方是build.gradle.kts写法

 implementation("com.squareup.retrofit2:retrofit:2.9.0")
        new Thread() {
            @Override
            public void run() {
                Retrofit retrofit=new Retrofit.Builder().baseUrl("http://192.168.1.3:9000").build();
                HttpReq httpReq = retrofit.create(HttpReq.class);
                //Form表单格式
                Call<ResponseBody> call=httpReq.postLogin("ouo","123123");


//JSON格式
//                JSONObject jsonObject=new JSONObject(map);
//                String jsonString=jsonObject.toString();
//                RequestBody form=RequestBody.create(MediaType.parse("application/json;charset=utf-8"),jsonString);
//                Call<ResponseBody> call=httpReq.postLoginJson(form);

                try {
                    ResponseBody responseBody = call.execute().body();
                    String resString = responseBody.string();
                    Log.e("Retrofit返回的结果:",resString);
                }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\":\"ffasdaada\",\"name\":\"OUO\"}";
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

保存到XML文件(存储到手机)

保存到手机上: /data/data/对应APP包名

保存

		//保存
        SharedPreferences sp=getSharedPreferences("sp_city",MODE_PRIVATE);
        SharedPreferences.Editor editor= sp.edit();
        editor.putString("token","123456789");
        editor.commit();

删除

        //删除
        SharedPreferences sp1=getSharedPreferences("sp_city",MODE_PRIVATE);
        SharedPreferences.Editor editor1=sp.edit();
        editor1.remove("token");
        editor1.commit();

读取

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

注意:

逆向场景:SharedPreferences

1.app启动时,向后端API发送请求:设备信息-》device_id

2.device_id 写入到本地的XML

3.再次发送请求时,携带device_id=?(肯定是有个地方写入的,所以就能根据这点找到加密算法)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

OUO~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值