前言 在逆向的时候遇到过反编译 py 字节码,之前也就没咋在意,啥不会查就完事儿了,好家伙,省赛让我给遇到了,直接嘤嘤嘤,但还好解出来了; 今天趁这个机会,系统的学习一下,以防下次阴沟里翻船,本博文的 Python 版本是3.8.5,版本不同形成的字节码会略有不同,但是大同小异; 【记】2021年第四届浙江省大学生网络安全技能挑战赛: CSDN 个人博客 什么是 py 字节码? Python 代码先被编译为字节码后,再由 Python 虚拟机来执行字节码,Python 的字节码是一种类似汇编指令的中间语言,一个 Python 语句会对应若干字节码指令,虚拟机一条一条执行字节码指令 Python 的 dis 模块支持对 Python 代码进行反汇编, 生成字节码指令。 121, 116, 33, 32, 100, 62] def test(): for st in str: print(chr(st^16),end='') test() 字节码
前言 emmm,关于字节码混淆,最早碰到还是在校赛的时候,当时一脸懵逼,什么情况,怎么 uncompyle6 不能反编译 pyc 了,不过之后也就不了了之了,今天特地写此博文纪念 DASCTF Oct 版本的字节码解释器; 注意这里是小端序(内存存储),高位在后面,所以是 0xF303; Magic Number 之后的四个字节为时间戳,这里是 0x5EC652B0,之后就是 Python 代码对象 往后四个字节是局部变量的个数 co_nlocals; 往后四个字节是栈空间大小 co_stacksize; 往后四个字节是 co_flags; 之后就是 co_code 了,也就是编译好的字节码的部分 ,也就是后方 163 个字节的长度都是代码对象; 此 co_code 对象的字节码内容结束后,接着是 co_consts 内容,也就是用到的常量的内容: 最开始是 TYPE_TUPLE,表示这是个元组类型 _6Y_1ayer_by_layer_by_layer} 后记 参考: Python 字节码与字节码混淆 Python代码保护 | pyc 混淆从入门到工具实现
我们都知道Java字节码是JVM所使用的指令集。java字节码可以分为如下几类: 操作数栈 Java 方法的栈桢分为操作数栈和局部变量区。 如下代码生所的字节码: ? dup pop 指令常用于舍弃调用指令的返回结果。 Idc加载常量池中的例子 将局部变量区的值加载到操作数栈的指令 Java 虚拟机将局部变量区当成一个数组,依次存放 this 指针(仅非静态方法),所传入的参数,以及字节码中的局部变量。 局部变量表的字节码 iinc M, N指令,(其中 M为正整数,N为整数),用于操作局部变量区。 字节码如下图所示: ? iinc例子 Java 相关指令 new: 后跟目标类,生成该类的未初始化的对象。
在我们工作、学习、以及研究 JVM 过程当中,不可避免的要查看 Java 字节码,通过查看字节码可以了解一个类的编译结果,也能通过编译器层面来分析一个类的性能。 字节码文件是不能直接打开的,下面栈长教大家几种简单的方法如何查看 Java 字节码。 2、Intellij IDEA 中查看字节码 Intellij IDEA 直接集成了一个工具菜单,可以直接查看字节码,打开 ByteCode 插件窗口方法如下: View -> Show bytecode 如图所示,可以看到 String 类的的字节码。 3、Eclipse 中查看字节码 在 Eclipse 中查看字节码稍显麻烦,需要安装插件,并打开插件窗口,安装和使用方式如下: Name: bytecode Location: http://andrei.gmxhome.de
字节码解析 结构 本位将详细介绍字节码的2进制结构和JVM解析2进制流的规范。 字节码采用UTF-8缩略编码的方式编码——'\u0001'到'\u007f'的字符串(相当于ASCII 0~127)只用一个字节表示,而'\u0800'到'\uffff'的编码使用3个字节表示。 (#2,10,this) 10 ~3c 0xa7 goto ~3d~3e 0x0016 32行 goto指令的参数 ~3f~48 对应catch括号之间的源码: 行号 偏移位 字节码 指令 说明 能够理解字节码和JVM的识别过程还可以帮助我们更深层次优化代码。无论Java代码写得再漂亮也要转换成字节码去运行。从字节码层面去看运行的方式,要比从Java源码层面更为透彻。 理解字节码还有一个好处,更容易理解多线程的3个主要特性:原子性、可见性和有序性。比如new Object() 从字节码层面一看就知道不具备原子性,指令重排的问题在字节码层面也是一目了然。
ASM是一个java字节码操纵框架,它能被用来动态生成类或者增强既有类的功能。ASM 可以直接产生二进制 class 文件,也可以在类被加载入 Java 虚拟机之前动态改变类行为。 Java class 被存储在严格格式定义的 .class文件里,这些类文件拥有足够的元数据来解析类中的所有元素:类名称、方法、属性以及 Java 字节码(指令)。 二、如何使用ASM ASM框架中的核心类有以下几个: ① ClassReader:该类用来解析编译过的class字节码文件。 ② ClassWriter:该类用来重新构建编译后的类,比如说修改类名、属性以及方法,甚至可以生成新的类的字节码文件。
什么是字节码 字节码(Byte-code)是一种包含执行程序,由一序列 op 代码/数据对组成的二进制文件,是一种中间码。字节是电脑里的数据量单位。 字节码文件展示 ? image.png 首先了解一张图 java字节码总览图 ? image.png 魔数 四个字节 首先讲一下魔数占四个字节对应 上图demo.class 0Xcafe babe表示java的class文件。好奇的你发现 cafe babe 约等于下张图 ? image.png 版本号 4个字节 对应demo 0X0000 0033 其中前面的0000是次版本号,后面的0033是主版本号。 Access_Flag.png 后续可以参考java程序如何按照字节码翻译
(但在Python 3上却难觅其踪 -- 原因是它们不再与.py文件出现在同一个目录中,而是放在一个名为__pycache__的子目录中了)。或许你也已听说过这是一种程序加速机制。 访问和理解Python字节码 如果你也想玩玩这个,Python标准库中的dis模块就非常有用了; dis模块为Python字节码提供了一个“反汇编程序”,从而可以轻松获取人为可读的版本并查找各种字节码指令 “原始”字节码 - 不具有可读性字节码 - 可以通过代码对象的co_code的属性来访问。如果您想尝试手动反汇编函数,则可以使用列表dis.opname从十进制字节值中查找相应字节码指令的名称。 对于Python, 理解字节码会有类似的效果 - 如果您可以预想到Python源代码会被转换成怎样的字节码,你就可以更好地决定如何编写和优化它。 字节码解释器的实现位于文件Python/ceval.c中。这里是Python 3.6.4版本的文件 ; 字节码指令是由第1266行的switch语句开始处理。
字节码是一种中间代码,它与特定平台无关。并且每个支持 Java 的平台都需要有相应的 JVM,负责解释和执行字节码。 字节码增强 字节码增强技术就是一类对现有字节码进行修改或者动态生成全新字节码文件的技术。 核心类: ClassReader:读取字节码并将其转换为内部数据结构。 ClassWriter:将内部数据结构转换回字节码,允许对字节码进行修改。 ClassVisitor:字节码访问者接口,通过它可以在访问字节码的过程中进行操作。 classNode = new ClassNode(Opcodes.ASM9); classReader.accept(classNode, ClassReader.SKIP_DEBUG); // 3、
在上一篇我们介绍了类加载器的相关功能,在这一篇中我们在分享一下虚拟机中的另一个非常重要的功能字节码执行引擎。我们知道Java虚拟机的主要任务就是加载class文件并执行其中的字节码。 加载class的功能是由类加载器实现的,那么执行其中字节码的功能就是由字节码执行引擎执行的。下图为虚拟机的基本结构图。 ? 虚拟机执行字节码时,会监视程序中使用最频繁的代码,将其编译成本地机器代码,而其它的字节码,继续保留为字节码。 执行引擎是由硬件芯片构成,它用本地代码执行字节码。 上面所说的都是不同执行引擎的的执行特性,但无论是哪一种执行引擎其中最基本的功能都是执行字节码。 我们知道class文件的常量池中存有大量的符号引用,字节码中的方法调用就以常量池中指向方法的符号引用作为参数。
Java字节码对于虚拟机,就好像汇编语言对于计算机,属于基本执行指令。每一个Java字节码指令是一个byte数字,并且有一个对应的助记符。 其中有这么几行字节码指令 0: ldc #2 //常量池中#2为500000,将500000压入栈 2: istore_1 3: sipush void print(char[] cs,short[] s) { System.out.println(s[0]); System.out.println(cs[0]); } 编译后的字节码为 来看一下如下代码 public void print(byte i) { int k = i; long l = i; } 编译后的字节码 public void print(byte) 对象/数组操作指令 Java是面向对象的程序设计语言,虚拟机平台从字节码层面就对面向对象做了深层次的支持。
jvm运行的指令就是.class字节码。所以通过学习字节码的规范和规则能提升我们对代码执行过程的理解。 这里恶补一下字节码相关的基础知识。 我们打开.class文件,发现字节码都是十六进制的编码。 字节码的组成有以下部分,正入上图所示,十六进制的字节码中就包含这些部分 。如下图所示。 2.版本号:因为java有不同的版本,所以在java字节码中也有标记。0000 0034两个字节就表示java1.8版本。 在字节码中的十六进制关系如下图所示。 (5) 当前类名 访问标志后的两个字节,描述的是当前类的全限定名。这两个字节保存的值为常量池中的索引值,根据索引值就能在常量池中找到这个类的全限定名。 ,其字节码中方法的执行和常量池和代码区有很大的联系。
本文介绍一种NodeJS源代码保护方式:通过把nodejs代码转化为字节码,用node启动字节码文件的方式,保护nodejs源代码不泄漏。 为了防止源码泄漏带来的一系列令人不安的后果,这里介绍一种专门针对于nodejs源码的保护技术:将nodejs代码转化为字节码文件。 通过v8虚拟机,可以将js代码编译为字节码。而v8虚拟机是能够识别和直接运行该字节码的。 因此,以下执行逻辑成为可能: 1、js代码 -> js字节码 2、js字节码 -> nodejs ->运行 实现代码 (例程) 生成字节码文件的部分: var v8 = require('v8 ,读取、运行字节码。
从局部变量1中装载float类型值 fload_2 从局部变量2中装载float类型值 fload_3 从局部变量3中装载float类型值 dload_0 从局部变量2中装载引用类型值 aload_3 从局部变量3中装载引用类型值 iaload 从数组中装载int类型值 laload 类型值存入局部变量2 lstore_3 将long类型值存入局部变量3 fstore_0 将float类型值存入局部变量0 fstore_1 将float 类型值存入局部变量1 fstore_2 将float类型值存入局部变量2 fstore_3 将float类型值存入局部变量3 dstore_0 将double 类型值存入数组中 castore 将char类型值存入数组中 sastore 将short类型值存入数组中 wide指令 wide 使用附加字节扩展局部变量索引
AOP的实现一般使用了动态代理和字节码修改,本文介绍使用javassist实现类的创建和修改 添加依赖 <dependency> <groupId>org.javassist</groupId > <artifactId>javassist</artifactId> <version>3.28.0-GA</version> </dependency> 使用字节码创建一个类 初始化 Method printName = person.getClass().getMethod("printName"); printName.invoke(person); 输出 miao~ mark 使用字节码修改类 student = new Student(); student.greeting(); 输出 before greeting~ hello student after greeting~ 常见问题 字节码修改类没有生效 需要调用修改的类CtClass.toClass,把修改的信息写入到类字节码 attempted duplicate class definition for name: "XXX" 因为对应的类信息已经加载
方案4:字节码插桩 字节码函数插桩目前有以下两种框架 ASM 思路:应用程序打包成APK之前会先编译成.class文件,然后打包成dex,最后组成apk。 ASM框架进行字节码函数插桩 ============== 经过上述方案的对比,最终采用ASM进行字节码插桩。主要是对代码的侵入低,可定制化配置(过滤采集页面,过滤时长,配置页面映射等)。 * 使用android提供的Transform API获取project的文件 * 检测到文件后缀为class的时候进行文件修改 \* ASM框架相应API进行字节码读取和分析和插入 \* 后续如果缺少相应的控件,那么可以根据相应的控件进行添加对应的字节码描述即可: 例如在APP中的底部控件为Google的design控件,添加: SDK\_API\_CLASS = "com/cage/ 相关视频推荐: 【Android组件化设计】字节码插桩优化框架初始化速度 本文转自 https://juejin.cn/post/6844904194445426702,如有侵权,请联系删除。
最常用的字节码处理框架有 AspectJ、ASM 等等,它们的相同之处在于输入输出都是 Class 文件。 并且,它们都是 在 Java 文件编译成 .class 文件之后,生成 Dalvik 字节码之前执行。 3.性能较低 AspectJ 在实现时会包装自己一些特定的类,它并不会直接把 Trace 函数直接插入到代码中,导致生成的字节码比较大,影响性能,如果想对 App 中所有的函数都进行插桩,性能影响肯定会比较大 2)、监控 OKHttp3 的每一次网络请求。 3)、监控 HttpConnection 的每一次网络请求。 TAG, "okhttp chain.proceed 耗时:" + mOkHttpData.costTime); } // 4、记录当前请求返回的响应码和响应数据大小
动态生成字节码 我们知道,我们编写的 Java 代码都是要被编译成字节码后才能放到 JVM 里执行的,而字节码一旦被加载到虚拟机中,就可以被解释执行。 虽然我对 JVM 的字节码语法不熟,但有大神开发了可以在 IDEA 里查看字节码的插件:ASM Bytecode Outline ,在要查看的类文件里右键选择 Show bytecode Outline 即可以右侧的工具栏查看我们要生成的字节码。 Instrument ---- 介绍 字节码是修改完了,可是 JVM 在执行时会使用自己的类加载器加载字节码文件,加载后并不会理会我们做出的修改,要想实现对现有类的修改,我们还需要搭配 Java 的另一个库 ; } } } 执行字节码修改和转换的类。
在平时的demo中,依靠字节码顺序,解析程序执行流程,真正的执行顺序是字节码的执行顺序,单线程下字节码顺序是与程序书写顺序一致的,多线程环境下,共享变量的赋值读取顺序却不可掌握时机。 3:SurvivorTo和SurvivorFrom互换 互换之后SurvivorTo成为下一次GC的From区,当对象年龄达到15,最终如果存活,存入老年代。 ,元空间使用本地内存 垃圾收集器: 并行 串行 并发标记 G1 ZGC 1.串行垃圾回收器(Serial)单线程环境设计只用一个线程回收 2.并行垃圾回收器(Parellel)多个收集线程并行工作 3. G1不产生内存碎片 可精准控制停顿 字节码指令解析 以Price问题为例 package com.kk; import org.junit.Assert; import org.junit.Before System.out.println( "Set up for "+ list.get(1) ); for (int i = 0; i < 3;
这部分个人觉得主要是属于设计机构拓展的内容,大家可以一起来学习一下 Java 字节码的设计结构以及感受一下设计者的设计。 class 类文件结构 Java 提供 javap 命令可以分析字节码文件,我们可以使用 javap -verbose 命令分析一个字节码文件时, 将会分析该字节码文件的魔数、版本号、常量池、类信息、类的构造方法 魔数和 Class 文件版本 魔数:所有的.class 字节码文件的前4个字节都是魔数,魔数为固定值: 0xCAFEBABE 版本信息,魔数之后的4个字节是版本信息,前两个字节表示 minor version code_length 表示该方法所包含的字节码的字节数以及具体的指令码。 具体字节码即是该方法被调用时,虚拟机所执行的字节码。 exception_table, 这里存放的是处理异常信息。 对于Java每一个实例方法(非静态方法), 其在编译后生成的字节码中比实际方法多一个参数, 它位于方法的第一个参数位置.