java 字节码解析与jar破解实战
1) 关于字节码的相关介绍,尤其是字节码对照表
可以参考Oracle官网Java8的jvms(Java虚拟机规范): https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html#jvms-6.5 第6.5节详细介绍了相关操作说明;
其它版本的jvms和jls(Java语言规范)亦可以在官网查看,建议下载PDF文件以便于随时查看: https://docs.oracle.com/javase/specs/;
维基百科也列出了相关的说明: https://en.wikipedia.org/wiki/Java_bytecode_instruction_listings (需科学上网),这里列出可复制到Excel进行自定义筛选.
这是组成Java字节码的指令的列表,Java字节码是一种抽象的机器语言,最终由Java虚拟机执行。Java字节码是从Java平台上运行的语言(最著名的是Java编程语言)生成的。 | |||||
请注意,根据Java指令集,任何引用的“值”都是指32位int。 | |||||
助记符 | 操作码(十六进制) | 操作码(二进制) | 其他字节[计数]:[操作数标签] | 堆叠 [之前]→[之后] | 描述 |
aaload | 32 | 0011 0010 | arrayref,索引→值 | 将数组中的引用加载到堆栈上 | |
aastore | 53 | 0101 0011 | arrayref,索引,值→ | 将引用存储在数组中 | |
aconst_null | 1 | 0000 0001 | →空 | 将空引用推入堆栈 | |
aload | 19 | 0001 1001 | 1:索引 | →objectref | 从局部变量#index将引用加载到堆栈上 |
aload_0 | 2a | 0010 1010 | →objectref | 从局部变量0将引用加载到堆栈上 | |
aload_1 | 2b | 0010 1011 | →objectref | 从局部变量1将引用加载到堆栈上 | |
aload_2 | 2c | 0010 1100 | →objectref | 从局部变量2将引用加载到堆栈上 | |
aload_3 | 2d | 0010 1101 | →objectref | 从局部变量3将引用加载到堆栈上 | |
anewarray | bd | 1011 1101 | 2:indexbyte1,indexbyte2 | 计数→arrayref | 创建长度的参考文献的一个新的数组计数和由类参考识别出的部件类型索引(| indexbyte2 indexbyte1 << 8在常量池中) |
areturn | b0 | 1011 0000 | objectref→[空] | 从方法返回引用 | |
arraylength | be | 1011 1110 | arrayref→长度 | 获取数组的长度 | |
astore | 3a | 0011 1010 | 1:索引 | objectref→ | 将引用存储到局部变量#index中 |
astore_0 | 4b | 0100 1011 | objectref→ | 将引用存储到局部变量0 | |
astore_1 | 4c | 0100 1100 | objectref→ | 将引用存储到局部变量1中 | |
astore_2 | 4d | 0100 1101 | objectref→ | 将引用存储到局部变量2中 | |
astore_3 | 4e | 0100 1110 | objectref→ | 将引用存储到局部变量3中 | |
athrow | bf | 1011 1111 | objectref→[空],objectref | 引发错误或异常(注意已清除堆栈的其余部分,仅保留对Throwable的引用) | |
baload | 33 | 0011 0011 | arrayref,索引→值 | 从数组中加载字节或布尔值 | |
bastore | 54 | 0101 0100 | arrayref,索引,值→ | 将字节或布尔值存储到数组中 | |
bipush | 10 | 0001 0000 | 1:字节 | →值 | 将一个字节作为整数值压入堆栈 |
breakpoint | ca | 1100 1010 | 保留用于Java调试器中的断点;不应出现在任何类文件中 | ||
caload | 34 | 0011 0100 | arrayref,索引→值 | 从数组加载字符 | |
castore | 55 | 0101 0101 | arrayref,索引,值→ | 将一个字符存储到数组中 | |
checkcast | c0 | 1100 0000 | 2:indexbyte1,indexbyte2 | objectref→objectref | 检查objectref是否为某种类型,其类引用在索引的常量池中(indexbyte1 << 8 | indexbyte2) |
d2f | 90 | 1001 0000 | 值→结果 | 将double转换为float | |
d2i | 8e | 1000 1110 | 值→结果 | 将double转换为int | |
d2l | 8f | 1000 1111 | 值→结果 | 将双精度型转换为长型 | |
dadd | 63 | 0110 0011 | 值1,值2→结果 | 加两个双打 | |
daload | 31 | 0011 0001 | arrayref,索引→值 | 从数组加载双精度 | |
dastore | 52 | 0101 0010 | arrayref,索引,值→ | 将双精度数存储到数组中 | |
dcmpg | 98 | 1001 1000 | 值1,值2→结果 | 比较两个双打,NaN为1 | |
dcmpl | 97 | 1001 0111 | 值1,值2→结果 | 比较两个双打,在NaN上为-1 | |
dconst_0 | 0e | 0000 1110 | →0.0 | 将常数0.0(double)压入堆栈 | |
dconst_1 | 0f | 0000 1111 | →1.0 | 将常数1.0(double)压入堆栈 | |
ddiv | 6f | 0110 1111 | 值1,值2→结果 | 除以两双 | |
dload | 18 | 0001 1000 | 1:索引 | →值 | 加载一个双值从本地变量#INDEX |
dload_0 | 26 | 0010 0110 | →值 | 从局部变量0加载双精度 | |
dload_1 | 27 | 0010 0111 | →值 | 从局部变量1加载一个double | |
dload_2 | 28 | 0010 1000 | →值 | 从局部变量2加载双精度 | |
dload_3 | 29 | 0010 1001 | →值 | 从局部变量3加载双精度 | |
dmul | 6b | 0110 1011 | 值1,值2→结果 | 乘以两个双打 | |
dneg | 77 | 0111 0111 | 值→结果 | 否定双 | |
drem | 73 | 0111 0011 | 值1,值2→结果 | 从两个双打之间的除数得到余数 | |
dreturn | af | 1010 1111 | 值→[空] | 从方法返回双精度 | |
dstore | 39 | 0011 1001 | 1:索引 | 值→ | 将双精度值存储到局部变量#index中 |
dstore_0 | 47 | 0100 0111 | 值→ | 将double存储到局部变量0 | |
dstore_1 | 48 | 0100 1000 | 值→ | 将double存储到局部变量1 | |
dstore_2 | 49 | 0100 1001 | 值→ | 将double存储到局部变量2中 | |
dstore_3 | 4a | 0100 1010 | 值→ | 将double存储到局部变量3 | |
dsub | 67 | 0110 0111 | 值1,值2→结果 | 从另一个减去一个双 | |
dup | 59 | 0101 1001 | 值→值,值 | 将值复制到堆栈顶部 | |
dup_x1 | 5a | 0101 1010 | 值2,值1→值1,值2,值1 | 将顶部值的副本插入顶部的两个值。value1和value2的类型不能为double或long。 | |
dup_x2 | 5b | 0101 1011 | 值3,值2,值1→值1,值3,值2,值1 | 从堆栈顶部插入两个值(如果value2是double或long,它也占用value3的条目)或三个值(如果value2既不是double也不是long),则将顶部值的副本插入堆栈。 | |
dup2 | 5c | 0101 1100 | {value2,value1}→{value2,value1},{value2,value1} | 复制前两个堆栈字(如果value1不为double或long,则为两个值;如果value1为double或long,则为单个值) | |
dup2_x1 | 5d | 0101 1101 | 值3,{值2,值1}→{值2,值1},值3,{值2,值1} | 复制两个单词,然后在第三个单词下方插入(请参见上面的说明) | |
dup2_x2 | 5e | 0101 1110 | {value4,value3},{value2,value1}→{value2,value1},{value4,value3},{value2,value1} | 复制两个单词并插入第四个单词下方 | |
f2d | 8d | 1000 1101 | 值→结果 | 将浮点数转换为双精度数 | |
f2i | 8b | 1000 1011 | 值→结果 | 将float转换为int | |
f2l | 8c | 1000 1100 | 值→结果 | 将浮点数转换为长整数 | |
fadd | 62 | 0110 0010 | 值1,值2→结果 | 添加两个浮点数 | |
faload | 30 | 0011 0000 | arrayref,索引→值 | 从数组加载一个浮点数 | |
fastore | 51 | 0101 0001 | arrayref,索引,值→ | 将浮点数存储在数组中 | |
fcmpg | 96 | 1001 0110 | 值1,值2→结果 | 比较两个浮点数,在NaN上为1 | |
fcmpl | 95 | 1001 0101 | 值1,值2→结果 | 比较两个浮点数,在NaN上为-1 | |
fconst_0 | 0b | 0000 1011 | →0.0英尺 | 将0.0f压入堆栈 | |
fconst_1 | 0c | 0000 1100 | →1.0分 | 将1.0f压入堆栈 | |
fconst_2 | 0d | 0000 1101 | →2.0f | 将2.0f推入堆栈 | |
fdiv | 6e | 0110 1110 | 值1,值2→结果 | 划分两个浮点数 | |
fload | 17 | 0001 0111 | 1:索引 | →值 | 从局部变量#index加载浮点值 |
fload_0 | 22 | 0010 0010 | →值 | 从局部变量0 加载浮点值 | |
fload_1 | 23 | 0010 0011 | →值 | 从局部变量1 加载浮点值 | |
fload_2 | 24 | 0010 0100 | →值 | 从局部变量2 加载浮点值 | |
fload_3 | 25 | 0010 0101 | →值 | 从局部变量3 加载浮点值 | |
fmul | 6a | 0110 1010 | 值1,值2→结果 | 乘以两个浮点数 | |
fneg | 76 | 0111 0110 | 值→结果 | 取消浮动 | |
frem | 72 | 0111 0010 | 值1,值2→结果 | 从两个浮点数之间的除法得到余数 | |
freturn | ae | 1010 1110 | 值→[空] | 返回浮点数 | |
fstore | 38 | 0011 1000 | 1:索引 | 值→ | 将浮点值存储到局部变量#index中 |
fstore_0 | 43 | 0100 0011 | 值→ | 将浮点值存储到局部变量0 | |
fstore_1 | 44 | 0100 0100 | 值→ | 将浮点值存储到局部变量1中 | |
fstore_2 | 45 | 0100 0101 | 值→ | 将浮点值存储到局部变量2中 | |
fstore_3 | 46 | 0100 0110 | 值→ | 将浮点值存储到局部变量3中 | |
fsub | 66 | 0110 0110 | 值1,值2→结果 | 减去两个浮点数 | |
getfield | b4 | 1011 0100 | 2:indexbyte1,indexbyte2 | objectref→值 | 获取对象objectref的字段值,其中该字段由常量池索引中的字段引用标识(indexbyte1 << 8 | indexbyte2) |
getstatic | b2 | 1011 0010 | 2:indexbyte1,indexbyte2 | →值 | 获取类的静态字段值,其中该字段由常量池索引中的字段引用标识(indexbyte1 << 8 | indexbyte2) |
goto | a7 | 1010 0111 | 2:branchbyte1,branchbyte2 | [没有变化] | 前进到另一个指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
goto_w | c8 | 1100 1000 | 4:branchbyte1,branchbyte2,branchbyte3,branchbyte4 | [没有变化] | 前进到另一个指令branchoffset(signed int的自无符号字节构成| branchbyte2 << 16 | << branchbyte1 24 branchbyte3 << 8 | branchbyte4) |
i2b | 91 | 1001 0001 | 值→结果 | 将int转换为字节 | |
i2c | 92 | 1001 0010 | 值→结果 | 将int转换为字符 | |
i2d | 87 | 1000 0111 | 值→结果 | 将int转换为double | |
i2f | 86 | 1000 0110 | 值→结果 | 将int转换为float | |
i2l | 85 | 1000 0101 | 值→结果 | 将int转换为long | |
i2s | 93 | 1001 0011 | 值→结果 | 将int转换为short | |
iadd | 60 | 0110 0000 | 值1,值2→结果 | 加两个整数 | |
iaload | 2e | 0010 1110 | arrayref,索引→值 | 从数组中加载一个int | |
iand | 7e | 0111 1110 | 值1,值2→结果 | 对两个整数执行按位与运算 | |
iastore | 4f | 0100 1111 | arrayref,索引,值→ | 将一个int存储到一个数组中 | |
iconst_m1 | 2 | 0000 0010 | →-1 | 将int值-1加载到堆栈上 | |
iconst_0 | 3 | 0000 0011 | →0 | 将int值0加载到堆栈上 | |
iconst_1 | 4 | 0000 0100 | →1 | 将int值1加载到堆栈上 | |
iconst_2 | 5 | 0000 0101 | →2 | 将int值2加载到堆栈上 | |
iconst_3 | 6 | 0000 0110 | →3 | 将int值3加载到堆栈上 | |
iconst_4 | 7 | 0000 0111 | →4 | 将int值4加载到堆栈上 | |
iconst_5 | 8 | 0000 1000 | →5 | 将int值5加载到堆栈上 | |
idiv | 6c | 0110 1100 | 值1,值2→结果 | 除以两个整数 | |
if_acmpeq | a5 | 1010 0101 | 2:branchbyte1,branchbyte2 | 值1,值2→ | 如果引用是相等的,分支到在指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
if_acmpne | a6 | 1010 0110 | 2:branchbyte1,branchbyte2 | 值1,值2→ | 如果引用不相等,则转移到在指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
if_icmpeq | 9f | 1001 1111 | 2:branchbyte1,branchbyte2 | 值1,值2→ | 如果整数相等,分支到在指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
if_icmpge | a2 | 1010 0010 | 2:branchbyte1,branchbyte2 | 值1,值2→ | 如果值1大于或等于值2,则分支到在指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
if_icmpgt | a3 | 1010 0011 | 2:branchbyte1,branchbyte2 | 值1,值2→ | 如果值1大于值2,分支到指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
if_icmple | a4 | 1010 0100 | 2:branchbyte1,branchbyte2 | 值1,值2→ | 如果值1小于或等于值2,则转移到指令在branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
if_icmplt | a1 | 1010 0001 | 2:branchbyte1,branchbyte2 | 值1,值2→ | 如果值1小于值2,分支到指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
if_icmpne | a0 | 1010 0000 | 2:branchbyte1,branchbyte2 | 值1,值2→ | 如果整数不相等,则转移到在指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
ifeq | 99 | 1001 1001 | 2:branchbyte1,branchbyte2 | 值→ | 如果值是0,则转移到在指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
ifge | 9c | 1001 1100 | 2:branchbyte1,branchbyte2 | 值→ | 如果值是大于或等于0时,转移到指令在branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
ifgt | 9d | 1001 1101 | 2:branchbyte1,branchbyte2 | 值→ | 如果值大于0,则转移到在指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
ifle | 9e | 1001 1110 | 2:branchbyte1,branchbyte2 | 值→ | 如果值小于或等于0时,转移到指令在branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
iflt | 9b | 1001 1011 | 2:branchbyte1,branchbyte2 | 值→ | 如果值小于0,则转移到在指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
ifne | 9a | 1001 1010 | 2:branchbyte1,branchbyte2 | 值→ | 如果值不为0,则转移到在指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
ifnonnull | c7 | 1100 0111 | 2:branchbyte1,branchbyte2 | 值→ | 如果值不为空,则分支到在指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
ifnull | c6 | 1100 0110 | 2:branchbyte1,branchbyte2 | 值→ | 如果值是零,则分支到在指令branchoffset(符号短从无符号字节构成branchbyte1 << 8 | branchbyte2) |
iinc | 84 | 1000 0100 | 2:索引,常量 | [没有变化] | 通过带符号字节const递增局部变量#index |
iload | 15 | 0001 0101 | 1:索引 | →值 | 从局部变量#index加载int值 |
iload_0 | 1a | 0001 1010 | →值 | 从局部变量0 加载一个int值 | |
iload_1 | 1b | 0001 1011 | →值 | 从局部变量1 加载一个int值 | |
iload_2 | 1c | 0001 1100 | →值 | 从局部变量2 加载一个int值 | |
iload_3 | 1d | 0001 1101 | →值 | 从局部变量3 加载一个int值 | |
impdep1 | fe | 1111 1110 | 保留给调试器中与实现有关的操作;不应出现在任何类文件中 | ||
impdep2 | ff | 1111 1111 | 保留给调试器中与实现有关的操作;不应出现在任何类文件中 | ||
imul | 68 | 0110 1000 | 值1,值2→结果 | 两个整数相乘 | |
ineg | 74 | 0111 0100 | 值→结果 | 取反整数 | |
instanceof | c1 | 1100 0001 | 2:indexbyte1,indexbyte2 | objectref→结果 | 确定对象objectref是否为给定类型,由常量池中的类引用index(indexbyte1 << 8 | indexbyte2) 标识 |
invokedynamic | ba | 1011 1010 | 4:indexbyte1,indexbyte2、0、0 | [arg1,arg2,...]→结果 | 调用动态方法并将结果放在堆栈上(可能为空);该方法由常量池中的方法引用索引标识(indexbyte1 << 8 | indexbyte2) |
invokeinterface | b9 | 1011 1001 | 4:indexbyte1,indexbyte2,count,0 | objectref,[arg1,arg2,...]→结果 | 在对象objectref上调用接口方法,并将结果放在堆栈上(可能为空);接口方法由常量池中的方法引用索引标识(indexbyte1 << 8 | indexbyte2) |
invokespecial | b7 | 1011 0111 | 2:indexbyte1,indexbyte2 | objectref,[arg1,arg2,...]→结果 | 在对象objectref上调用实例方法并将结果放在堆栈上(可能为空);该方法由常量池中的方法引用索引标识(indexbyte1 << 8 | indexbyte2) |
invokestatic | b8 | 1011 1000 | 2:indexbyte1,indexbyte2 | [arg1,arg2,...]→结果 | 调用静态方法并将结果放在堆栈上(可能为空);该方法由常量池中的方法引用索引标识(indexbyte1 << 8 | indexbyte2) |
invokevirtual | b6 | 1011 0110 | 2:indexbyte1,indexbyte2 | objectref,[arg1,arg2,...]→结果 | 在对象objectref上调用虚拟方法并将结果放在堆栈上(可能为空);该方法由常量池中的方法引用索引标识(indexbyte1 << 8 | indexbyte2) |
ior | 80 | 1000 0000 | 值1,值2→结果 | 按位int或 | |
irem | 70 | 0111 0000 | 值1,值2→结果 | 逻辑整数余数 | |
ireturn | ac | 1010 1100 | 值→[空] | 从方法返回整数 | |
ishl | 78 | 0111 1000 | 值1,值2→结果 | int左移 | |
ishr | 7a | 0111 1010 | 值1,值2→结果 | int算术右移 | |
istore | 36 | 0011 0110 | 1:索引 | 值→ | 将int值存储到变量#index中 |
istore_0 | 3b | 0011 1011 | 值→ | 将int值存储到变量0中 | |
istore_1 | 3c | 0011 1100 | 值→ | 将int值存储到变量1中 | |
istore_2 | 3d | 0011 1101 | 值→ | 将int值存储到变量2中 | |
istore_3 | 3e | 0011 1110 | 值→ | 将int值存储到变量3中 | |
isub | 64 | 0110 0100 | 值1,值2→结果 | 整数减 | |
iushr | 7c | 0111 1100 | 值1,值2→结果 | 逻辑右移 | |
ixor | 82 | 1000 0010 | 值1,值2→结果 | 异或 | |
jsr† | a8 | 1010 1000 | 2:branchbyte1,branchbyte2 | →地址 | 跳转到子程序在branchoffset(符号短距离无符号字节构成branchbyte1 << 8 | branchbyte2),并放置在堆栈上的返回地址 |
jsr_w† | c9 | 1100 1001 | 4:branchbyte1,branchbyte2,branchbyte3,branchbyte4 | →地址 | 跳到子程序在branchoffset(signed int的自无符号字节构成| branchbyte2 << 16 | branchbyte3 << 8 | branchbyte1 << 24 branchbyte4)并放置在堆栈上的返回地址 |
l2d | 8a | 1000 1010 | 值→结果 | 将多头转换为双倍 | |
l2f | 89 | 1000 1001 | 值→结果 | 将long转换为float | |
l2i | 88 | 1000 1000 | 值→结果 | 将long转换为int | |
ladd | 61 | 0110 0001 | 值1,值2→结果 | 加两个多头 | |
laload | 2f | 0010 1111 | arrayref,索引→值 | 从数组加载长 | |
land | 7f | 0111 1111 | 值1,值2→结果 | 两个多位的按位与 | |
lastore | 50 | 0101 0000 | arrayref,索引,值→ | 将long存储到数组 | |
lcmp | 94 | 1001 0100 | 值1,值2→结果 | 如果两个long相同,则按0;如果value1大于value2,则按1;否则,则按-1。 | |
lconst_0 | 9 | 0000 1001 | →0公升 | 将0L(长类型为零的数字)压入堆栈 | |
lconst_1 | 0a | 0000 1010 | →1公升 | 将1L(长类型的数字1)推入堆栈 | |
ldc | 12 | 0001 0010 | 1:索引 | →值 | 将常量池中的常量#index(字符串,整数,浮点数,类,java.lang.invoke.MethodType,java.lang.invoke.MethodHandle或动态计算的常量)推入堆栈 |
ldc_w | 13 | 0001 0011 | 2:indexbyte1,indexbyte2 | →值 | 将常量索引从常量池(字符串,整数,浮点数,类,java.lang.invoke.MethodType,java.lang.invoke.MethodHandle或动态计算的常量)推入堆栈(宽索引构造为indexbyte1 << 8 | indexbyte2) |
ldc2_w | 14 | 0001 0100 | 2:indexbyte1,indexbyte2 | →值 | 将常量池中的常量#index(双精度,长整数或动态计算的常量)推入堆栈(宽索引构造为indexbyte1 << 8 | indexbyte2) |
ldiv | 6d | 0110 1101 | 值1,值2→结果 | 划分两个多头 | |
lload | 16 | 0001 0110 | 1:索引 | →值 | 从局部变量#index加载一个长值 |
lload_0 | 1e | 0001 1110 | →值 | 从局部变量加载一个长值0 | |
lload_1 | 1f | 0001 1111 | →值 | 从局部变量1加载一个长值 | |
lload_2 | 20 | 0010 0000 | →值 | 从局部变量2加载一个长值 | |
lload_3 | 21 | 0010 0001 | →值 | 从局部变量3加载长值 | |
lmul | 69 | 0110 1001 | 值1,值2→结果 | 乘以两个多头 | |
lneg | 75 | 0111 0101 | 值→结果 | 否定很长时间 | |
lookupswitch | ab | 1010 1011 | 8 +:<0–3个字节填充>,defaultbyte1,defaultbyte2,defaultbyte3,defaultbyte4,npairs1,npairs2,npairs3,npairs4,匹配偏移对... | 键→ | 使用键从表中查找目标地址,并从该地址处的指令继续执行 |
lor | 81 | 1000 0001 | 值1,值2→结果 | 两个多位的按位或 | |
lrem | 71 | 0111 0001 | 值1,值2→结果 | 两个多头除法的余数 | |
lreturn | ad | 1010 1101 | 值→[空] | 返回一个长值 | |
lshl | 79 | 0111 1001 | 值1,值2→结果 | 将long值1左移int value2位置 | |
lshr | 7b | 0111 1011 | 值1,值2→结果 | 将long值1的按位右移int value2的位置 | |
lstore | 37 | 0011 0111 | 1:索引 | 值→ | 将长值存储在局部变量#index中 |
lstore_0 | 3f | 0011 1111 | 值→ | 将长值存储在局部变量中0 | |
lstore_1 | 40 | 0100 0000 | 值→ | 将长值存储在局部变量1中 | |
lstore_2 | 41 | 0100 0001 | 值→ | 将长值存储在局部变量2中 | |
lstore_3 | 42 | 0100 0010 | 值→ | 将长值存储在局部变量3中 | |
lsub | 65 | 0110 0101 | 值1,值2→结果 | 减去两个多头 | |
lushr | 7d | 0111 1101 | 值1,值2→结果 | 长整数1的按位右移整数值2的位置,无符号 | |
lxor | 83 | 1000 0011 | 值1,值2→结果 | 两个long的按位XOR | |
monitorenter | c2 | 1100 0010 | objectref→ | 输入对象的监视程序(“获取锁定” – synced()部分的开始) | |
monitorexit | c3 | 1100 0011 | objectref→ | 退出对象的监视程序(“释放锁” – synced()节的结尾) | |
multianewarray | c5 | 1100 0101 | 3:indexbyte1,indexbyte2,尺寸 | count1,[count2,...]→arrayref | 在常量池索引(indexbyte1 << 8 | indexbyte2)中创建一个新的维数组,该数组的维的类型由类引用标识;每个维度的尺寸由count1,[ count2等] 标识 |
new | bb | 1011 1011 | 2:indexbyte1,indexbyte2 | →objectref | 在常量池索引中创建由类引用标识的类型的新对象(indexbyte1 << 8 | indexbyte2) |
newarray | bc | 1011 1100 | 1:类型 | 计数→arrayref | 用atype标识的原始类型的count元素创建新数组 |
nop | 0 | 0000 0000 | [没有变化] | 不执行任何操作 | |
pop | 57 | 0101 0111 | 值→ | 丢弃栈顶值 | |
pop2 | 58 | 0101 1000 | {value2,value1}→ | 丢弃堆栈中的前两个值(如果是双精度或长整型,则丢弃一个值) | |
putfield | b5 | 1011 0101 | 2:indexbyte1,indexbyte2 | objectref,值→ | 将字段设置为对象objectref中的value,其中该字段由常量池中的字段引用索引标识(indexbyte1 << 8 | indexbyte2) |
putstatic | b3 | 1011 0011 | 2:indexbyte1,indexbyte2 | 值→ | 将静态字段设置为类中的值,其中该字段由常量池中的字段引用索引标识(indexbyte1 << 8 | indexbyte2) |
ret† | a9 | 1010 1001 | 1:索引 | [没有变化] | 从局部变量#index的地址继续执行(与jsr不对称是有意的) |
return | b1 | 1011 0001 | →[空] | 从方法返回void | |
saload | 35 | 0011 0101 | arrayref,索引→值 | 从数组中加载短路 | |
sastore | 56 | 0101 0110 | arrayref,索引,值→ | 短数组存储 | |
sipush | 11 | 0001 0001 | 2:字节1,字节2 | →值 | 将short作为整数值压入堆栈 |
swap | 5f | 0101 1111 | 值2,值1→值1,值2 | 交换堆栈上的两个高位字(请注意,value1和value2不能为double或long) | |
tableswitch | aa | 1010 1010 | 16 +:[0–3个字节填充],defaultbyte1,defaultbyte2,defaultbyte3,defaultbyte4,lowbyte1,lowbyte2,lowbyte3,lowbyte4,highbyte1,highbyte2,highbyte3,highbyte4,跳转偏移量... | 索引→ | 从偏移量索引表中的地址继续执行 |
wide | c4 | 1100 0100 | 3/5:操作码,索引字节1,索引字节2 | [与相应的说明相同] | 执行opcode,其中opcode是iload,fload,aload,lload,dload,istore,fstore,astore,lstore,dstore或ret,但假定索引为16位;或执行iinc,其中索引为16位,而要递增的常数为带符号的16位short |
或 | |||||
iinc,索引字节1,索引字节2,计数字节1,计数字节2 | |||||
(no name) | cb-fd | 这些值当前未分配给操作码,并保留以供将来使用 |
2) 常用的字节码工具
常用工具有:
jd-gui,查看用比较好用,轻量级. 地址: http://java-decompiler.github.io/;
java bytecode editor,轻量级,不是很方面,但可实现字节码手动编辑,地址: http://set.ee/jbe/;
JByteMod,重量级,支持多种反编译工具,亦可实现单方法查看,地址: https://github.com/GraxCode/JByteMod-Beta;
需要注意:JByteMod 为 idea 项目,需要引入4个jar包;然后打包后生成的JByteMod-1.8.0.jar;
mvn install:install-file -Dfile=存放路径\JByteMod-Beta-master\lib\attach-1.7.jar -DgroupId=com.sun -DartifactId=attach -Dversion=1.7 -Dpackaging=jar
mvn install:install-file -Dfile=存放路径\JByteMod-Beta-master\lib\cfr_0_139.jar -DgroupId=org.benf -DartifactId=reader -Dversion=1.3.9 -Dpackaging=jar
mvn install:install-file -Dfile=存放路径\JByteMod-Beta-master\lib\procyon-0.5.33.jar -DgroupId=org.bitbucket.mstrobel -DartifactId=procyon -Dversion=0.5.33 -Dpackaging=jar
mvn install:install-file -Dfile=存放路径\JByteMod-Beta-master\lib\weblaf-complete-1.29-fixed.jar -DgroupId=com.weblaf -DartifactId=weblaf -Dversion=1.2.9 -Dpackaging=jar
3) jar包破解实战
原理: 某些商业用jar包,有license验证,那么找到验证方法,跳过判断,即该验证方法直接返回true.
工具: IDEA,JByteMod-XX.jar
以某商业Jar包为例; 找到License中的a方法为签名认证方法.
使用字节码编辑工具进行该方法编辑,编辑后结果为:
该方法对应的字节码为:
可简单记为:
iconst_1
ireturn
然后通过Ctrl+S另存为新的jar包,替换Maven仓库中的jar包即可实现破解.
写在后面的话: javaassist\asm\cglib\lombok等字节码工具包均是动态地修改了字节码,实现了动态代理.因此也可利用上述工具进行字节码文件修改达到破解目标.
最后,本博客提供破解方法仅供学习用,不得将破解软件用于商业目的.
希望本篇博客对各位看官有帮助.