dalvik字节码问答

Java层逆向分析-Dalvik字节码 往期推荐 修改资源去广告 修改包名实现分身 篡改Apk名称、图标 AndroidKiller介绍与使用 1.1 Dalvik寄存器 Dalvik中的寄存器都是32位大小,支持所有类型。对于小于或等于32位类型,使用一个寄存器就可以了,对64位(long和double)类型,需要使用两个相邻的寄存器来存储。 1.2 寄存器的命名法 寄存器就相当于一个个容器,在里面储存各类型值,为了很好地知道存储位置,就需要对寄存器进行命名,就像我们每个人都有一个属于自己的名字一样。 寄存器的命名法有两种:V命名法和P命名 阅读详情

参考资料

https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html

https://source.android.com/devices/tech/dalvik/instruction-formats.html

http://www.milk.com/kodebase/dalvik-docs-mirror/docs/dalvik-bytecode.html



什么是Dalvik字节码?

dalvik 的虚拟机指令代码。dalvik字节码之于dalvik VM,就像可执行程序的机器码之于CPU一样。

dalvik 字节码是一种基于寄存器的虚拟机指令系统。它的含义是,每一个可执行函数都有一个Frame,Frame的大小是固定,包括固定数目大小的虚拟寄存器。
dalvik VM 会为这个函数的参数、本地变量、临时变量都分配一个虚拟寄存器。所有的运算都是在寄存器之间完成的。
当然,我说的,变量与寄存器不是一对一的关系,而是多对一的关系。因为变量的生命周期并不完全一样,有些变量之间生命周期不重合,就会出现共用一个寄存器的情况。

在下面的描述中,我们用v0, v1, ... vN 表示寄存器,一共有N+1寄存器。

除了常用的寄存器外,还有两个隐含的寄存器:result和exception。当函数返回时,返回值被写入到result寄存器,而抛出异常时,异常对象会被放入exception寄存器。
在dalvik的实现中,这两个寄存器都是放在Thread对象中的。一个Thread对象代表一个java线程,一个java线程同时只需要一个result和exception寄存器。

dalvik字节码指令分成几大部分:
  • const 指令:负责将常量值放入到虚拟寄存器中;
  • mov指令:在虚拟寄存器之间拷贝数据;
  • 比较指令:比较两个寄存器的值,有部分指令是直接与0比较的;
  • 分支指令:包括if系列指令如if-eq, if-ge等等,以及switch指令,goto指令。switch指令作为一个单独指令存在的;
  • invoke指令:函数调用指令,分成invoke-direct, invoke-static, invoke-native, invoke-virtual和invoke-interface几大系列;
  • field操作指令:包括put/get,针对静态域和非静态域;
  • class相关的指令:比如new-instance, instance-of,checkcast等等
  • 异常处理指令:包括throw, move-exception这样的指令;
  • 算术、位移等指令:不多说了;
  • 其他指令

字节码的格式?

dalvik字节码是以一个字(两字节)为单位,必须是一个字的整数倍。所以,你看到dexdump出的字节指令,其长度都必须x2才能得到其字节长度。

不同指令的字节码长度不一样,相同操作类型的指令长度是一样的,根据指令操作类型,就可以确定指令的长度。比如 mov v1, v2,指令长度是1字。

字节码在dex文件中存储时,都是小端的。因此,第一个字节必须是操作类型(操作符),是一个0~255范围内的一个数字,根据操作符,我们可以确定后续有多少个字以及表示什么意思。


每个操作符都有一个格式(format),根据格式,我们可以知道该指令的长度,参数有哪些、每个参数的长度、参数是如何存储的,这些重要的信息。不同的指令可以有相同的格式,因此,dalvik将他们分类,并给每个格式以不同的描述。每种格式被称为Format ID。

下表详细说明了dalvik字节码的格式(来自https://source.android.com/devices/tech/dalvik/instruction-formats.html)。这个表分为4列。其中前3列分别是Format, ID, Syntax。

Format列

"op" 表示操作符,8位1字节无符号整数。大写字母"A","B","C"等表示4位数;"Ø" 表示空,占4位,必须为0;
每个部分用'|'分割,表示一个逻辑上独立的部分。
排列最小单位是字(两字节),中间不分割。例如B|A|op,这个长度是1个字(两字节),但是包括3部分,op操作符和 A, B两个参数; 而AA|opBBBB 包含两个字(4字节),其中"AA|op"是第一个字,而"BBBB"则是在第二字。

因为在实际存储时,是以小端方式,而在描述时,则以大端方式。阅读时要注意。比如 ØØ|op , 在实际存储时,“op”在第0字节,而“ØØ”在第1字节。在如
AA|opBBBB 则是 op在第0字节,AA在第1字节,BBBB 占用第2,3字节。

ID列

ID由3部分组成: <长度><寄存器个数><类型描述>。 例如 "10x"  "1"表示指令长度为1, "0"表示用到0个寄存器,"x"表示类型描述; “21t“ 表示指令长度为2,用到1个寄存器,并需要跳转("t"表示分支跳转)。
最后类型描述部分各个字符表示的意思如下表:
Mnemonic Bit Sizes Meaning
b 8 有符号1字节立即数
c 16, 32 常量池索引
f 16 interface相关的常量
h 16 16位有符号立即数,表示32/64位中的最高16位
i 32 32位有符号立即数,或者32位的浮点数
l 64 64位有符号立即数,或者双精度浮点数
m 16 method的常量索引值
n 4 4位有符号立即数
s 16 16位有符号立即数
t 8, 16, 32 分支目标偏移值
x 0 无更多附加数

Syntax列

字节码可以反汇编,形成类似汇编代码的助记符形式,方便人类阅读。一般形式是 op Arg1[, Arg2, [,Arg3 ...]] 这种形式。
你在Format列看到的字符会反映到Syntax列。例如,如果在Foramt列看到了"AA",那么在Syntax列看到的“AA”所指的是同一个概念。

在Syntax,会给它加不同前缀,形成不同的格式,代表不同含义:
  • "vX": 前缀"v"表示这是一个虚拟寄存器,"X"对应Foramt列中的参数,"X"的值就是虚拟寄存器的索引;
  • "#+X":前缀“#+”表示一个有符号的立即数;
  • "+X": 前缀“+"表示一个相对偏移地址;
  • "kind@X" :前缀"kind" 表示类型(只能取"type","meth", "field", "string",分别表示解析表中class,method,feild,string的索引),“X”是索引值
  • 另外[X=N]这样的格式出现在某条指令之前,表示X满足N时有效。例如[A=2] 表示A的值为2时。


Format ID Syntax Notable Opcodes Covered
N/A 00x N/A 针对一些不使用的指令
ØØ|op 10x op  
B|A|op 12x op vA, vB  
11n op vA, #+B  
AA|op 11x op vAA  
10t op +AA goto
ØØ|opAAAA 20t op +AAAA goto/16
AA|opBBBB 20bc op AA, kind@BBBB 目前没有该格式的指令
AA|opBBBB 22x op vAA, vBBBB  
21t op vAA, +BBBB  
21s op vAA, #+BBBB  
21h op vAA, #+BBBB0000
op vAA, #+BBBB000000000000
 
21c op vAA, type@BBBB
op vAA, field@BBBB
op vAA, string@BBBB
check-cast
const-class
const-string
AA|opCC|BB 23x op vAA, vBB, vCC  
22b op vAA, vBB, #+CC  
B|A|opCCCC 22t op vA, vB, +CCCC  
22s op vA, vB, #+CCCC  
22c op vA, vB, type@CCCC
op vA, vB, field@CCCC
instance-of
22cs op vA, vB, fieldoff@CCCC suggested format for statically linked field access instructions of format 22c
ØØ|opAAAAloAAAAhi 30t op +AAAAAAAA goto/32
ØØ|opAAAA BBBB 32x op vAAAA, vBBBB  
AA|opBBBBloBBBBhi 31i op vAA, #+BBBBBBBB  
31t op vAA, +BBBBBBBB  
31c op vAA, string@BBBBBBBB const-string/jumbo
A|G|opBBBB F|E|D|C 35c [A=5] op {vC, vD, vE, vF, vG}, meth@BBBB
[A=5] op {vC, vD, vE, vF, vG}, type@BBBB
[A=4] op {vC, vD, vE, vF}, kind@BBBB
[A=3] op {vC, vD, vE}, kind@BBBB
[A=2] op {vC, vD}, kind@BBBB
[A=1] op {vC}, kind@BBBB
[A=0] op {}, kind@BBBB

The unusual choice in lettering here reflects a desire to make the count and the reference index have the same label as in format 3rc.

 
35ms [A=5] op {vC, vD, vE, vF, vG}, vtaboff@BBBB
[A=4] op {vC, vD, vE, vF}, vtaboff@BBBB
[A=3] op {vC, vD, vE}, vtaboff@BBBB
[A=2] op {vC, vD}, vtaboff@BBBB
[A=1] op {vC}, vtaboff@BBBB

The unusual choice in lettering here reflects a desire to make the count and the reference index have the same label as in format 3rms.

suggested format for statically linked invoke-virtual and invoke-super instructions of format 35c
35mi [A=5] op {vC, vD, vE, vF, vG}, inline@BBBB
[A=4] op {vC, vD, vE, vF}, inline@BBBB
[A=3] op {vC, vD, vE}, inline@BBBB
[A=2] op {vC, vD}, inline@BBBB
[A=1] op {vC}, inline@BBBB

The unusual choice in lettering here reflects a desire to make the count and the reference index have the same label as in format 3rmi.

suggested format for inline linked invoke-staticand invoke-virtual instructions of format 35c
AA|opBBBB CCCC 3rc op {vCCCC .. vNNNN}, meth@BBBB
op {vCCCC .. vNNNN}, type@BBBB

where NNNN = CCCC+AA-1, that is Adetermines the count 0..255, and Cdetermines the first register

 
3rms op {vCCCC .. vNNNN}, vtaboff@BBBB

where NNNN = CCCC+AA-1, that is Adetermines the count 0..255, and Cdetermines the first register

suggested format for statically linked invoke-virtual and invoke-super instructions of format3rc
3rmi op {vCCCC .. vNNNN}, inline@BBBB

where NNNN = CCCC+AA-1, that is Adetermines the count 0..255, and Cdetermines the first register

suggested format for inline linked invoke-staticand invoke-virtual instructions of format 3rc
AA|opBBBBloBBBB BBBB BBBBhi 51l op vAA, #+BBBBBBBBBBBBBBBB const-wide


常量池与常量解析表是什么?

java的很多操作,都需要类、方法、域和字符串。这些反映在字节码中,都是以字符串的形式存在的。

所有的类名、方法名、域名、域和方法的签名已经常量字符串全部存储在常量池中。常量池是一个字符串池,包含所有字符串。

同时为了访问这些常量字符串,必须通过一些解析表来进行映射。这些解析表包括type表--保存class名字的索引;field表,包括field所属类的解析表索引、field名字的索引、field签名的索引;method表,包括method所属类的索引、method名字的索引、签名的列表;string表保存的则是常量池中的索引。

用下面的图表示的更加清楚:



图中的每一项在Dex文件中都是以表的方式存在的。

这些表对程序的运行至关重要。如果指令中含有“string@XXXX”这种格式的参数时,那么就要从StringId表中取出一个StringId项,然后通过其offset,从常量池中得到对应的字符串;对于"method@XXXX"这种格式,就是从MethodId的表中得到一个MethodId项,从而读取到各项信息。


但是要注意一点,上图描绘的是dex文件中存在的关系,不是运行时的关系。当我们执行一条指令,比如 "const/string vA, string@BBBB", 寄存器vA中保存的不是“BBBB”的值,也不是从常量池中得到的字符串地址,而是一个和该字符串对应的java.lang.String对象;同理,const/class vA, class@BBBB这样的指令,vA保存的是BBBB所描述的class的java.lang.Class对象。但是 invoke指令和iput,iget等field指令,在运行时并不取得对应的java.reflect.Method和java.reflect.Field对象,而是Dalvik内部的Method和Field对象。这些内部对象只能在dalvik内部使用,不会反映在java层的代码中,java代码也无法使用。

之所以出现这些差别,是因为string,class这种对象,是必须能够让java层代码访问的,而field和method对java代码是透明的。


Dalvik为了应对这种差别,都会做一个与这些表大小一样,索引一致的cache表,这个cache表内放置了已经解析好的对象,需要的时候直接从cache中取,不需要这样来回查找了。


当然,这些表内防止的是所有java代码能够访问到的类、field、method和字符串。比如,你要访问一个android.content.Content类及其方法,那么他们的名字就会放在表内。


和类、对象操作相关的指令有哪些?

对于java语言中一些特有的表达式,它们对应的指令关系做一个深入的探讨。

new 语句对应的指令


new语句对应的是new-instance指令和一个invoke-direct指令。new-instance负责创建一个新的对象,而invoke-direct指令则是用于调用构造函数。

不是所有的new语句都会产生invoke-direct指令,如果new的时候调用的构造函数没有参数,而且被new的类也没有定义不带参数的构造函数时,它就不会产生invoke-direct指令了。


instance of语句对应的指令


对应instance-of指令


check cast指令



这种情况只发生在down cast的时候。如果你将一个父类类型转换为子类类型,就会默认生成一条check-cast指令。


<Class>.class语句



当我们要取一个类的class时候,就会产生一条 const/class指令。例如


const-class v1, Ljava/lang/Integer;


Field访问指令

Field访问指令包括get和put。Field分为static和instance两种类型。因此,对static的访问,都是sget/sput开头,而对instance类型的访问,都是iget/iput。
根据数据类型的不同,以iget为例,有iget, iget-object, iget-wide, iget-boolean, iget-char, iget-short, iget-byte。虽然有这么多种类型,但是在dalvik的实现中,其实只有iget和iget-wide两种区别。除了iget-wide之外的其他iget指令,都是以4字节为单位,占用一个虚拟寄存器;而iget-wide则是8字节为单位,占用两个寄存器。


例如 iget vA, vB, field@CCCC。表示将vA内容放到vB的CCCCfield中,即便是一个char类型的,也是4字节操作,即vB的数据是4字节长度,Field在object中占用的空间也是4字节长度;而对于iget-wide,则是使用连着的两个寄存器存储数据。按照小端原则。


因为这个缘故,在做odex优化时,iget指令被优化成iget-quick/iget-object-quick和iget-wide-quick。因为object比较特殊,所以将iget-quick分化出一个iget-object-quick来。quick优化后面章节有专门描述。


如何调用和返回函数

在dalvik中,调用函数是通过invoke指令来的。invoke指令按照参数多少,可以分为invoke和invoke/range两种类型。dalvik所有的参数都是通过虚拟寄存器传递的。当参数少时,直接用invoke,这种情况下,将所有用来传递参数的虚拟寄存器都放到指令中取;而参数较多时,就需要用到range方式,这种方式下,invoke指令中包含了参数个数和第一个参数用到的寄存器的标号。

按照调用函数的类型不同,分为:
  • invoke-direct
  • invoke-virtual
  • invoke-super
  • invoke-interface
  • invoke-static
其中,direct类的函数,包括除了static外的非虚函数,例如构造函数、prive函数等。static函数是按照direct函数对待的。那么,native函数是否也是direct函数呢?其实,这是按照实现方法来区分一个函数是native还是非native的,native方法也可以是 virtual,甚至是可以实现interface的一个方法,所以,native方法的调用,可能是上面任意一种指令。对native方法的调用与普通方法是一样的,所不同的是调用时,函数入口所有区别而已; 那么final方法是否也是direct呢?实际上final方法只是为了防止进一步继承,而不能确定它一定就是direct的,即不可能存在继承和覆盖的情况。所以fianl方法是按照virtual方法来对待的。final方法与普通方法的不同不是体现在字节码上,而是体现在类和函数的定义上,dalvik虚拟机会在加载类的时候进行验证(verify),其中一项是检查当前类的父类是否是final的,或者是否包含final方法。



invoke的指令编码是:
6e..72 35c invoke-kind {vC, vD, vE, vF, vG}, meth@BBBB
6e: invoke-virtual
6f: invoke-super
70: invoke-direct
71: invoke-static
72: invoke-interface
A: argument word count (4 bits)
B: method reference index (16 bits)
C..G: argument registers (4 bits each)
invoke的返回值是放在result寄存器中的。要取得结果需要用move-result vN的方法来取得。

74..78 3rc invoke-kind/range {vCCCC .. vNNNN}, meth@BBBB
74: invoke-virtual/range
75: invoke-super/range
76: invoke-direct/range
77: invoke-static/range
78: invoke-interface/range
A: argument word count (8 bits)
B: method reference index (16 bits)
C: first argument register (16 bits)
N = A + C - 1
Call the indicated method. See firstinvoke-kind description above for details, caveats, and suggestions.

在参数传递上,对于非static函数,第一个参数是this对象。



switch相关指令是怎么回事?

switch有两种指令类型:packed-switch和spare-switch。这些是指令和数据放在一起的,case分支信息是放在switch指令指定的payload数据中。
指令的数据是:
2b 31t packed-switch vAA, +BBBBBBBB (with supplemental data as specified below in "packed-switch-payload Format") A: register to test
B: signed "branch" offset to table data pseudo-instruction (32 bits)
B所指出的数据就是 switch的payload数据,记录了case分支信息。如果没有对应的case分支,就会走到下条指令来执行。
下条指令是default分支的数据
2c 31t sparse-switch vAA, +BBBBBBBB (with supplemental data as specified below in "sparse-switch-payload Format") A: register to test
B: signed "branch" offset to table data pseudo-instruction (32 bits)
同上
那么payload的数据格式是什么呢?
Typepacked-switchspare-switch
ushort 0x01000x0200
ushortsizesize
 int first_key
int[] targets
int[] keys
int[] targets

packed-switch是一种压缩的switch,它给出一个first_key,用vAA-first_key得到的索引,然后从targets中取得偏移,这个偏移是相对于switch的指令的。用伪代码就是
int idx = value_of(vAA) - first_key;
if (idx >= size)
  target_pc = current_pc + PACKED_SWITCH_SIZE;
else
  target_pc = current_pc + targets[idx];
注意一点: targets中的数据偏移是以16位(2字节)为偏移单位的。

那么,同理,spare-switch有keys和targets两个数组,需要先和keys比较,得到对应的索引,然后再找targets中对应的偏移。用伪代码表示就是
int idx = find_key(keys, value_of(vAA));
if (idx >= size)
  target_pc = current_pc + PACKED_SWITCH_SIZE;
else
  target_pc = current_pc + targets[idx];


quick类型的指令有那些,起到什么作用?

dalvik有一项优化,即将dex文件转换为odex文件。那么,odex与dex有什么区别呢?在文件结构上,odex文件会创建一个hash表,以类名为key,帮助系统快速找到class,从而快速加载;在字节码上,引入了一些quick指令,这些quick指令,能够加快执行速度。

quick指令是针对iget/iput,以及invoke-virtual的。iget/iput指令总共被分成3种:iget/iput-quick, iget/iput-object-quick, iget/iput-wide-quick。它们主要是类型与长度不同(见上一节),实际原理都是一样的。iget/iput的原理,是将原iget/iput中记录field在解析表中的索引,直接换成了这个field在对象中的偏移。虚拟机只要用this对象+偏移就可以获取到field在object对象中的位置,从而进行读写。

另外一个invoke-virtual-quick和invoke-virtual-quick/range,同样的,它将invoke-virtual指令内包含的method在解析表内的索引,换成了对应的虚函数在对象的虚函数表中的偏移,通过 this->class->vtable + offset的方式取得method指针,然后进行调用。

除了这些外,还有一个execute-inline指令。这个指令其实是将很多非常基础的类的函数换成dalvik内部指针。比如String类的很多函数,如charAt等,换成dalvik内部用c语言编写的 java_lang_String_charAt函数,在运行时,直接调用内部函数,速度会提升很多。execute-inline会将对应的内部函数的索引放在指令里面。


字节码的程序结构如何?

在dex文件内部,一个方法的代码数据由2大部分组成:代码数据和Try-catch数据块。代码数据包括:代码和payload数据。每个块之间都必须4字节对齐

用下面的表表示:

代码数据
 align pending (如果不是4字节对齐,增加一个0x0000的pending)
payload数据(switch, fill-array-data指令的数据,如果有)
 align pending (如果不是4字节对齐,增加一个0x0000的pending)
 try-catch数据数组

代码数据部分都是可执行的代码。与我们通常看到的代码不同,davlik字节码使用了大量的goto指令,用于实现各种分支和循环。


try-catch数据结构是什么?

try-catch数据结构,是放在代码的后面。在dalvik用CodeItem这个结构来保存code数据,其中的insns_size_in_code_units_是以2字节为单位保存从Method代码开始位置到TryItem数组的偏移。

try catch是用TryItem数组保存的,一个TryItem的结构如下

  struct TryItem {
    uint32_t start_addr_;
    uint16_t insn_count_;
    uint16_t handler_off_;

   private:
    DISALLOW_COPY_AND_ASSIGN(TryItem);
  };
start_addr_是try块开始位置,相对于代码开始位置的偏移

insn_count_记录try块涉及的代码指令数;

handler_off_ 是catch块的数据,这个偏移是相对与代码开始位置。它的数据是经过LEB128编码的,数据可以被解析成一个CatchHandlerItem结构

CatchHandlerItem结构定义如下

    struct CatchHandlerItem {
      uint16_t type_idx_;  // type index of the caught exception type
      uint32_t address_;  // handler address
    } handler_;
type_idx_ 是exception type的索引,同样是type解析表的索引;

address_ 是处理代码的入口。

HandleItem可能不止一个,会有多个。


TryItem是一个数组,数组的长度可以0,1,... N个。当异常发出时,得到异常发出位置的dex pc,用这个dex pc去匹配TryItem中start_addr_和insn_count_,看dex pc落在哪个TryItem的区间内。如果找到了TryItem块,就找它下面的CatchHandleItem,去匹配抛出的exception类型,与CatchHandlerItem定义的类型是否一致,如果一致就找到了对应的处理,如果不一致,则继续查找。

如果本函数内已经没有可以处理的Try-catch了,就向上找调用者函数,依次取查找。


当然,我们知道,java的异常,是 try-catch-finally结构,所以,如果有 finally的话,它是最后一个CatchHandleItem了。

一个TryItem的CatchHandlerItem信息结构,可以用下表来描述(数据都是经过LEB128加密的)

剩余handler_catch 个数(int型)
catch的exception type index
catch块的addr偏移
剩余handler_catch 个数(int型)
......
 剩余handler_catch (<0 表示还有一个final块, 0表示没有了)
 finally块的addr偏移(如果<0)


(因为个人水平限制和时间限制,内容很乱,没有很好组织,请读者原谅,我力图将dalvik的比较全面的知识告诉大家,如果有什么不正确的,请指出。谢谢)
深入解析Dalvik操作码与Android底层机制 Dalvik虚拟机(Dalvik Virtual Machine,DVM)是Android平台的核心执行引擎,专为移动设备资源受限的环境而设计。与传统的Java虚拟机(JVM)不同,Dalvik采用基于寄存器的架构,而非JVM的基于栈的架构。这种设计在执行效率和内存占用方面具有优势,尤其适合嵌入式系统。Dalvik虚拟机运行的并非标准的Java字节码(.class文件),而是由Android SDK工具链将多个.class文件转换为。 阅读详情

相关推荐

Dalvik 字节码

dalvik字节码有两种类型,原始类型和引用类型。 该文档出自android 4.0

Dalvik可执行格式和字节码规范

<<Android软件安全权威指南>>笔记 第三章 Dalvik可执行格式与字节码规范 早期Android是Dalvik虚拟机,Android4.4以后,引入ART(Android Runtime)虚拟机 JIT(Just-In-Time)即时编译,AOT(Ahead-Of-Time)预先编译 ART提供对Dalvik的可执行格式与字节码的支持 Dalvik虚拟机 特点 专有DEX(Dalvik Executable)可执行文件格式 常量池32位索引 基于寄存器架构,完整指令系统 D

z1nk的博客 1354

【ENVI初学】查看影像空间分辨率及重采样

例如,如果线段长度为900米,对应的像素数量为30个,那么影像的空间分辨率为30米。通过计算长度与像素数量的比值,即可得到影像的空间分辨率。,在弹出的对话框中,输入重采样后输出文件的xy分辨率大小。在测量结果窗口中,可以看到该线段的长度以及角度。在影像上点击并拖动鼠标,绘制一条直线段。工具,选择要重采样的影像后点击ok。,避免了插值过程中光谱信息的改变。:最近邻插值法通过选择原始影像中。在ENVI中打开影像后右键选择。指示了每个像素的地理跨度(聚合为一个新像素,通常通过。方法一:查看影像的元数据。

weixin_41619112的博客 5571

安卓逆向_3 --- Dalvik 字节码、Smali 详解

Dalvik 字节码、Smali 详解

freeking101的博客 9735

【UGUI】二、Text(TMP) 文本

Text(TMP)、导入中文字库

天行健,君子以自强不息 3367

ModelSim 2020.4破解失败?手把手教你解决‘Unable to checkout viewer license‘报错(附环境变量配置)

本文针对ModelSim 2020.4启动时遇到的‘Unable to checkout viewer license’报错,提供了从理解许可证机制到分步修复的完整指南。文章深入解析了viewer license与图形用户界面(GUI)的关系,并详细说明了通过生成有效许可证文件、精准配置系统环境变量及替换关键文件来解决图形界面启动失败的问题,帮助用户彻底修复并理解其背后的逻辑。

k6l7m8n9的博客 991

Android逆向学习笔记1:Dalvik字节码规范(1)

Zygote创建Java虚拟机并注册JNI方法,创建服务端Socket,启动SystemServer进程。init进程是Android系统中用户空间的第一个进程,进程号为1。mul-int/2addr v0, v1:表示将v0,v1寄存器的值相乘,结果保存到v0寄存器中。常量池采用32位索引值,对类方法名,字段名,常量的寻址快;如果dx命令失败,可能是前面代码中自动加了包名,将其删去就好,或者直接复制上面代码保存。第一条指令是将v3,v4相加,结果存到v0寄存器中,add是加,int表明是整数类型。

weixin_58284606的博客 723

迪文串口屏开发环境搭建全流程:从DGUS Tool到串口联调

串口屏作为嵌入式人机交互的核心组件,通过串口通信实现主控MCU与显示终端的指令交互。其工作原理基于变量地址映射,主控读写VP地址即可驱动屏上控件,兼具低功耗与高稳定性。在工业控制、医疗设备及智能家居面板等场景中,串口屏能显著降低开发门槛与硬件成本。迪文屏凭借DGUS Tool上位机与自定义串口协议,成为主流选择。掌握DGUS Tool版本匹配、串口驱动安装及VP地址规划,是快速搭建开发环境的关键。

weixin_33937499的博客 608

常见的 256 条 Dalvik 字节码指令

Dalvik 字节码指令是 Android 虚拟机的指令集,广泛用于处理 .dex 文件中的代码。下面列出一些常用的 Dalvik 指令,但 Dalvik 指令集有很多操作码,这里仅列出 256 个常用指令及其功能简述。为简明起见,指令按类别分类。这 256 条指令涵盖了 Dalvik 指令集中常用的加载、移动、算术运算、逻辑运算、条件跳转、方法调用等指令类别。

1291

Android 面试题 Handler篇

1.Handler的实现原理。

ViewHandKownHealth的博客 955

YOLO11改进-注意力-引入自调制特征聚合模块SMFA

SMFA(自调制特征聚合模块): SMFA模块用于协同建模局部和非局部信息,它分为两个分支:一个是EASA(Efficient Approximation of Self-Attention,简化的自注意力分支),用于捕获非局部信息;另一个是LDE(Local Detail Estimation,局部细节估计分支),用于捕获局部细节。EASA通过对输入特征进行下采样,然后利用全局特征的方差进行调制,再与原始特征进行聚合,提取非局部结构信息。LDE分支则通过卷积操作提取输入特征中的高频局部信息。

qq_64693987的博客 2208

OSC 第 84 期高手问答 — Android Dex 注入技术

OSCHINA 本期高手问答(7月29日- 8月4日)我们请来了 @jiangsai (江赛)为大家解答关于 Android Dex 注入技术 方面的问题。 江赛,@jiangsai ,听云移动研发总监,拥有10多年研发经验,早期主要从事网络设备协议栈开发,对 Linux Kernel、TCP/IP 协议栈等有一定的研究,从 2010 年开始从事移动设备...

weixin_34342992的博客 124

实战:利用 Agent Harness 自动化 PPT 制作

本文将带你从零开始,用轻量级Agent编排框架,结合主流大模型(GPT-4o、通义千问、文心一言等),搭建一套全自动化的PPT生成工作流:从用户输入自然语言需求,到需求解析、大纲生成、内容填充、数据匹配、排版美化、导出成品,全程不需要手动操作,最快10分钟就能产出符合要求的PPT。我们不会讲晦涩的Agent理论,所有步骤都有可直接运行的代码示例,哪怕你只有基础的Python语法基础,也能跟着一步步做出能用的自动PPT工具。

AI Agent Harness Engineering 569

【Android】Android 概述_问答

ContentAndroid 概述 老师文档里的问题 Android 概述 为什么要了解Android的版本发展? Android的内核是什么,droid是什么? Android系统的五层都是什么?分别是什么功能? Dalvik虚拟机和Java是一回事吗?是不是凡java程序也都可以运行在Dalvik上,或者反之? 最终能够发行的Android应用是什么文件? Android应用的运行方式是什...

博客皆笔记 1万+

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

要跳槽的再等等,先看看这份面经……

7年高级开发程序员差点在第一轮就被刷了。。。

m0_68102173的博客 631

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

android x86还是arm,Android x86是否模拟ARM?(Does Android x86 emulate ARM?)

Android x86是否模拟ARM?(Does Android x86 emulate ARM?)试图找出应用程序在为ARM编译时如何在VMWare上的Android x86上工作。 GUI是否在Qemu上运行? 如果Android x86真的是x86,它们究竟运行得如何?Trying to figure out how apps work on Android x86 in VMWare wh...

weixin_39555415的博客 691

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

7年高级开发程序员差点在第一轮就被刷了。。。

前言 背景是公司需要急招一名高级开发,我记录了一个比较典型小伙伴的面试问答,先看下这位应聘者的简历以及我在面试中提问的技术点(由于篇幅问题,仅抽取技能部分)。 ↑ 简历 在还没开始面试前,简历的问题挺明显的: 从技能部分可以看出中间件描述稍微有点少,且技能描述比较模糊,其次关键性的技术没有描述(第三项),第六项需要猜测,不能清晰感知到他会的中间件是什么只能靠面试中来问(很容易栽倒坑里)。 (偷偷问一句,你们的简历是不是也这样写的?如果是的话可以在文章底部留言哦!) 面试岗位需求:

java_121388的博客 2336

Rivet Actors 仓库文档同步规范:docs-sync 对照表与代码块类型检查实战指南

在 Rivet Actors 这类"引擎 + 多语言 SDK + 前端控制台"耦合紧密的开源仓库中,改动一行引擎配置或一个 ActorError 错误码,往往意味着文档、示例与测试必须同步更新。本文以仓库内的 [.claude/reference/docs-sync.md](https://link.gitcode.com/i/ffcd33cd673d650b142ba6334b1a8b7b) 为

gitblog_01095的博客 373

Dalvik

1.了解dalvik寄存器dalvik中的寄存器都是32位2.寄存器之v命名法与p命名法参数寄存器 P0-Pn局部变量寄存器 V0-Vn3.dex文件反汇编工具 smali.jar\ddx.jar4.类型smali==>JavaV voidZ boolean B byte C charS shortI intJ longF floatD doubleL java类[ 数组5.字段Lpackage/name/ObjectName;->FieldName:Ljava/lang/S

Codeoooo 博客 1619

Dalvik字节码和Smali基本语法

什么是Dalvik字节码 Dalvik是Google专门为Android设计的一个虚拟机。 Dalvik VM是基于寄存器的,而JVM是基于栈的,Dalvik有专属的文件执行格式dex(Dalvik executable),jvm使用的是java字节码。 Dalvik VM你jvm速度更快,占用空间更少。 什么是Smali Smali,baksmali分贝时指安卓系统里的java虚拟机(Dalv...

700

dalvik 字节码解析

  以下为从官网复制的一份,方便不翻墙查看。官方地址:https://source.android.com/devices/tech/dalvik/instruction-formatshttps://source.android.com/devices/tech/dalvik/dalvik-bytecodeDalvik 字节码通用设计·        机器模型和调用规范旨在大致模仿常见的真实架构...

peikm的博客 1988

第一节 简单的一个扫盲

玩过win PE破解的,都知道一个逆向破解,去查看汇编语言,然后插入自己的代码,保持堆栈平衡,修改逻辑代码,然后保存,完成破解过程。安卓里面的这个汇编语言就是smali,修改它来改变原本程序逻辑的过程,称之为 插桩。比起win PE 的难度,smali的插桩比较简单,因为我们修改的是系统架构的jar apk文件,这些文件是不能混淆的,因为三方的是需要这些,因此相对于混淆的apk来说,明文太多,对

程序员入门进阶 1919

深入理解Dalvik字节码指令及Smali文件

今天来介绍有关Davilk虚拟机相关的知识,首先便是介绍我们最关心的Davilk字节码相关知识,进而深入到Android逆向领域.之所以写这篇文章,是因为有姑娘要学习这,再加上网上的许多资料太过零散和片面,

Floating Cat 3万+

Dalvik字节码

总体设计 机器模型和调用约定是近似模仿常见的真实的架构和C风格调用约定。 机器是基于寄存器的,并且框架在被创建的时候是固定大小的。每一框架包含一个特定数量的寄存器(由函数指定)和一些需要执行该函数的附属的数据,例如(但不限制在这些)程序计数器和包含该方法的.dex文件的引用。 当用于位值的时候(例如整数和浮点数),寄存器被认为是32位宽度。相邻的寄存器对被用于64位值。...

weixin_33774883的博客 354
上一篇: 匿名类和final外部变量的实现
下一篇: ART虚拟机参数说明
漂流的代码
博客等级 码龄23年 188粉丝 99原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值