Soot入门学习笔记

Soot

适合参考的文档和教程如下:

北京大学软件分析技术

南京大学软件分析

Tutorials for soot

McGill University

198:515 (vt.edu)

比较好的笔记资料:

南京大学《软件分析》课程笔记

比较好的入门作业或者案例:

CSCE710 Assignment 1

基于Soot的拓展项目:

  • ByteCodeDL:使用soot生成fact,使用souffle作为datalog引擎,最后使用neo4j进行可视化,实现了多种程序分析算法;(个人觉得讨论区的案例是比较有价值的)
  • Tabby:基于soot生成代码属性图,应用案例比较多

简介

Soot是McGill大学的Sable研究小组自1996年开始开发的Java字节码分析工具,它提供了多种字节码分析和变换功能,通过它可以进行过程内和过程间的分析优化,以及程序流图的生成,还能通过图形化的方式输出,让用户对程序有个直观的了解。尤其是做单元测试的时候,可以很方便的通过这个生成控制流图然后进行测试用例的覆盖,显著提高效率。

Soot是java优化框架,提供4种中间代码来分析和转换字节码。

soot一共支持4种IR(imtermediate representation),分别是:

  • Baf:精简的字节码(bytecode)表示,操作简单,主要用来插桩类
iload 1  // load variable x1, and push it on the stack
iload 2  // load variable x2, and push it on the stack
iadd     // pop two values, and push their sum on the stack
istore 1 // pop a value from the stack, and store it in variable x1
  • Jimple:适用于优化的3-address(简单理解两个输入一个输出)中间表示,可以用来做各种优化需要的分析,比如类型推测(虚调用优化)、边界检查消除、常量分析以及传播、公共子串分析等等
stack1 = x1 // iload 1
stack2 = x2 // iload 2
stack1 = stack1 + stack2 // iadd
x1 = stack1 // istore 1
  • Shimple:Jimple的SSA(Static Single Assignment)变体,每个“变量”只被赋值一次,而且用前会定义,这可以用来做各种reaching分析,比如一个“变量”作用域,进而分析例如内联inline时需要进行的检查等等
  • Grimple:Jimple的聚合版本,不再要求表达式树线性排布(也就是按照三地址码一条一条写下来),因此减少了一些中间变量,同时也引入了new这个operator,适用于反编译和代码检查。

SSA(Static Single Assignment)

SSA即为“静态单一分配”,SSA中的所有赋值都有不同名称的变量,详细解释如下:

  • 每个定义需要给定一个新的名字;

  • 将新名称传播给后续使用;

  • 每个变量都只有一个定义。

一般生成SSA要比3AC慢很多,但是有时可以利用SSA来提高非敏感数据流分析的精度。

在这里插入图片描述

为什么要使用中间表示?

如果直接使用Java bytecode

  • 😨 太贴近机器器码(为执⾏而设计)
  • 😭 语句句类型大约有200种(⾄至多有256条指令)
  • 😫 基于栈的代码

Soot提供的输入输出格式

输入格式:

  • java(bytecode and source code up to Java 7)
  • android字节码
  • Jimple中间表示
  • Jasmin,低级中间表示
  • soot提供的分析功能

输出格式:

  • java 字节码
  • android字节码
  • Jimple
  • Jasmin

Soot提供的分析功能

  • 调用图构建
  • 指针分析
  • Def/use chains
  • 模板驱动的程序内数据流分析
  • 模板驱动的程序间数据流分析,与heros结合
  • 别名可以使用基于流、字段和上下文敏感的需求驱动指针分析Boomerang解析
  • 结合FlowDroid或IDEal的污染分析

原理解析

Soot的执行过程如下

在这里插入图片描述

基本数据结构

Overview

上述数据结构的总览图如下
在这里插入图片描述

Data structures

在这里插入图片描述

Scene:分析环境

  • 代表 Soot 输入程序的整个运行、分析、变换的环境。通过 Scene 类,你可以设置应用程序类(供 Soot 进行分析的类)、主类(包含 main 方法的类)以及访问关于程序间分析的信息(例如,指向信息和调用图)。

SootClass:代表了加载到 Soot 中或使用 Soot 创建的单个类,特别的SootClass有以下3类:

  • argument class为我们自己写的程序入口,通过这个class来配置编译选项等并启动soot分析框架
  • application class为待分析的java程序
  • library class为soot库函数

SootField:类中的成员字段,或者是类的属性

SootMethod:类中的单个方法

Method Body

Body:用来表示方法的实现,在Soot中,一个Body(不同的IR,有着不同的Body表现形式,如JimpleBody)隶属于一个SootMethod,即Soot用一个Body为一个方法存储代码。

每个Body里面有三个主链,分别由 Locals 链(body.getLocals())、Units 链(body.getUnits())、Traps 链(body.getTraps())组成。

  • Locals 链存储方法中的局部变量,可以通过body.getLocals()访问。
  • Units 链存储代码片段的接口
  • Traps 链存储方法中异常处理的接口

在这里插入图片描述

根据上图,可知jimple body对象还可以调用getUnits()方法来获得Units Chain上所有的Units,每个Unit就是jimple body之中的一条语句。

Statements

Soot中的Statements或者声明是用接口 Unit 表示,所以有不同的接口实现,因为有不同的中间表示。

Unit 在 Jimple 中的实现是 Stmt(在Grimple中一个Inst是一个Unit),并且这些类型都继承了Unit这个类。因此可以直接用instanceof来判断一条语句到底是identityStmt(特殊值,如参数、this或被捕获的异常,分配给一个Local)类型,assignStmt类型(赋值语句)或者其他的什么类型。

Stmt可以分为15种具体的语句类型:

在这里插入图片描述

注意:AssignStmt 表示赋值语句;而 IdentityStmt表示变量是参数或者this等

public int foo(java.lang.String) {
    // locals
    r0 := @this; // IdentityStmt
    r1 := @parameter0;
    if r1 != null goto label0; // IfStmt
    $i0 = r1.length(); // AssignStmt
    r1.toUpperCase(); // InvokeStmt
    return $i0; // ReturnStmt
label0: // created by Printer
    return 2;
}
Value

单个数据表示为值或者Value,实现了Value接口的类有:

Local(局部变量)

  • JimpleLocal 局部变量

  • TemporaryRegisterLocal$开头的临时变量

java.lang.String[] r0; //Local
int i0, i1, i2, $i3, $i4;
java.io.PrintStream $r1, $r2;
java.lang.Exception $r3, r4;

Constant(常量),常用StringConstantNumericConstant

Expression(Expr),表示各种运算。Expr接口又有大量的实现,例如NewExpr和AddExpr。一般来说,一个Expr对一个或几个Value进行一些操作,并返回另一个Value,比如下面这个表达式,在这个AssignStmt中,它的leftOp是 x,rightOp是 AddExpr(y+2)。

x = y + 2 //AssignStmt 

Ref

  • ConcreteRef

    • ArrayRef 指向数组

    • FieldRef 指向field

      • StaticFieldRef 静态field的引用
      • InstanceFieldRef 指向的field是一个对象实例
  • IdentityRef

    • CaughtExcrptionRef 指向捕获到的异常的引用

    • ParameterRef 函数参数的引用@parametera.f

    • ThisRef this的引用,@this

Box

Box:可以看做指向数组的指针,当Unit包含另一个Unit的时候,需要通过Box来访问

  • 包括 UnitBox(指向Units)、ValueBox(指向Values)

从下图可以看出

  • 一个 Unit 可以有多个 UnitBox,但是每个 UnitBox 只能指向一个 Unit

  • 一个Value可以有多个ValueBox,但是每个ValueBox只能指向一个Value,对于一个Unit,可以得到很多个ValueBox,包含着这条语句内部的所用到的以及和所定义的值。

    在这里插入图片描述

在上图中可以注意到i1=0 等于是一个Stmt, i0是一个Valuebox,里面包含这i0这个local 的value

常用方法

public List<ValueBox> getUseBoxes();	//返回 Unit 中使用的 Value 的引用
public List<ValueBox> getDefBoxes();	//返回 Unit 中定义的 Value 的引用
public List<ValueBox> getUseAndDefBox();//返回 Unit 中定义并使用的 Value 的引用

以List of ValueBox的形式返回。

// 一般 Value 指的是 Local(变量)、Expr(表达式)、Constant(常量)
public List geUnitBoxes();				//获得 Unit 跳转到的 UnitxBox 列表
public List getBoxesPointingTothis();	//Unit 作为跳转对象时,获取所有跳转到该 Unit 的 UnitBox
public boolean fallsThrough();			//如果接下来执行后面的 Unit,则为 true
public boolean branches();				//如果执行时会跳转到其他 Unit,则返回 true。如:IfStmt、GotoStmt
public void rediectJumpsToThisTo(Unit newLocation);//把跳转到 Unit 重定向到 newLocation

通过以上的方法我们能够确定跳转到这个Unit的其他Unit(调用getBoxesPointingToThis()),也可以找到跳到的其他Unit(调用getUnitBoxes())。

主流IR Jimple

Jimple 是什么?

Jimple 是 Soot 提供的一种中间表示形式,它是基于栈的、有类型的、基于三地址的表示。与 Java 字节码相比,Jimple 更接近源代码的结构,因为它是直接从字节码操作翻译而来的。Jimple 的特点包括:

  • 基于栈:Jimple 使用操作数栈来存储中间结果和临时变量,这使得它更接近 Java 源代码的结构。
  • 有类型:每个参数都有明确的类型声明,这有助于保留源代码中的类型信息,避免了类型精度的丢失。
  • 基于三地址分析:复杂的表达式会被转化成一系列的基本操作,即三地址码。每个操作包含一个目标变量和两个操作数,这种形式便于后续的分析和转换。

在这里插入图片描述

在Soot中,主要是基于Jimple进行分析,在流程中构建的是JimpleBody,而其它的Body的构建需要通过开关来控制。

Java 字节码是底层的、基于栈的操作形式,而 Jimple 更接近源代码的结构。让我们来看一个简单的示例来对比两者之间的区别:

Java 源代码:

int i, j;
i = 2;
j = 2 * i + 8;

Java 字节码:

0: iconst_2
1: istore_1
2: iconst_2
3: iload_1
4: imul
5: bipush 8
6: iadd
7: istore_2

Jimple 代码:

int i, j, temp$0, temp$1, temp$2, temp$3;

temp$0 = 2;
i = temp$0;

temp$1 = 2 * i;
temp$2 = temp$1;

temp$3 = temp$2 + 8;
j = temp$3;

Soot执行阶段

在Soot中,Soot的执行分为几个称为packs的阶段。首先要生成Jimple代码,以便输入到一系列的转换函数(也称为Pack)中。

每个Pack的命名都是有规律可循的,按照约定,命名方式通常包括以下几个部分:

  • 全局模式设置(可选):字母缩写的是"w"。

  • IR类型: 在过程内执行中,第一个字母代表中间表示(Intermediate Representation)的类型。

    • j --> Jimple
    • s --> Shimple
    • b --> Baf
    • g --> Grimp
  • 角色: 第二个字母表示Pack在整个分析过程中所扮演的角色。例如,“b” 代表Body Creation(创建方法体),“o” 代表Optimization(优化),“t” 代表User-defined Transformation(用户定义的转换),“a” 代表Attribute generation(属性生成)等。

  • 后缀: 通常最后一个字母是 “p”,表示这是一个Pack。

例如,“jtp” 表示在Jimple阶段应用用户定义的转换,“bbp” 表示在Jimple阶段对方法体应用用户定义的转换,“stp” 表示在Shimple阶段应用用户定义的转换。

过程(程序)内执行

在这里插入图片描述

上面这张图是过程内执行执行流程图。在这个执行流程中,每个应用程序类都会按照一条路径进行处理,但它们无法访问其他应用程序类处理过程生成的任何信息。换句话说,每个应用程序类的处理过程是相互独立的,它们之间没有共享的信息或状态。

默认情况下,黑色的线表示的是默认打开的Pack,而红色的线表示可以通过添加编译选项来打开的Pack。用户可以在转换阶段添加自己的分析相关操作,即在Jimple Transformation Pack(jtp)阶段实现。

例如,在jtp 阶段添加一个小的自定义的Transformer,可以输出程序中所有class和method的名称等信息。这在PackManager注册后会在适当的阶段执行,并且Soot的执行流执行完自定义的myTransform后,将继续沿着执行流执行。

import soot.*;
import soot.options.Options;
import java.util.*;

public class JimpleAnalysis {
    public static void main(String[] args) {
        // 设置 Soot 选项
        Options.v().set_src_prec(Options.src_prec_java);
        Options.v().set_output_format(Options.output_format_jimple);
        
        // 加载需要分析的类
        SootClass myClass = Scene.v().loadClassAndSupport("MyClass");
        
        // 在jtp阶段添加自定义的Transformer
        PackManager.v().getPack("jtp").add(new Transform("jtp.myTransformer", new SceneTransformer() {
            @Override
            protected void internalTransform(String phaseName, Map<String, String> options) {
                // 输出所有类的名称
                System.out.println("Classes:");
                for (SootClass clazz : Scene.v().getClasses()) {
                    System.out.println(clazz.getName());
                }
                
                // 输出每个类中的方法名称
                System.out.println("Methods:");
                for (SootClass clazz : Scene.v().getClasses()) {
                    System.out.println("Class: " + clazz.getName());
                    for (SootMethod method : clazz.getMethods()) {
                        System.out.println("    Method: " + method.getName());
                    }
                }
            }
        }));
        
        // 运行Soot分析
        PackManager.v().runPacks();
    }
}
数据流分析
控制流图CFG

一个CFG是表示一个方法内的程序执行流的图,它由一系列基本块(basic block)组成,其中每个基本块是一组按顺序执行的语句。控制流图中的节点通常代表基本块,而边则表示程序执行的控制流转移,例如条件语句、循环或函数调用等。例如语句A执行后的下一条语句是B,则CFG中应有一条从A到B的有向边。

  • 通常所有的控制流分析(Control Flow Analysis)指的就是创建控制流图(Control Flow Graph);
  • CFG是静态程序分析的基本结构;
  • CFG中的节点可以是单独的3AC,或者是基本块(BB,Basic Block);

在这里插入图片描述

什么是数据流分析

How application-specific Data(abstraction) Flows(safe-approximation) through the Nodes (Transfer function)and Edges(Control-flow handling) of CFG?

  • 这里的Application-specific Data指的就是我们静态分析时关注的抽象(Abstraction)数据,例如进行污点分析时,我们关注的就是污点对象;

  • Node通常通过转换函数(Transfer functions)进行分析处理,例如函数调用(Method Call),形参到返回值的转换处理;

  • Edge的分析也就是Control-flow处理,例如GOTO等指令的处理;

  • 不同的数据流分析存在不同的抽象数据(data abstraction)、不同的safe-approximation策略、不同的tranfer functions以及不同的control-flow handings。

例如,如果我们关注程序变量的正负等状态,那么此时的Application-specific Data指的就是表示变量状态的一些抽象符号;Transfer functions指的就是各种加减乘除运算;Control-flow handing指的就是merges处的符号合并。

数据流分析前驱知识

Input and Output States

  • 每一个IR的执行,都会将input state 转换成output state

  • input(output) state和statement之前(之后)的program point相关;

  • 数据流分析就是,对于程序中的所有IN[s]和OUT[s],需要找到一个方法去解析一系列的safe-approximation约束规则;这些约束规则基于语句的语义(transfer functions)或者控制流(flows of control)。
    在这里插入图片描述

Transfer Function’s Constraints

  • Transfer Function’s Constraints即基于转换函数的约束规则,主要分为两种,一种是Forward Analysis,另外一种就是Backward Analysis;

  • 对于Forward Analysis来讲,IN[s]经过转换函数fs的处理,可以得到OUT[s];

  • 对于Backward Analysis来讲,OUT[s]经过转换函数fs的处理,可以得到IN[s]。

在这里插入图片描述

Control Flow’s Constraints

  • Control Flow’s Constraints即基于控制流的约束规则,主要体现在BB之间以及BB之内;

  • 对于 IN[Si+1] = OUT[Si] ,要说明的含义其实就是,对于每一个statement,后一个statement的输入就是前一个statement的输出;因为BB中的statement不能存在分叉啥的,所以能这么认为;

  • 对于 IN[B] = IN[S1] 以及 OUT[B] = OUT[Sn] ,要说明的含义其实就是,对于每一个BB,其输入就是第一个statement的输入,其输出就是最后一个statement的输出。

可达性分析

TODO

活跃变量分析

TODO

可用表达式分析

TODO

过程(程序)间执行

在这里插入图片描述

Jimple Body Creation

首先,Soot 会将 jb pack应用于每个具有程序Body的方法。本地方法如 System.currentTimeMillis() 是没有Body的。jb pack是固定的,它负责创建 Jimple 表示。它不能被改变!

全局模式(Whole-program mode)

在这种模式下,Soot在执行周期中包含另外三个packs:cg(call-graph generation)、wjtp(whole Jimple transformation pack)和wjap(whole Jimple annotation pack)。此外,为了添加整个程序的优化(例如静态内联),我们可以指定-W选项,进一步将wjop(whole Jimple annotation pack)添加到混合中。

  • cg,即调用图包,使用各种构建算法构建调用图,不同模式下构建调用图的方式不同,详细参数见此处。

    简单获取cg图的方法:

    在这里插入图片描述

  • wjtp,即整个Jimple转换包。这是您应该插入任何跨过程/整个程序分析的主要包。当它执行时,调用图已经被计算出来,可以立即访问。

  • wjop,即整个Jimple优化包。如果您希望根据您的整个程序分析结果实现代码优化或其他Jimple IR的转换,则应使用此包。

  • wjap,即整个Jimple注释包,可用于用额外的元数据注释Jimple语句。此元数据可以持久化在Java字节码属性中。

所有这些 packs 都可以更改,特别是可以向这些 packs 添加 SceneTransformers,这些 SceneTransformers 进行整个程序分析。SceneTransformer 通过 Scene 访问程序,以便分析和转换程序。下面的代码片段向 wjtp 包添加了一个伪Transformer:

public static void main(String[] args) {
  PackManager.v().getPack("wjtp").add(
      new Transform("wjtp.myTransform", new SceneTransformer() {
        protected void internalTransform(String phaseName,
            Map options) {
          System.err.println(Scene.v().getApplicationClasses());
        }
      }));
  soot.Main.main(args);
}
jtb && jop && jap Pack

jtp默认是可用且是空的。通常在这里进行过程内分析(intra-procedural analysis)

jop包含一套Jimple优化操作。它默认未启用,可以通过Soot的命令行 -o 或者 -p jop enabled 来启用。

jap是Jimple的注释(annotation)包。每个Jimple body里都可以加入注释,这样你或者其他人或JVM便可以评估优化的结果。这个包默认是启用的,但该包中所有的阶段(phases)默认未启用,因此,如果你把你的分析添加到这个包里,默认会自动启用。

请注意,添加到(non-whole)Jimple 包的每个 Transform 必须是 BodyTransformer。

比如以下代码片段启用了空指针标记器,并注册了一个新的 BodyTransformer,该转换器会打印出每个方法中每个语句的标记:

public static void main(String[] args) {
  PackManager.v().getPack("jap").add(
      new Transform("jap.myTransform", new BodyTransformer() {

        protected void internalTransform(Body body, String phase, Map options) {
          for (Unit u : body.getUnits()) {
            System.out.println(u.getTags());
          }
        }

      }));
  Options.v().set_verbose(true);
  PhaseOptions.v().setPhaseOption("jap.npc", "on");
  soot.Main.main(args);
}

bb && tag Pack

Soot接下来对每个body应用bbtag Pack。bb Pack将优化并打了标签(optimized anf tagger)的Jimple bodies转换成Baf bodies。Baf是Soot里一种基于栈的中间表示,通过Baf,Soot创建字节码。tag Pack汇聚特定的标签(aggregates certain tags)。比如说,如果有多条Jimple(或者Baf)语句共享同一个行号标签,那么Soot便只会在第一条含有这个标签的语句上保留这个标签,保证唯一性。

其他

想要了解详细过程解释可以查看Prof. Dr. Eric Bodden » Packs and phases in Soot

各种Pack由类PackManager管理,其init方法负责创建各Pack实例对 象,并为之添加变换器。下面我列举了Soot中的部分Pack。

Pack名所属的Pack类说明
jbJimpleBodyPack(BodyPack的子类)创建Jimple体
jjJavaToJimplePack(BodyPack的子类)实现Java到Jimple的转换
cgCallGraphPack(由ScenePack派生)调用图生成、指针分析、类层析分析(CHA)
wstpScenePack全局Shimple变换包
wsopScenePack全局Shimple优化包
wjtpScenePack全局Jimple转换包
wjopScenePack全局Jimple优化包
wjapScenePack全局Jimple注释包
jtpBodyPackJimple转换包
jopBodyPackJimple优化包
japBodyPackJimple注释包
tagBodyPack代码属性tag聚集包

使用命令行进行阶段定制

阶段选项是可以应用于Soot中不同packs的配置,以定制它们在分析过程中的行为。以下是如何在Soot中与阶段选项进行交互的方法:

  1. 列出可用的packs
    • 要获取Soot中所有可用packs的列表,您可以在命令行中执行命令java soot.Main -pl
  2. 获取特定pack的帮助
    • 您可以通过使用命令java soot.Main -ph PACK来获取特定pack的帮助和可用选项,其中PACK是从使用-pl选项运行Soot时列出的pack名称之一。
  3. 为pack设置选项
    • 要为pack设置选项,您需要使用-p选项,后面跟着pack名称以及形式为OPT:VAL的键值对,其中OPT是您要设置的选项,VAL是要设置的值。
    • 例如,要关闭所有用户定义的程序内转换,您可以执行:java soot.Main -p jtp enabled:false MyClass,其中MyClass是您希望进行分析的类。
过程间分析

数据流分析等都是程序内的分析,是不处理方法调用的,如果遇到了函数调用,过程间分析会沿着过程间的控制流edges进行数据流传播。

在这里插入图片描述

OO (面向对象)语言的调用图的构造(以 JAVA 为代表):

  • 类层次分析(CHA,Class Hierarchy Analysis):效率高
  • 指针分析(k-CFA,Pointer Analysis):精确度高
调用图

为了更方便的进行过程间分析,我们通常还需要构造Call Graph。

Call Graph即为调用图,也就是程序中调用关系的表示。本质上,call graph是一组从callers到他们的目标方法的调用边(call edges),callers的目标方法称为(callees)。

在这里插入图片描述

一个Call Graph图示如下:

在这里插入图片描述

call graph是过程间分析的基础,对于创建call graph的几种比较有代表性的算法如下,越往右,精度越高,但是速度越低,成本也会越高。

在这里插入图片描述

更多调用图构造算法详情可见Call Graph Construction Algorithms Explained – Ben Holland

函数调用类型

对于Java程序而言,总共分为三种函数调用:Static Call、Special Call、Virtual Call;其中主要关注的就是Virtual Call,Virtual Call也是Java多态的关键体现,对于Virtual Call,调用的目标方法(callee)只能在运行时确定,对于静态程序分析而言,确定callee就成了一个难点。
在这里插入图片描述

class MyClass {
    // 静态方法
    static void staticMethod() {
        System.out.println("This is a static method.");
    }
    
    // 实例方法
    void instanceMethod() {
        System.out.println("This is an instance method.");
    }
}

public class Main {
    public static void main(String[] args) {
        // 静态调用
        MyClass.staticMethod();
        
        // 特殊调用(构造函数调用)
        MyClass obj = new MyClass();
        
        // 虚拟调用(实例方法调用)
        obj.instanceMethod();
    }
}

虚拟调用是指通过对象引用调用非静态方法。在 Java 中,非静态方法的调用是多态的,即在运行时根据对象的实际类型来确定调用哪个方法。因此,虚拟调用需要在运行时进行动态绑定。

Method Dispatch of Virtual Calls

Java,虚拟函数的调用是通过一个称为虚表(vtable)的结构来实现的。对象中存储着指向虚表的指针,虚表中存储着对应于类中虚拟函数的函数指针。当调用虚拟函数时,实际执行的函数由对象的实际类型决定,并且是通过虚表指针进行查找和调用的,这个过程就是虚函数调度或者分派(Dispatch)。

对于Virtual Call,其callee只能在运行时才能确定,callee的确定(或者说Dispatch)取决于 :

  • receiver object的类型c
  • caller的方法描述(descriptor)。形如<ReturnType MehtodName(ParameterTypes)>
Signature = class type + method name + descriptor
Descriptor = return type + parameter types

定义函数 Dispatch(c, m) 去模拟运行时方法分派,总的思路是优先在子类中匹配,匹配不到则递归地到父类中匹配

在这里插入图片描述

其具体流程是寻找true type为c,调用的方法为m的真实目标方法(因为Java多态问题,Virtual Call需要计算运行时真实调用的方法),如果c类中存在一个非抽象的方法 m ′ m^{\prime} m,其方法名和方法签名和要寻找的m一样,则 m ′ m^{\prime} m即为我们需要找的真实方法;否则从类c的父类中去寻找m;

示例

在这里插入图片描述
)

类层次分析(Class Hierarchy Analysis)

适用于 IDE 等场景,快速分析并对准确性没有较高的要求

  • 定义函数 Resolve(cs) 解析方法调用的可能的目标方法,分别处理 static callspecial callvirtual call

  • CHA假设变量a可以指向类A以及类A的所有子类的对象,所以CHA计算目标方法的过程就是查询类A的整个继承结构来查询目标方法注意 special call 中调用父类方法的时候需要递归寻找,为了形式统一使用用 Dispatch 函数

  • 注意 virtual call 需要对对象的声明类型及其所有子类做 Dispatch(可能产生假的目标方法,不够准确)
    在这里插入图片描述

一个计算案例如下:

注意理解 Resolve( b.foo() )

在这里插入图片描述

CHA的优势是速度快,原因如下:

  • 只考虑call site中receiver variable的declared type和它的继承结构;

  • 忽略数据流和控制流信息。

CHA的劣势是精度较低,原因如下:

  • 容易引入虚假目标方法;

  • 没有使用指针分析。

Call Graph Construction

即通过CHA算法生成Call Graph,步骤如下:

  • 从入口方法开始(例如对于Java而言的main方法);

  • 对于每一个可达方法m,在方法m中的每一个调用点cs,通过CHA算法为每一个call site计算或者解析目标方法;

  • 重复这个过程直到没有新的方法被发现。

图示如下:

在这里插入图片描述

过程间控制流图 ICFG

ICFG(interprocedural control-flow graph)的信息就是CFG+CG(Call edges + Return edges)的信息。可以看做是给所有方法的CFG加上这些方法之间互相调用的边(CG)所形成的图。调用边(call edge)从调用语句(call site)连到被调方法(callee)的入口。

与CG不同的是,ICFG除了调用边,还包含相应的返回边(return edge),从callee的出口连到call site之后执行的下一个语句。

在这里插入图片描述

过程间数据流分析

过程间常量传播分析(Interprocedural Constant Propagation)

在 ICFG 中保留了调用点到返回点之间相连的边(call-to-return edge),能使得 ICFG 能够传递本地数据流(单个 CFG 内产生的数据流)

在本地方法的 CFG 中的 Node Transfer 需要把调用点的左值变量 kill 掉(Return Edge Transfer 会覆盖这些变量的值)

下面是一个详细示例:

在这里插入图片描述

指针分析

Pointer Analysis即指针分析;如果我们使用CHA创建CallGraph,我们知道CHA仅仅考虑Class Hierarchy(类继承关系),那么正对如下程序分析,因为Number存在三个子类,那么调用 n.get() 方法的时候,就会产生三条调用边,其中有两条是假的调用边,导致最终分析的结果是一个不准确的结果:

在这里插入图片描述

而如果通过指针分析,那么就可以清楚地知道变量n指向的对象,能有效避免 CHA 中出现 fake target 的问题,由此只会产生一条调用边,此时分析的结果就是一个precise(精确)的结果:

在这里插入图片描述

  • 指针分析是基础的静态分析,计算一个指针能够指向内存中的哪些地址
  • 对于面向对象语言,以 JAVA 为例,指针分析计算一个指针(variable or field)能够指向哪些对象
  • 指针分析可以看作一种 may analysis,计算结果是一个 over-approximation

在这里插入图片描述

实际操作

下载地址: Central Repository: org/soot-oss/soot (maven.org)

命令行使用

下载jar包,并使用主类,详细类使用文档可见 Overview (Soot API)

java -cp sootclasses-trunk-jar-with-dependencies.jar soot.Main
Soot version trunk
Copyright (C) 1997-2010 Raja Vallee-Rai and others.
All rights reserved.
...

输入java -cp sootclasses-trunk-jar-with-dependencies.jar soot.Main -help可获得命令的解释帮助

当然在Github项目的doc文件夹下也有一个叫soot_options.htm的html版本的参数帮助文档便于阅读

或者是在线的参数参考网站:Soot Command Line Options

或者最适于阅读的PDF版本

常用参数解释
  • -cp-classpath: 不同于java的classpath,soot也有自己的classpath且默认classpath为空,所以使用的时候需要添加一下当前路径(不能用~)。(soot不会默认去当前文件夹下寻找符合条件的文件,而是会去它自身的classpath寻找,而soot的classpath默认情况下是空的,这也就导致soot找不到对应的文件,解决办法是在命令里添加指定位置的代码-cp,-cp .表示在当前目录寻找。)

  • -pp:soot进行汇编、反汇编等等工作时,需要类型信息、类的完整层次结构,所以需要java.lang.Object,使用该参数可以自动包含所需的jdk中的jar文件

较为详细的调用(不加pp)

java soot.Main -f jimple --soot-classpath .:/usr/local/pkgs/openjdk17/jre/lib/rt.jar Hello

输入

  • -process-dir dir: 处理指定目录中的所有类。

    可以将多个文件并排输入,也可以使用-process-dir一次输入一个文件夹

    假设当前目录下有以下文件

    A.class
    B.class
    A.java
    B.java
    

    可以使用以下命令进行解析

    java -cp soot.jar soot.Main -cp . -pp A B
    java -cp soot.jar soot.Main -cp . -pp -process-dir .
    
  • -allow-phantom-refs: 允许未解析的类。

  • -main-class class: 设置整个程序分析的主类。

  • -process-jar-dir dir: 处理指定目录中的所有 JAR 文件中的类。

  • -src-prec format 是 Soot 中的一个重要参数,用于设置源代码的优先级,决定了 Soot 将如何处理输入的 Java 源代码或者类文件

    • c: 表示 Soot 应该只使用类文件(.class 文件)作为输入,而忽略任何源代码(.java 文件)。
    • J: 表示 Soot 应该使用 Jimple 作为中间表示(IR)来处理输入的 Java 源代码或类文件。
    • java: 表示 Soot 应该直接使用 Java 源代码作为输入,并将其转换为 Jimple。
    • apk: 表示 Soot 应该处理 Android 应用程序包(APK)文件,提取其中的类文件和资源。
    • dotnet: 表示 Soot 应该处理 .NET 程序集文件。

输出

  • -d dir, -output-dir dir: 指定输出文件的目录。
  • -f format,-output-format format: 设置输出格式。
    • J, j: 输出为 Jimple 格式。
    • S, s: 输出为 Shimple 格式。
    • B, b: 输出为 Baf 格式。
    • G, g: 输出为 Grimple 格式。
    • X: 输出为 XML 格式。
    • dex: 输出为 Dex 格式。
    • force-dex: 强制输出为 Dex 格式。
    • n: 不输出。
    • jasmin: 输出为 Jasmin 格式。
    • c: 输出为类文件。
    • d: 输出为 Dava 格式。
    • t: 输出为模板格式。
    • a: 输出为 ASM 格式。

为了方便使用我们可以使用简单的脚本(bat或者shell)

#!/bin/bash
# 路径设置
SOOT_PATH="/home/asiv/reserch/oss/java/sootclasses-trunk-jar-with-dependencies.jar"

# soot路径设置
SOOT_CLASSPATH="."

# 如果有额外的依赖 jar 包,可以添加到类路径中
# CLASSPATH="$CLASSPATH:path/to/dependency.jar"

# Soot 命令
java -cp $SOOT_PATH soot.Main -cp . -pp $1 $2 $3 $4 $5 $6 $7 $8 $9
#chmod +x soot.sh
#./soot.sh -f J -process-dir target -d .
#!/bin/bash

# 路径设置
SOOT_PATH="/home/asiv/reserch/oss/java/sootclasses-trunk-jar-with-dependencies.jar"

# soot路径设置
SOOT_CLASSPATH="."

# 如果有额外的依赖 jar 包,可以添加到类路径中
# CLASSPATH="$CLASSPATH:path/to/dependency.jar"

# Soot 命令
java -cp $SOOT_PATH soot.tools.CFGViewer -cp . -pp $1 $2 $3 $4 $5 $6 $7 $8 $9
#./soot_cfg.sh Test -d .
生成示例

源代码

public class Helloworld {
	public static void main(String[] args) {
		System.out.println("Hello, world");
	}
}

jimple格式输出

javac HelloWorld.java
./soot.sh -f J HelloWorld -d .
#完整的命令行
#java -cp sootclasses-trunk-jar-with-dependencies.jar soot.Main -pp -cp .  HelloWorld -d .

HelloWorld.jimple的文件内容

public class HelloWorld extends java.lang.Object
{

    public void <init>()
    {
        HelloWorld r0;

        r0 := @this: HelloWorld;

        specialinvoke r0.<java.lang.Object: void <init>()>();

        return;
    }

    public static void main(java.lang.String[])
    {
        java.io.PrintStream $r0;
        java.lang.String[] r1;

        r1 := @parameter0: java.lang.String[];

        $r0 = <java.lang.System: java.io.PrintStream out>;

        virtualinvoke $r0.<java.io.PrintStream: void println(java.lang.String)>("hello");

        return;
    }
}

注意

可以看到soot已经帮我们把java的class代码转换为了jimple格式,可以看到的是代码逻辑与之前的程序是一致的,但是代码已经变成了三地址码的格式。

  • 变量名字之前带有$的就是soot额外引入的,帮助构建三地址码的变量,其他则是原程序之中的变量

  • method的参数以及this指针会用@来修饰。

  • 对于函数调用会有不用类型的invoke前缀来修饰,共有如下三种。

    在这里插入图片描述

Soot.Main运行

Soot.Main 原理同命令行运行

public static void main(String[] args){
        // 获取类路径
        String classpath = args[0];
        // 打印类路径
        System.out.println(classpath);
        // 调用soot.Main的main方法,生成Jimple代码
        soot.Main.main(new String[] {
                "-f", "J", // 输出格式为Jimple
                "-soot-class-path", classpath, // 设置Soot的类路径
                "-pp", // 使用Soot的默认类路径
                args[1] // 要分析的类文件
        });
        // 结束程序
        return;
    }

Options运行

当使用 Soot 进行 Java 字节码分析时,Options API 提供了一种灵活的方式来配置 Soot 的行为。

其中

  • Options 类通常包含了一系列可以配置Soot行为的全局选项。
  • .v() 方法是获取单例对象或者静态方法,用来访问或修改Soot的全局选项集

注意:

Options.v().set_app(true);

set_app(true) 是设置某个具体的选项值,这里的app选项通常指的是处理整个应用程序(Application)而非库文件(Library)。当设置为true时,Soot会按照处理完整的应用程序的方式来运行,这意味着它可能包括对主类(即包含main方法的类)以及所有依赖项的处

		Printertemp printertemp = new Printertemp();
		Transform t0 = new Transform("wjtp.Printertemp", printertemp);
		PackManager.v().getPack("wjtp").add(t0);

		Options.v().set_whole_program(true);
		Options.v().set_allow_phantom_refs(true);//允许缺失
		Options.v().set_prepend_classpath(true);
		Options.v().set_process_dir(Arrays.asList("*** ***"));//需要分析的.class文件路径
		Options.v().setPhaseOption("jb", "use-original-names:true");
		Options.v().set_keep_line_number(true);
		Options.v().set_output_format(Options.output_format_jimple);
		Scene.v().loadNecessaryClasses();
		
		PackManager.v().runPacks();
配置流程分析

那么问题来了什么时候应该配置这些选项呢?

  • 在类加载阶段,可以设置加载哪些类,哪些不进行加载。
  • 类加载之后,还需要对类贴上一定的标签,分类。(哪些类是应用的类,哪些类是library类等等),进而为phase的处理阶段进行一定的准备。
  • 在phase阶段对于一些类的处理,是可以定制的。(比如说,只解析应用的类,而不考虑library类等,因为前面对于类进行了标记)

在这里插入图片描述

API参数总体上可以分为以下几类:

  • 一般配置
  • 输入配置
  • 输出配置
  • 处理配置
  • 输入特性的配置
  • 输出特性的配置

一般配置

  • set_output_format(): 设置输出格式,决定分析结果的表现形式。

    Options.v().set_output_format(Options.output_format_jimple);
    
  • set_allow_phantom_refs(): 设置是否允许虚引用。

    Options.v().set_allow_phantom_refs(true);
    

    phantom 类是既不在进程目录中也不在 Soot 的 classpath 中的类,但它们被 Soot 加载的一些类/方法体所引用

    • 如果启用了 phantom 类,Soot 不会因为这种无法解决的引用而中止或失败,而是创建一个空的存根,称为 phantom 类,它包含 phantom 方法来弥补缺失的部分。

    建议当遇到以下报错时开启或者补充引用

    Warning: java.lang.invoke.LambdaMetafactory is a phantom class!
    
  • set_prepend_classpath(): 设置是否将当前类路径作为 Soot 类路径的前缀即使用内置的类路。相当于-pp

    Options.v().set_prepend_classpath(true);
    

输入配置

  • set_soot_classpath(): 设置 Soot 的类路径,用于加载分析的类。

    Options.v().set_soot_classpath("/path/to/classes:/path/to/lib.jar");
    
  • set_whole_program(): 设置是否启用整个程序分析模式。

    Options.v().set_whole_program(true);
    
  • set_no_bodies_for_excluded(): 设置是否对排除的方法忽略其主体。

    Options.v().set_no_bodies_for_excluded(true);
    
  • add_include(): 添加要包含的类或方法。

    Options.v().add_include("your.package.*");
    
  • add_exclude(): 添加要排除的类或方法。

    Options.v().add_exclude("your.package.ExcludedClass");
    
  • set_main_class() : 指定主类。主类是程序的入口点,Soot 将从主类开始分析程序的调用图和依赖关系。

    • 在 Soot 中,构建调用图(Call graph)是分析过程的一个关键步骤。一旦程序被转换成 Jimple 形式,接下来就是建立调用图。在建立调用图的过程中,有几种不同的方法可供选择,其中包括 CHA、SPARK 和 Paddle。可以通过设置不同的分析阶段(phase)来选择所需的方法:

      Options.v().setPhaseOption("cg.spark", "on");
      

输出配置

  • set_output_format(): 设置输出格式,决定分析结果的表现形式。

    Options.v().set_output_format(Options.output_format_jimple);
    

处理配置

  • setPhaseOption(): 设置特定分析阶段的选项,例如调用图的构建、数据流分析等。

    Options.v().setPhaseOption("cg", "verbose:true");
    

输入特性的配置

  • set_no_bodies_for_excluded(): 设置是否对排除的方法忽略其主体。

    Options.v().set_no_bodies_for_excluded(true);
    
  • add_include(): 添加要包含的类或方法。

    Options.v().add_include("your.package.*");
    
  • add_exclude(): 添加要排除的类或方法。

    Options.v().add_exclude("your.package.ExcludedClass");
    

输出特性的配置

  • set_output_format(): 设置输出格式,决定分析结果的表现形式。

    Options.v().set_output_format(Options.output_format_jimple);
    

Soot生成图

函数调用图CG

一个CG是表示**整个程序中不同方法(函数)**之间调用关系的图,每个函数被表示为图中的一个节点,而函数之间的调用关系则用有向边表示。例如方法foo()调用了方法bar(),则CG中应有一条从foo()bar()的有向边。
在这里插入图片描述

使用Spark(Soot指针分析研究工具包)并打开on-fly-cg选项以使构建的调用图更精确

Call Graph的结构

在这里插入图片描述

Call graph对象里包含了所有的Edges的集合,同时也包含了了几个关键Map

  1. src Map
  2. targetMap
  3. unitMap

这些Map的Key以SootMethod,unit 而value是Edge,为了更快的找到SootMethod或者Unit对应的Edge

生成dot文件

想要获取dot文件,可以像下面一样迭代调用图并以dot格式写出内容,如下所示。

private static void visit(CallGraph cg, SootMethod method) {
  String identifier = method.getSignature();
  visited.put(method.getSignature(), true);
  dot.drawNode(identifier);
  // iterate over unvisited parents
  Iterator<MethodOrMethodContext> ptargets = new Targets(cg.edgesInto(method));
  if (ptargets != null) {
    while (ptargets.hasNext()) {
        SootMethod parent = (SootMethod) ptargets.next();
        if (!visited.containsKey(parent.getSignature())) visit(cg, parent);
    }
  }
    // iterate over unvisited children
    Iterator<MethodOrMethodContext> ctargets = new Targets(cg.edgesOutOf(method));
  if (ctargets != null) {
    while (ctargets.hasNext()) {
       SootMethod child = (SootMethod) ctargets.next();
       dot.drawEdge(identifier, child.getSignature());
       System.out.println(method + " may call " + child);
       if (!visited.containsKey(child.getSignature())) visit(cg, child);
    }
  }
}

控制流图CFG

一个CFG是表示一个方法内的程序执行流的图,它由一系列基本块(basic block)组成,其中每个基本块是一组按顺序执行的语句。控制流图中的节点通常代表基本块,而边则表示程序执行的控制流转移,例如条件语句、循环或函数调用等。例如语句A执行后的下一条语句是B,则CFG中应有一条从A到B的有向边。

示例代码

//Triangle.java

public class Triangle {

    private double num = 5.0;

    public double cal(int num, String type){

        double temp=0;

        if(type == "sum"){

            for(int i = 0; i <= num; i++){

                temp =temp + i;
            
            }
        }
        else if(type == "average"){

            for(int i = 0; i <= num; i++){

                temp = temp + i;

            }

            temp = temp / (num -1);

        }else{

            System.out.println("Please enter the right type(sum or average)");

        }

        return temp;

    }

}
javac Triangle.java
java -cp sootclasses-trunk-jar-with-dependencies.jar soot.tools.CFGViewer -pp -cp . Triangle -d .

查看目录生成了两个dot文件,一个是类的方法,另一个是类的构造方法

ls
'Triangle double cal(int,java.lang.String).dot'
'Triangle void <init>().dot'

我们可以可以使用 DOT 语言的可视化工具(如Graphviz)将这些文件转换成图形化的控制流图,以便更直观地理解方法和类的结构和执行流程。

Ubuntu下

sudo apt install graphviz

Mac下

brew install graphviz

在终端输入以下命令生成png图片

dot -Tpng -o cal.png Triangle\ double\ cal\(int,java.lang.String\).dot
dot -Tpng -o init.png Triangle\ void\ \<init\>\(\).dot

下面是cal方法

在这里插入图片描述

初始化过程

在这里插入图片描述

IDEA项目集成

快速创建一个maven项目
在这里插入图片描述

在项目的 pom.xml 文件中添加 Soot 依赖

<dependencies>
    <dependency>
        <groupId>org.soot-oss</groupId>
        <artifactId>soot</artifactId>
        <version>4.4.1</version>
    </dependency>
</dependencies>

src/main/java创建一个简单的 Java 源文件

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, world!");
    }
}

有待完善

其他知识

污点分析

污点分析(taint analysis):是一项跟踪并分析污点信息在程序中流动的技术,该技术通过对系统中的敏感数据进行标记, 继而跟踪标记数据在程序中的传播, 检测系统安全问题。

它可以抽象为一个三元组<source, sink, sanitizers>形式:

source即为污染源,代表程序的敏感数据或引入的不受信任的数据;

sink为污点汇聚点,代表直接产生安全敏感操作,或向外发送隐私数据;

sanitizer即无害化处理,表示污染源数据通过一些操作解除了其危害性,如对发送出去的数据做了加密处理或对引入的数据做了安全校验。

参考链接

1.Soot使用笔记

2.https://www.zhihu.com/question/35388795/answer/146808522

3.Introduction: Soot as a command line tool · soot-oss/soot Wiki (github.com)

4.Soot(一)——安装与基本使用_soot 工具-CSDN博客

5.soot-tutorial - ZhechongHuang’s Homepage (cudraniatrec.github.io)

6.软件分析技术 (xiongyingfei.github.io)

7.Soot 静态分析框架(二)Soot的核心_soot框架-CSDN博客

8.【程序分析】函数调用图 | 控制流图 | 过程间控制流图 | 数据流图 | 值流图-CSDN博客

9.soot基础 – 常用参数配置_soot中如何将类声明为library class-CSDN博客

10.https://blog.csdn.net/TheSnowBoy_2/article/details/53436042

11.[https://people.cs.vt.edu/ryder/515/f05/lectures/Sootlecture-Weilei.pdf](https://people.cs.vt.edu/ryder/515/f05/lectures/Sootlecture-Weilei.pdf#:~:text=Phase in Soot In SOOT%2C each phase is,collection of transformers%2C each corresponding to a subphase.)

12.https://mp.weixin.qq.com/s/vc8ZDkrSxUV237C020E5Ag

13.https://ranger-nju.gitbook.io/static-program-analysis-book/

14.https://blog.csdn.net/raintungli/article/details/101446434

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/503068.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

vue3如何二次封装el-upload组件进行图片上传及删除

实现功能&#xff1a; 1、封装el-upload组件&#xff0c;父组件可以控制图片上传框的宽高 2、父组件可以传入提示信息&#xff0c;利用具名插槽 3、上传前的校验 4、实现删除功能 不同配置下的效果&#xff1a; 下边案例是图片上传的时候没有掉接口&#xff0c;在整体提交的时…

省级-能源结构数据(电力消费水平)(2000-2022年)

能源结构指能源总生产量或总消费量中各类一次能源、二次能源的构成及其比例关系。它是能源系统工程研究的重要内容&#xff0c;直接影响着国民经济各部门的最终用能方式&#xff0c;并反映了人民的生活水平。能源结构主要由生产结构和消费结构组成。 本数据通过电力消费水平来…

HarmonyOS 应用开发之FA模型与Stage模型应用组件

应用配置文件概述&#xff08;FA模型&#xff09; 每个应用项目必须在项目的代码目录下加入配置文件&#xff0c;这些配置文件会向编译工具、操作系统和应用市场提供描述应用的基本信息。 应用配置文件需申明以下内容&#xff1a; 应用的软件Bundle名称&#xff0c;应用的开发…

Delphi 12 安卓 部署文件,不支持中文文件名

procedure TForm3.Button1Click(Sender: TObject); var sFileName:string; begin sFileName:TPath.Combine(TPath.GetDocumentsPath,禁止吸烟.wav); showmessage(sFileName); MediaPlayer1.Stop ; MediaPlayer1.FileName: sFileName; MediaPlayer1.Play; end;

Python之Opencv教程(3):人脸识别

1、人脸识别代码 直接上代码&#xff1a; import cv2# 加载训练数据集文件 recogizer cv2.face.LBPHFaceRecognizer_create()recogizer.read(trainer/trainer.yml)# 准备识别的图片 img cv2.imread(images/lisa.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)face_dete…

Linux文件系统权限

文件的一般权限 每个文件针对每类访问者定义了三种主要权限&#xff1a; r&#xff1a;read 读 w&#xff1a;write 写 x&#xff1a;execute 执行 所属者/组/其他用户权限的字符表示二进制表示八进制表示---0000--x0011-w-0102-wx0113r--1004r-x1015rw-1106rwx1117 文件/目…

企业培训系统功能介绍

在当今知识经济时代&#xff0c;企业的竞争力在很大程度上取决于员工的专业能力和综合素质。为了适应不断变化的市场需求和技术进步&#xff0c;企业需要对员工进行持续有效的培训。一个高效的企业培训系统对企业人才培训至关重要。以下介绍一下企业培训系统的主要功能&#xf…

《操作系统导论》第15章读书笔记:机制:地址转换(address translation)

《操作系统导论》第15章读书笔记&#xff1a;机制&#xff1a;地址转换&#xff08;address translation&#xff09; —— 杭州 2024-03-30 夜 文章目录 《操作系统导论》第15章读书笔记&#xff1a;机制&#xff1a;地址转换&#xff08;address translation&#xff09;1.前…

c语言:用do-while输出前40项的斐波那契数值

求Fibonacci数列的前40个元素。该数列的特点是第1、2两个数为1、1。从第3个数开始&#xff0c;每数是其前两个数之和。 分析&#xff1a;从题意可以用如下等式来表示斐波那契数列&#xff1a; 1&#xff0c; 1&#xff0c; 2&#xff0c; 3&#xff0c; 5&#xff0c; 8&#x…

FA模型切换Stage模型组件切换之PageAbility切换

FA模型中PageAbility对应Stage模型中的UIAbility&#xff0c;PageAbility切换为UIAbility的方法如下。 在Stage应用中 创建UIAbility。 将FA应用中PageAbility的代码迁移到新创建的UIAbility中。 FA应用中PageAbility和Stage应用中的UIAbility生命周期基本一致&#xff0c;两者…

面试八股——redis——集群

0. redis集群的方案 1.主从复制&#xff08;高并发读&#xff09; 一个主节点负责写操作&#xff08;增删改&#xff09;&#xff0c;多个从节点负责查操作。 主从复制是让主节点修改数据之后&#xff0c;将对应数据同步到从节点中。 2.哨兵模式&#xff08;实现高可用&#x…

输出单链表倒数第K个结点值

方法一&#xff1a; 两次遍历链表。第一次遍历&#xff0c;计算链表长度&#xff0c;然后计算链表倒数第m个结点的正数位置k&#xff0c;判断位置是否合法&#xff0c;如果不合法&#xff0c;输出NOT FOUND&#xff0c;否则&#xff0c;进行第二次遍历链表&#xff0c;查找链表…

Cocos Creator 常见问题记录

目录 问题1、精灵图九宫格&#xff0c;角度不拉伸 问题2、BlockInputEvents 防止透屏 问题1、精灵图九宫格&#xff0c;角度不拉伸 点击编辑&#xff0c;拖拽到可变区域 问题2、BlockInputEvents 防止透屏

NFT-前端开发(一)

使用 在我们想要保存项目的目录下打开终端运行npx create-react-app test2命令初始化&#xff0c;test2是我们的项目名字&#xff0c;可以自己去更改。 初始化完成后&#xff0c;我们目录下就会多出一个test2文件夹 &#xff0c;然后我们在vscode中打开该文件夹 然后我们打开j…

Linux 基于chrony进行时钟同步方案验证

Linux 基于chrony进行时钟同步方案验证 1. 背景介绍2. 验证过程2.1 追踪配置2.2 追平记录2.2 追平时间换算 3. 疑问和思考3.1 如何统计追踪1s需要花费多长时间&#xff1f; 4. 参考文档 chrony是一个Linux系统中用于时钟同步的工具。它使用NTP&#xff08;网络时间协议&#xf…

HarmonyOS 应用开发之模型切换

本文介绍如何将一个FA模型开发的声明式范式应用切换到Stage模型&#xff0c;您需要完成如下动作&#xff1a; 工程切换&#xff1a;新建一个Stage模型的应用工程。 配置文件切换&#xff1a;config.json切换为app.json5和module.json5。 组件切换&#xff1a;PageAbility/Serv…

9.2-源码分析:Dubbo Remoting 层 Buffer 缓冲区

Buffer 是一种字节容器&#xff0c;在 Netty 等 NIO 框架中都有类似的设计&#xff0c;例如&#xff0c;Java NIO 中的ByteBuffer、Netty4 中的 ByteBuf。Dubbo 抽象出了 ChannelBuffer 接口对底层 NIO 框架中的 Buffer 设计进行统一&#xff0c;其子类如下图所示&#xff1a; …

私域流量:如何给微信客户贴上精准标签?

私域流量在现代营销中变得越来越重要&#xff0c;而给微信客户贴上精准标签是私域流量管理的一个关键环节。今天就给大家分享三个给客户贴上精准标签的小技巧&#xff0c;一起来看看吧&#xff01; 首先&#xff0c;我们可以通过设定静态标签来给微信客户贴上精准标签。这意味…

初识C++(四)深入了解拷贝构造函数

1.拷贝构造函数 拷贝构造函数是一种特殊的构造函数&#xff0c;在对象需要以同一类的另一个对象为模板进行初始化时被调用。它的主要用途是初始化一个对象&#xff0c;使其成为另一个对象的副本 class Date { public:Date(int year 1, int month 1, int day 1){_year yea…

JAVAEE之网络原理

1.IP地址 IP地址主要用于标识网络主机、其他网络设备&#xff08;如路由器&#xff09;的网络地址。简单说&#xff0c;IP地址用于定位主机的网络地址。 格式 IP地址是一个32位的二进制数&#xff0c;通常被分割为4个“8位二进制数”&#xff08;也就是4个字节&#xff09;&…