dalvik字节码问答

参考资料

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": 前缀“+"表示一个相对偏移地址;
  • "[email protected]" :前缀"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, [email protected] 目前没有该格式的指令
AA|opBBBB 22x op vAA, vBBBB  
21t op vAA, +BBBB  
21s op vAA, #+BBBB  
21h op vAA, #+BBBB0000

op vAA, #+BBBB000000000000

 
21c op vAA, [email protected]

op vAA, [email protected]

op vAA, [email protected]

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, [email protected]

op vA, vB, [email protected]

instance-of
22cs op vA, vB, [email protected] 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, [email protected] const-string/jumbo
A|G|opBBBB F|E|D|C 35c [A=5op {vC, vD, vE, vF, vG}, [email protected]

[A=5op {vC, vD, vE, vF, vG}, [email protected]

[A=4op {vC, vD, vE, vF}, kind@BBBB

[A=3op {vC, vD, vE}, kind@BBBB

[A=2op {vC, vD}, kind@BBBB

[A=1op {vC}, kind@BBBB

[A=0op {}, 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=5op {vC, vD, vE, vF, vG}, [email protected]

[A=4op {vC, vD, vE, vF}, [email protected]

[A=3op {vC, vD, vE}, [email protected]

[A=2op {vC, vD}, [email protected]

[A=1op {vC}, [email protected]

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=5op {vC, vD, vE, vF, vG}, [email protected]

[A=4op {vC, vD, vE, vF}, [email protected]

[A=3op {vC, vD, vE}, [email protected]

[A=2op {vC, vD}, [email protected]

[A=1op {vC}, [email protected]

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}, [email protected]

op {vCCCC .. vNNNN}, [email protected]

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

 
3rms op {vCCCC .. vNNNN}, [email protected]

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}, [email protected]

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文件中都是以表的方式存在的。

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

但是要注意一点,上图描绘的是dex文件中存在的关系,不是运行时的关系。当我们执行一条指令,比如 "const/string vA, [email protected]", 寄存器vA中保存的不是“BBBB”的值,也不是从常量池中得到的字符串地址,而是一个和该字符串对应的java.lang.String对象;同理,const/class vA, [email protected]这样的指令,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, [email protected]。表示将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}, [email protected]

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}, [email protected]

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的数据格式是什么呢?

Type packed-switch spare-switch
ushort  0x0100 0x0200
ushort size size
  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的比较全面的知识告诉大家,如果有什么不正确的,请指出。谢谢)

时间: 2024-10-09 20:45:58

dalvik字节码问答的相关文章

Android逆向基础----Dalvik字节码

参考此微博,更多详细内容可以到这里查看 http://blog.csdn.net/dd864140130/article/details/52076515 Dalvik字节码 1.寄存器位32位,64位数据(double)用两个相邻的32寄存器表示. 2.两种类型:基本类型和引用类型(对象和数组) 全限定名是什么? 以String为例,其完整名称是Java.lang.String,那么其全限定名就是java/lang/String;,即java.lang.String的"."用&quo

4. 如何解释dalvik字节码

如何解释dalvik字节码 文档: 在Android系统源码目录dalvik\docs有相关指令文档 dalvik-bytecode.html 实战: 来直接实战模拟来理解枯燥的理论 用IDA打开一个dex文件, 设置显示指令 随便找一段代码 注意: 206E 28DE 0050是IDA的显示问题 在文件中的存储顺序是 6E 20 DE 28 50 00 在WinHex搜索一下就知道了 上面截图的指令在文件中的排列顺序如下: 14 05 09 00 09 01 6E 20 DE 28 50 00

Dalvik字节码的类型,方法与字段表示方法

Dalvik字节码有着自己的类型,方法与字段表示方法,这些方法与Dalvik虚拟机指令集一起组成了一条条的Dalvik汇编代码. 1.类型 Dalvik字节码只有两种类型,基本类型与引用类型.Dalvik使用这两种类型来表示Java语言的全部类型,除了对象与数组属于引用类型外,其他的Java类型都是基本类型.BakSmali严格遵守DEX文件格式中的类型描述符定义.类型描述符对照如下表 语法 含义 V void,只用于返回类型 Z boolean B byte S short C char I

APK、Dalvik字节码和smali文件

     这部分涉及的内容比较多,我会尽量从最基础开始说起,但需要读者一定的android开发基础.但注意可能讲解详细得令人作呕,请根据个人理解程度斟酌. APK.Dalvik字节码和smali文件 APK文件 大家都应该知道APK文件其实就是一个MIME为ZIP的压缩包,我们修改ZIP后缀名方式可以看到内部的文件结构,例如修改后缀后用RAR打开鳄鱼小顽皮APK能看到的是(Google Play下载的完整版版本): Where's My Water.zip\ asset\            

安卓逆向学习---初始APK、Dalvik字节码以及Smali

参考链接:https://www.52pojie.cn/thread-395689-1-1.html res目录下资源文件在编译时会自动生成索引文件(R.java ), asset目录下的资源文件无需生成索引. 一般来说:使用C++游戏引擎的资源文件放在asset下 java开发中除音频和视频资源(放在raw或asset下),均放在res下. Dalvik字节码是什么? Dalvik是Google专门为Android设计的一个虚拟机. Dalvik VM是基于寄存器的,而JVM是基于栈的,Dal

Dalvik指令分析(一) 字节码转换为smali代码

有过android应用反编译或者再打包的朋友都有使用过apktool的经验,apktool能将dex文件的 字节码转换为smali代码,这个工具是怎么做到对dex进行解析并生成smali代码的呢?这就需要对 dex文件的格式很熟悉.需要掌握dalvik指令的字节码格式,并能翻译成对应的smali代码. 我准备写一系列的文章来分析dex文件的格式.dalvik字节码的格式.以及dex to smali的方法, 基于此可以做很多的应用,比如安全扫描.应用加固等等! Dalvik指令介绍请参考官方文档

实例详解:反编译Android APK,修改字节码后再回编译成APK

本文详细介绍了如何反编译一个未被混淆过的Android APK,修改smali字节码后,再回编译成APK并更新签名,使之可正常安装.破译后的apk无论输入什么样的用户名和密码都可以成功进入到第二个Activity. 有时难免要反编译一个APK,修改其中的若干关键判断点,然后再回编译成一个全新的可用的apk,这完全是可实现的.若要完成上述工作,需要以下工具,杂家后面会把下载链接也附上.这些软件截止本文发布时,经过杂家确认都是最新的版本. 1.APK-Multi-Toolv1.0.11.zip 用它

实例具体解释:反编译Android APK,改动字节码后再回编译成APK

本文具体介绍了怎样反编译一个未被混淆过的Android APK,改动smali字节码后,再回编译成APK并更新签名,使之可正常安装.破译后的apk不管输入什么样的username和password都能够成功进入到第二个Activity. 有时难免要反编译一个APK.改动当中的若干关键推断点,然后再回编译成一个全新的可用的apk,这全然是可实现的. 若要完毕上述工作,须要以下工具,杂家后面会把下载链接也附上.这些软件截止本文公布时,经过杂家确认都是最新的版本号. 1.APK-Multi-Toolv

【分析】dalvik虚拟机解释执行字节码

参考源码版本:Android-4.4.4_r2 提示:大部分分析直接注释在代码内. dvmInterpret函数中调用了dvmInterpretPortable函数对方法的字节码进行解释执行,dvmInterpret在dalvik/vm/interp/Interp.cpp文件中. dvmInterpretPortable函数在dalvik/vm/mterp/out/InterpC-portable.cpp文件中. 使用gcc -E -P -C InterpC-portable.cpp > Int