目录
一、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代码基础知识扫盲了解
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
关键字,看这里介绍,使类不能被继承,是方法不能被子类重写
2、导包依赖
-
导包依赖管理
:下载.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) 是在一个类里面,方法名字相同,而参数不同
,返回类型可以相同也可以不同
三、apk_java层逆向小案例
-
选择的是
抓包工具fiddler
,安卓手机
或者可以试试模拟器,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启动,代码如下