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=?(肯定是有个地方写入的,所以就能根据这点找到加密算法)