一、vscode安装java插件二、java代码基础知识扫盲了解
1、类class2、导包依赖3、定义变量4、方法函数 三、apk_java层逆向小案例四、java与frida的hook文章推荐
一、vscode安装java插件
jdk(Java Develpment Kit java 开发工具)是我们用来写代码的,jdk中包含jre,
jre(Java Runtime Environment java运行时环境)是用来使用代码的,jre包含jvm,
jvm(java Virtual Machine java 虚拟机)是用来运行代码的,jdk是给开发人员使用的, jre和jvm是给普通用户使用
JVM+Lib=JRE,总体来说就是,我们利用JDK(调用JAVA API)开发了属于我们自己的JAVA程序后,通过JDK中的编译程序(javac)将我们的.java文件编译成JAVA字节码(.class文件),在JRE上运行这些JAVA字节码(.class文件),JVM解析这些字节码,映射到CPU指令集或OS的系统调用。通俗的讲运行java代码实际上走了如图中的两步命令:javac编译,java运行;而python运行文件只需要python test.py即可
java的JDK环境准备:参考jdk1.8_java环境安装(目录3) 的介绍
下载vscode ,然后直接安装,中间安装路径可以自定义,以及创建桌面快捷方式,其它的不用改直接next
搜索Extension Pack for Java,然后下载安装
vscode测试java运行:ctrl+shift+p然后搜索Java: create Project,选择No build tools,然后选择一个文件夹,输入项目名,然后会自动创建好java工程目录,在src下直接runApp.java输出成功,配置完毕
注意CodeLens即代码中出现的灰色的Run|Debug按钮,如果你的文件没有的话,可能是网络不好的原因,导致没有加载出来
Java语言,源码文件是.java,而编译后的.class文件才是真正可以被JVM执行的字节码,所以我们的步骤都是先写.java文件,再编译成.class文件运行
新建一个文件MD5Util.java测试MD5加密,有两种方式运行java文件,这里推荐代码中的Run按钮即可输出md5值,bin目录下会有.class文件;第二种方式是控制台输入javac -encoding UTF-8 MD5Util.java对文件进行编译在src目录下生成.class文件,然后输入java MD5Util即可输出md5值
import java.security.MessageDigest;
import java.math.BigInteger;
public class MD5Util {
public static String md5(String str) {
byte[] digest = null;
try {
MessageDigest md = MessageDigest.getInstance("MD5");
digest = md.digest(str.getBytes("utf-8"));
String md5Str = new BigInteger(1, digest).toString(16); //16是表示转换为16进制数
return md5Str;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static void main(String[] args){
System.out.println(md5("1234"));
}
}
二、java代码基础知识扫盲了解
推荐java视频推荐文档教程 1、类class
一个文件里面可以有多个类,但是只能有一个公有的类(即public修饰的),公有的类名和文件名必须一致,一个类里面可以有很多方法,如图HelloWord是HelloWord.java文件下唯一的用public修饰的公有类
一个文件里有多少个类,通过javac -encoding UTF-8 HelloWorld.java编译后就会生成相应个数的.class文件,而要运行单个类的话,就通过java class文件名来运行即可
每个单独的类要执行的入口,都是先寻找主函数main,所以主函数的写法是固定的,{}代表代码的开始和结束,语句结束都要加上分号; ,void代表返回类型为空
public class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello World"); // 输出 Hello World
}
}
通过static关键字修饰的,叫静态属性,静态方法,静态属性调用方式是类名.属性名,静态方法的调用方式类名.方法名();
未通过static关键字修饰的统一称之为实例(对象)属性或方法,创建一个实例对象:XXclass obj = new XXclass(),实例属性,实例属性的调用方式,obj.属性名,实例调用方法obj.方法名();
类属性是唯一共有的,任何一个与之相连的都会随它一起改变,和列表引用的概念很相似;类直接调用的方式仅仅限于静态的属性和方法,每个实例对象的属性方法是独立的互不影响;这里建议直接看视频理解
构造器:构造器又称构造方法构造函数,构造器名和类名一致,无返回值;当对象创建时会自动调用构造器,进一步讲解看
类的继承,看这里介绍,extends关键字是单一继承类,implements关键字是多继承的特性
对父类的访问,看这里介绍,this关键字指向自己的引用,super关键字引用父类的
final关键字,看这里介绍,使类不能被继承,是方法不能被子类重写
导包依赖管理:下载.jar包,比如搜索Commons Codec包然后下载下来
将下载下来的.jar包放在lib目录下,之后通过import的方式即可导入,如下图片代码是一个java爬虫案例并导入了相关的包
所谓的.jar包里面都是众多的.class文件打包在一起的;而settings如下的配置,表示我们要import某个包时,它会自动去lib目录下寻找相关包依赖
一个文件DigestTest的类调用另一个文件DigestUtils的类方法
import java.security.MessageDigest;
public class DigestUtils {
public static String md5(String txt) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(txt.getBytes("GBK"));
StringBuffer buf = new StringBuffer();
for (byte b : md.digest()) {
buf.append(String.format("%02x", b & 0xff));
}
return buf.toString();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// public static void main(String[] args){
// System.out.println(md5("1234"));
// }
}
import java.io.*;
public class DigestTest{
public static void main(String[] args){
DigestUtils dig = new DigestUtils(); // 先实列化
System.out.println(dig.md5("1234"));
}
}
3、定义变量
变量的格式是:数据类型 变量名 = 初始化值,且必须初始化给个值,即三要素:类型、名称、值基本数据类型:8种,byte、short、int、long、float、double、boolean、char,其中char数据类型的字符是单引号,String类定义的是双引号
// 四种整数类型 byte a = 127; // 1字节8bit位,最大值2^7-1,最小值-2^7 short b = 32767; // 2字节16位,最大值2^15-1 int c = 2147483647; // 4字节32位 long d = 9223372036854775807L; // 8字节64位 // 两种浮点型 float e = 234.5f; // 4字节32位 double f = 12.9867; // 8字节64位 // 布尔型 boolean g = true; // 字符型 char h = '中'; // 2字节16位,类型是一个单一的16位Unicode字符,字符必须用单引号,且是单个字符,双引号代表字符串 char i = 97; // char类型输出97对应的字符其它类型:String类、StringBuilder、数组等
// String类
String j = "abcd";
// StringBuilder类
StringBuilder k = new StringBuilder(10);
k.append("shiyiyi"); // 字符串追加到此字符序列
// 数组动态初始化
int size = 10; // 数组大小
double[] myList1 = new double[size];
myList1[0] = 5.6;
myList1[1] = 4.5;
// 数组静态初始化
int[] myList2 = new int[]{3, 1, 2, 6, 4, 2};
int[] myList3 = {3, 1, 2, 6, 4, 2};
double[] myList4 = {1.9, 2.9, 3.4, 3.5};
String[] myList5 = {"23132", "fdaf"};
4、方法函数
参考教程1,参考教程2,不管是调用方法,还是定义方法,与python不同的是,java必须明确指定类型,传参类型,返参类型
方法重载:参考案例,重载(overloading) 是在一个类里面,方法名字相同,而参数不同,返回类型可以相同也可以不同
选择的是抓包工具fiddler,root后的安卓7以下的手机 或者可以试试模拟器,jadx反编译工具, 一些软件安装配置看这篇文章
如图,案例:com.mylrc.mymusic,通过抓包发现sign是加密的,而这个sign类似md5
打开jadx反编译apk后,直接通过搜索定位sign加密代码处
通过如下的java代码行分析,sign = md5(搜索词+时间+wyy_search)
带着上面的猜想去测试,猜测正确
测试请求,成功返回
上面是看java代码直接就猜出sign的生成逻辑,下面我再通过hook的方法去验证以下明文和结果:
第一步:已经root的手机与电脑通过usb连接,并开启frida服务,只有开启了frida服务才能进行frida hook
adb shell su cd /data/local/tmp ./frida-server-15.1.14-android-arm64
第二步,分析代码编写frida-hook脚本,如图我们知道sign值是MD5加密得到的,所以我们要hook的类名就是com.mylrc.mymusic.tool.Utils,要hook的方法就是MD5,传入参数一个,返回参数一个
第三步:编写脚本如下,输出明文,以及返回结果
function hook_java(){
// 目的:hook 类名:com.mylrc.mymusic.tool.Utils 下的 MD5 方法,查看输入明文组成结构
Java.perform(function(){
var utils = Java.use("com.mylrc.mymusic.tool.Utils");
console.log(utils)
utils.MD5.implementation = function(key){
console.log("Utils.MD5_key", key);
var result = this.MD5(key);
console.log("Utils.MD5_result", result);
return result
}
})
}
setImmediate(hook_java)
第四步:查看app的包名com.mylrc.mymusic,另开一个cmd窗口输入frida -U -l music_match.js --no-pause -f com.mylrc.mymusic,此时敲个回车,然后在手机端app上搜索,如图打印出hook的内容,明文周杰伦1646545254800wyy_search,确实和我们前面看java代码的明文组成是一样的,hook成功,第一个案例结束
以上整个步骤用到的cmd命令如下
# 一个cmd窗口先开frida服务 adb shell su cd /data/local/tmp ./frida-server-15.1.14-android-arm64 # 另开一个cmd窗口注入hook的js frida -U -l music_match_hook.js --no-pause -f com.mylrc.mymusic
总结:frida hook 函数方法,确定类名,确定方法名,然后传参个数,然后按如下格式填写即可hook;setImmediate用于执行给定的function,当从控制台进行hook时会调用相关的function
function hook_java(){
// 目的:hook 类名:xxxxxxxx 下的 kkkk 方法
Java.perform(function(){
var utils = Java.use("待填写的类名");
utils.待填写的方法名.implementation = function(key){
待编写的逻辑
}
})
}
setImmediate(hook_java)
前面步骤的第三步和第四步也可以用python启动,代码如下
frida hook javafrida hook soJava视频讲解Java文档讲解



