当前位置:网站首页>ASM piling: after learning ASM tree API, you don't have to be afraid of hook anymore
ASM piling: after learning ASM tree API, you don't have to be afraid of hook anymore
2022-07-29 06:01:00 【Code and thinking】
background
about ASM Insert piles , Maybe many people are familiar , But most of them may stay core api On , For some plug-in libraries on the market now , In fact, many use tree api I wrote , because tree api The simplicity and clarity of , It has increasingly become the choice of many open source libraries .(ASM There are two sets api type , Namely core and tree)

ASM Introduce
ASM In fact, it is a tool that can compile bytecode , For example, our daily development will introduce many class libraries, right , Or our project is too big , When you want to modify a point , Unified modification is easy to make mistakes ( For example, privacy compliance issues ), At this time, if there is a tool for the generated class If the document is edited , It is very convenient for us to carry out the follow-up work .
This chapter mainly introduces tree api, What follows ASM All refer to tree api The operation of !
class file
What we often say class file , In fact, from the perspective of binary , It is simply divided into the following parts :
You can see , One class The file is actually composed of several parts in the above figure , and ASM, It is to further Abstract these structures , about class file , In fact, it is abstracted into asm Medium class node class

For one class The file is , Uniqueness can be identified through the following , Namely :version( edition ),access( Scope , such as private Equal modifier ),name( name ),signature( Generic signature ),superName( Parent class ),interfaces( Implemented interface ),fields( Current properties ),methoss( The current approach ). So if you want to modify one class, We modify the corresponding classNode that will do
fields
attribute , It is also a very important part of classes , In bytecode , That's how it's defined

For an attribute ,ASM Abstract it as FieldNode

For an attribute field Come on , Uniqueness can be identified through the following :access( Scope , Follow class Same structure , such as private modification ),name( The attribute name ),desc( Signature ),signature( Generic signature ),value( Current corresponding value )
methods
Compared to attributes , Our method structure is more complex 
Compared with the single attribute , A method may consist of multiple instructions , Successful execution of a method , It also involves the cooperation between the local variable table and the operand stack .ASM Abstract the method into such a definition Method = Method head + Method body
Method head : That is to identify the basic attributes of a method , Include :access( Scope ),name( Method name ),desc( Method signature ),signature( Generic signature ),exceptions( Method can throw exceptions )

Method body : Compared with method header , The concept of method body is actually relatively simple , In fact, the method body is the set of instructions of the method , It mainly includes instrutions( Method instruction set ),tryCatchBlocks( Abnormal node set ),maxStack( The maximum depth of the operand stack ),maxLocals( Maximum length of local variable table )

You can see , One of the methods InsnList object , It refers to the abstraction of the instruction set of a method , Let's continue here
InsnList
public class InsnList implements Iterable<AbstractInsnNode> {
private int size;
private AbstractInsnNode firstInsn;
private AbstractInsnNode lastInsn;
AbstractInsnNode[] cache;
...
You can see , The main object is firstInsn, And lastInsn, Represents the head instruction and tail instruction of the method instruction set , Every instruction is actually abstracted into AbstractInsnNode Subclasses of ,AbstractInsnNode Defines the most basic information of an instruction , We can look at the subclasses of this class

Here we take a look at our most commonly used methodInsnNode
public class MethodInsnNode extends AbstractInsnNode {
/**
* The internal name of the method's owner class (see {@link
* org.objectweb.asm.Type#getInternalName()}).
*
* <p>For methods of arrays, e.g., {@code clone()}, the array type descriptor.
*/
public String owner;
/** The method's name. */
public String name;
/** The method's descriptor (see {@link org.objectweb.asm.Type}). */
public String desc;
/** Whether the method's owner class if an interface. */
public boolean itf;
This is the most fundamental definition of a common method instruction ,owner( Method caller ),name( Method name ),desc( Method signature ) wait , They all have similar structures , This is also the focus of our next practical battle .
Signature
Um. ! Let's finally introduce this magical thing ! I don't know when you are reading the introduction , Is there any doubt on your face , I interpret this as generic signature , This heel desc( Function signature ) What's the difference between the parameters ? Of course , This is not just a function , In attributes , Class structure appears ! Isn't it amazing !
Actually Signature Property is in JDK 1.5 After the release, it was added to Class In the document specification , It is an optional fixed length attribute , Can appear in class 、 In the attribute table of the attribute table and method table structure . Let's think about it ,jdk1.5 What happened ! In fact, it is the support for generics , that 1.5 pre-release sdk What do I do , Is it necessary to be compatible ! therefore java The standards group came up with a compromise , It's generic erasure , Generic information compilation ( Type variable 、 Parameterized types ) after All have been wiped away , So as to be compatible with the former . Then this leads to another problem , Erased generic information is sometimes just what we need , therefore Signature And that's what happened , Store this generic information here , To provide the acquisition of type information such as runtime reflection ! You can actually see , The value of most of our methods or attributes is null, Only when there is a generic definition , Generic information will be stored in Signature Inside
The actual part
All right. ! With a theoretical basis , We should also go to actual combat , It's not saliva ! Take our thread optimization as an example , In a work project , Or in old projects , There may be most irregular thread creation operations , For example, direct new Thread wait , The generated thread name will be given the default name , We first call this kind of thread “ Anonymous threads ”! Of course ! It's not that this thread has no name , Instead, the thread name is generally “Thread -1 ” This kind of name without extra information , This will cause great interference to our later thread maintenance , After a long time , Most of these anonymous threads may exist , It is possible to create threads oom crash! So our goal is , Give these threads “ name ”, That is, the name of the caller
solve “ anonymous ”Thread
In order to achieve this goal , We need to be right about thread Have an understanding of the structure of , Of course Thread There are many constructors for , Let's give a few examples
public Thread(String name) {
init(null, null, name, 0);
}
public Thread(ThreadGroup group, String name) {
init(group, null, name, 0);
}
You can see , We Thread Multiple constructors for , The last parameter is name, namely Thread The name of , So our hook Point is , Can you be in Thread Construction process of , Call to have name Can we achieve our goal by using the constructor of ! Let's take another look at ordinary new Thread() Bytecode

So how can we put new Thread() The way to become new Thread(name) What is the way? ? It's simple ! Just need us to put init This instruction of can be changed into a parametric way , How to change ? In fact, it's change desc! Method signature is enough , Because of a method call , Matching is based on method signature . We add a after the function string The parameters of
node yes methidInsnNode
def desc =
"${node.desc.substring(0, r)}Ljava/lang/String;${node.desc.substring(r)}"
node.desc = desc
Then can we finish it , No more than that. , We just add a parameter to the method signature pair , But that doesn't mean that our function works like this ! Because in the parameter list of method parameters string We haven't put the parameters into the operand stack yet ! Then we can construct one string Parameters are placed in the operand stack , The order is ldc It's the order !asm A class provided for us is LdcInsnNode, We can create an object of this kind , The construction parameter needs to pass in a string , Then this can put the current method owner( As explained above , Caller name ) Put it in. , Whether we have achieved the goal we want ! All right. ! We have something again , Where are we going to insert it ?

So our goal is very clear , Is in the init Insert before instruction call ,asm Also provided insertBefore Method , Provide convenient operation of inserting in front of a certain instruction .
method.instructions.insertBefore(
node,
new LdcInsnNode(klass.name)
)
Let's look at the bytecode after the last insertion

Of course , Let's insert asm The code is usually in android Provided to us Transform The stage is going on (agp There are changes in the new version , But the general workflow is consistent ), So we are transfrom In order to avoid excessive interference with classes , We also need to eliminate unnecessary stages early ! For example, we only new Thread operation , Then take Fei Opcodes.INVOKESPECIAL Operation filtering . And right and wrong init Stage ( That is, the non constructor stage ) perhaps owner Not for Thread Class can be filtered in advance , Do not participate in the change .
Then we see the complete code ( Need to be in Transform Code executed in )
static void transform(ClassNode klass) {
println("ThreadTransformUtils")
// It only deals with Thread
klass.methods?.forEach { methodNode ->
methodNode.instructions.each {
// If it is a constructor, continue
if (it.opcode == Opcodes.INVOKESPECIAL) {
transformInvokeSpecial((MethodInsnNode) it, klass, methodNode)
}
}
}
}
private static void transformInvokeSpecial(MethodInsnNode node, ClassNode klass, MethodNode method) {
// If it's not a constructor , Just quit
if (node.name != "<init>" || node.owner != THREAD) {
return
}
println("transformInvokeSpecial")
transformThreadInvokeSpecial(node, klass, method)
}
private static void transformThreadInvokeSpecial(
MethodInsnNode node,
ClassNode klass,
MethodNode method
) {
switch (node.desc) {
// Thread()
case "()V":
// Thread(Runnable)
case "(Ljava/lang/Runnable;)V":
method.instructions.insertBefore(
node,
new LdcInsnNode(klass.name)
)
def r = node.desc.lastIndexOf(')')
def desc =
"${node.desc.substring(0, r)}Ljava/lang/String;${node.desc.substring(r)}"
// println(" + $SHADOW_THREAD.makeThreadName(Ljava/lang/String;Ljava/lang/String;) => ${this.owner}.${this.name}${this.desc}: ${klass.name}.${method.name}${method.desc}")
println(" * ${node.owner}.${node.name}${node.desc} => ${node.owner}.${node.name}$desc: ${klass.name}.${method.name}${method.desc}")
node.desc = desc
break
}
}
Last
See here , It should be possible to understand asm tree api Relevant usage and practice , I hope it helps !
author :Pika
link :https://juejin.cn/post/7121643784638562317
边栏推荐
- ASM插桩:学完ASM Tree api,再也不用怕hook了
- Flutter 绘制技巧探索:一起画箭头(技巧拓展)
- Performance comparison | FASS iSCSI vs nvme/tcp
- Android studio login registration - source code (connect to MySQL database)
- Flink, the mainstream real-time stream processing computing framework, is the first experience.
- "Shandong University mobile Internet development technology teaching website construction" project training log V
- PHP write a diaper to buy the lowest price in the whole network
- Semaphore (semaphore) for learning notes of concurrent programming
- 30 knowledge points that must be mastered in quantitative development [what is individual data]?
- Tear the ORM framework by hand (generic + annotation + reflection)
猜你喜欢

XDFS&空天院HPC集群典型案例

Thinkphp6 pipeline mode pipeline use

Training log 7 of the project "construction of Shandong University mobile Internet development technology teaching website"

【DL】关于tensor(张量)的介绍和理解

并发编程学习笔记 之 Lock锁及其实现类ReentrantLock、ReentrantReadWriteLock和StampedLock的基本用法

D3.JS 纵向关系图(加箭头,连接线文字描述)

Flink connector Oracle CDC 实时同步数据到MySQL(Oracle19c)

Ribbon learning notes 1

Huawei 2020 school recruitment written test programming questions read this article is enough (Part 2)

ASM插桩:学完ASM Tree api,再也不用怕hook了
随机推荐
Performance comparison | FASS iSCSI vs nvme/tcp
Laravel swagger add access password
ssm整合
【Clustrmaps】访客统计
Research and implementation of flash loan DAPP
Xsan is highly available - xdfs and San are integrated with new vitality
Realize the scheduled backup of MySQL database in Linux environment through simple script (mysqldump command backup)
有价值的博客、面经收集(持续更新)
C # judge whether the user accesses by mobile phone or computer
Nifi changed UTC time to CST time
Centos7 silently installs Oracle
My ideal job, the absolute freedom of coder farmers is the most important - the pursuit of entrepreneurship in the future
微信小程序源码获取(附工具的下载)
【Attention】Visual Attention Network
Tear the ORM framework by hand (generic + annotation + reflection)
并发编程学习笔记 之 工具类CountDownLatch、CyclicBarrier详解
[DL] introduction and understanding of tensor
IDEA中设置自动build-改动代码,不用重启工程,刷新页面即可
以‘智’提‘质|金融影像平台解决方案
这些你一定要知道的进程知识