Android软件安全与逆向分析——了解Dalvik寄存器


Dalvik寄存器


Dalvik虚拟机基于寄存器架构,在代码中大量地使用到了寄存器。

Dalvik将部分寄存器映射到了ARM寄存器上(ARM架构的CPU本身集成了多个寄存器),还有一部分则通过调用栈进行模拟。

Dalvik用到的寄存器都是32位的,支持任何类型。

Dalvik指令的语法为“op vAAAA,vBBBB”,每个大写字母代表4位(16进制),即最大值为2^16=65536,所以寄存器的取值范围为v0~v65535。


Dalvik虚拟机为每个进程维护一个调用栈,这个调用栈其中一个作用就是用来“虚拟”寄存器。

每个函数都在函数头部使用.registers指定函数用到的寄存器数目,当虚拟机执行到这个函数时会根据寄存器的数目分配适当的栈空间,用来存放寄存器实际的值。

虚拟机通过处理字节码对寄存器进行读写操作,其实都是在写栈空间。

Android SDK中有一个名为 dalvik.bytecode.Opcodes 的接口,它定义了一份完整的 Dalvik 字节码列表。处理这些字节码的函数为一个宏函数 HANDLE_OPCODE() ,每个字节码的处理过程都可以在 Android 源码的 dalvik\vm\mterp\c 目录中找到。


以 OP_MOVE 做例子:


HANDLE_OPCODE($opcode /*vA, vB*/)
    vdst = INST_A(inst);
    vsrc1 = INST_B(inst);
    ILOGV("|move%s v%d,v%d %s(v%d=0x%08x)",
        (INST_INST(inst) == OP_MOVE) ? "" : "-object", vdst, vsrc1,
        kSpacing, vdst, GET_REGISTER(vsrc1));
    SET_REGISTER(vdst, GET_REGISTER(vsrc1));
    FINISH(1);
OP_END

关于 INST_A 和 INST_B 的定义可在同目录下的 header.cpp 文件中找到:


/*
 * Extract the "vA, vB" 4-bit registers from the instruction word (_inst is u2).
 */
#define INST_A(_inst)       (((_inst) >> 8) & 0x0f)
#define INST_B(_inst)       ((_inst) >> 12)


_inst在这里表示一个16位的指令,

INST_A 将 _inst 右移8位后和 0x0f相与,即获得 _inst 高8位的低4位作为 vdst 的值;

INST_B 将 _inst 右移12位,即获得 _inst 的最高4位为 vsrc1 的值。


ILOGV 用来输出调试信息:

# define ILOGV(...) ILOG(LOG_VERBOSE, __VA_ARGS__)

INST_INST 的作用为:(获得 _inst 的低8位)


/*
 * Extract instruction byte from 16-bit fetch (_inst is a u2).
 */
#define INST_INST(_inst)    ((_inst) & 0xff)

GET_REGISTER 用来获取寄存器的值, SET_REGISTER 用来设置寄存器的值:


# define GET_REGISTER(_idx)                 (fp[(_idx)])
# define SET_REGISTER(_idx, _val)           (fp[(_idx)] = (_val))

另外,操作的寄存器可以是其他的大小和类型,如 GET_REGISTER_WIDE、GET_REGISTER_FLOAT等:


# define GET_REGISTER_WIDE(_idx)            getLongFromArray(fp, (_idx))
# define SET_REGISTER_WIDE(_idx, _val)      putLongToArray(fp, (_idx), (_val))
# define GET_REGISTER_FLOAT(_idx)           (*((float*) &fp[(_idx)]))
# define SET_REGISTER_FLOAT(_idx, _val)     (*((float*) &fp[(_idx)]) = (_val))

其中,fp为ARM栈帧寄存器(从逻辑上讲,栈帧就是一个函数执行的环境:函数参数、函数的局部变量、函数执行完后返回到哪里等等,C语言中,每个栈帧对应着一个未运行完的函数。栈帧中保存了该函数的返回地址和局部变量),在虚拟机运行到某个函数时它指向函数的局部变量区,其中就维护着一份寄存器值的列表;

GET_REGISTER 宏以 _idx 为索引返回一个“寄存器”的值,而 SET_REGISTER 则是以 _idx 为索引设置相应寄存器的值。


如果 Dalvik 虚拟机开启了寄存器数目验证,即 #ifdef CHECK_REGISTER-INDICES 为真时,在进行寄存器读写时,虚拟机会首先判断 _idx 是否小于 curMrthod -> registerSize , 如果条件不成立则说明寄存器超出引用范围,此时虚拟机会通过 assert(!"bad reg") 抛出异常(源码在 header.cpp的第 219 行起)。


最后一条指令是 FINISH ,用来完成一条指令的执行,功能由 ADJUST_PC 完成,主要是计算当前指令占用的长度,将 PC 寄存器加上计算出的偏移,这样一条指令执行完成后, PC 计数器会指向下一条要执行的指令。


理解有待深化。。

委屈





评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值