本帖最后由 Mitjavaz 于 2015-8-11 10:37 编辑
我是一名新人,这两天也发了两个很基础的东西,令大家见笑了。 我的风格就是把我认为的,我所学会的东西,迫不及待地写出来,目的是既能给需要的人带来参考,也能给自己纠正错误理解。 所以呢,今天,我依然是在写基础的东西。 因为我是新人,所以每天学的进度有限,理解能力也有限,所以后期可能会写这样的体会心得的速度会放慢。 毕竟后期慢慢深入进去,就是各种算法,各种机制,各种壳,各种注入之类的东西了。 今天,给大家带来的是Dalvik opcodes方面的东西。 相信大家经常会在反编译中看到,有时候修改个if语句,经常被if-ne和if-nez等等这样的代码整迷糊。 今天的目的就是:认识Dalvik opcodes 并且通过这个目的,为后期的:注入代码等技术手段打下扎实的基础。 而本次就直接沿用上一篇:Android逆向工程——程序修改 所提到的游戏作为实操对象。 在上一篇文章中,我们找到并定位到了“狂热期结束”这个关键词,然后通过Android Killer定位到了关键词所在的类以及其所在的方法。 然后通过修改IF条件语句中的判断条件,实现了延长功能,或者是说将功能的时候推延了。 但是这依然不够完美,因为功能结束之后,又是一段等待期,相信很多人会厌烦这段时间,感觉很折磨人。 那么,这次我就尝试着做永久性的修改操作。 我们再看下代码: 我在此以每一个IF语句作为一个模块来拆分,将其分成了三个模块。 第一个IF语句是一个模块,它只有三个执行语句,分别是:int i2 = 1 + B; 、B = i2; 、和第二个IF语句。 在程序设计上而言,IF语句就是一条判断语句,也只是一条执行语句,无论这条IF语句内包含了多少条执行语句,它都如简单的一条赋值语句一样的存在。 第二个IF语句是一个模块,它有八条执行语句。 第三个IF语句是第三个模块,它只有一条执行语句。 从程序设计上,我们就大致把它的执行流程看了一下。 然后在上一篇:Android逆向工程——程序修改 此文中,我也大概解读了一下第一条和第二条IF语句主要是为了达到什么目的。 第一条IF语句(if (A == 1)):判断功能是否开启,即:游戏当前进行时的状态是否处于狂热期。 第二条IF语句(if (i2 >= 1200)):判断狂热期时间,即:游戏是否该关闭狂热状态,结束狂热期。 由于我们在上一篇文章中,改动了第二条IF语句的条件模块中的常量“1200”,那么,我们换一个思维考虑。 既然if (i2 >= 1200)是判断狂热期的结束,那它对应的所执行的语句,就是执行狂热期结束这一操作。 而我们此次修改的目的是什么? 实现永久狂热状态。 既然是永久狂热状态,那我们是不是就能出这样一个预想,把这条语句整个删除不要,它是不是就没有结束狂热期这一个操作,也就实现了永久狂热期这一个目的。 接着,我们再看下JAVA源码。 既然要做删除,就得知道从哪删到哪。 那么这里,我们在分析过功能之后,也都清楚,关键是第二条IF语句,它掌控着关闭状态的按钮。 删除的方式有: 1、针对性删除:也就是将关闭状态的执行语句删除。 注:这样的删除有一个好处,就是删除的代码不多,但是不好的地方就在于在并不知晓具体是哪一句的时候,操作过程显得复杂繁琐,现在这里只有八条执行语句,假如最后一条执行语句才是关闭状态的执行语句,那么,就需要测试八次。 所以,这里采用要说到的第二种方式。 2、划区间删除:也就是将内嵌式的语句划分后删除。 注:这种方式相对较好,但是不好的地方就是需要多分析,特别是在需要补充返回值的情况下。 原始的Dalvik opcodes: sget v0, Lc/j;->A:I
if-ne v0, v5, :cond_3
sget v0, Lc/j;->B:I
add-int/lit8 v0, v0, 0x1
sput v0, Lc/j;->B:I
const/16 v2, 0x4b0
if-lt v0, v2, :cond_3
sput v1, Lc/j;->A:I
sput v1, Lc/j;->B:I
invoke-static {}, Lb/f;->a()Lb/f;
invoke-static {}, Lb/f;->h()I
move-result v0
if-nez v0, :cond_2
move v0, v5
:cond_2 iget v2, p0, Lc/j;->n:I
mul-int/lit8 v2, v2, 0x64
div-int v0, v2, v0
const/16 v2, 0x7d0
const/16 v4, 0x23
move v3, v1
invoke-static/range {v0 .. v5}, Lb/n;->a(IIIIIZ)I
move-result v6
move v0, v1
move v2, v7
move v3, v1
move v4, v8
invoke-static/range {v0 .. v5}, Lb/n;->a(IIIIIZ)I
move-result v0
add-int/2addr v0, v6
mul-int/lit8 v0, v0, 0x64
sput v0, Lc/j;->x:I
iget-object v0, p0, Lc/j;->au:Lf/a;
iget-object v0, v0, Lf/a;->d:La/h;
const-string v2, "\u72c2\u70ed\u671f\u7ed3\u675f\u3002"
invoke-virtual {v0, v2, v8}, La/h;->a(Ljava/lang/String;I)V
iget-object v0, p0, Lc/j;->au:Lf/a;
const/4 v2, 0x3
invoke-virtual {v0, v2}, Lf/a;->d(I)V 删除的Dalvik opcodes: const/16 v2, 0x4b0
if-lt v0, v2, :cond_3
sput v1, Lc/j;->A:I
sput v1, Lc/j;->B:I
invoke-static {}, Lb/f;->a()Lb/f;
invoke-static {}, Lb/f;->h()I
move-result v0
if-nez v0, :cond_2
move v0, v5
:cond_2 iget v2, p0, Lc/j;->n:I
mul-int/lit8 v2, v2, 0x64
div-int v0, v2, v0
const/16 v2, 0x7d0
const/16 v4, 0x23
move v3, v1
invoke-static/range {v0 .. v5}, Lb/n;->a(IIIIIZ)I
move-result v6
move v0, v1
move v2, v7
move v3, v1
move v4, v8
invoke-static/range {v0 .. v5}, Lb/n;->a(IIIIIZ)I
move-result v0
add-int/2addr v0, v6
mul-int/lit8 v0, v0, 0x64
sput v0, Lc/j;->x:I
iget-object v0, p0, Lc/j;->au:Lf/a;
iget-object v0, v0, Lf/a;->d:La/h;
const-string v2, "\u72c2\u70ed\u671f\u7ed3\u675f\u3002"
invoke-virtual {v0, v2, v8}, La/h;->a(Ljava/lang/String;I)V
iget-object v0, p0, Lc/j;->au:Lf/a;
const/4 v2, 0x3
invoke-virtual {v0, v2}, Lf/a;->d(I)V 最后保留的Dalvik opcodes:
我们看这最后保留的代码: sget v0, Lc/j;->A:I //【sget vx, 字段ID 】这就是这一行的定式,表示:通过字段ID得到一个数据,将数据转交给vx保存。理解就是这么理解,但是官方化就不这么说。
if-ne v0, v5, :cond_3 //【if-ne vx,vy 目标】这就是if-ne的定式,表示:vx 不等于vy ,那么就跳转到 目标 。也就是说,假如这里的v0 不等于 v5 那么程序就不顺势往下执行,而是跳跃到:cond_3这个标记所在的地方。
sget v0, Lc/j;->B:I //同第一条分析。
add-int/lit8 v0, v0, 0x1 //加法运算:vx = vy + lit8 ,书写规范:add-int/lit8 vx,vy,lit8。
sput v0, Lc/j;->B:I //注意,这里的意思是,将vx的数据保存到字段ID对应的存储地方。
if (A == 1) { int i2 = 1 + B; B = i2;
显然,一目了然了,我们保留的Dalvik opcodes和JAVA源码是一致的。 其实,这里有一个难点,就是Dalvik opcodes太多种类了,我们对这些种类都没多少认识。 我在这里找了相关资料,给大家提供一下: PDF版:http://pan.baidu.com/s/14pv46 网页:http://pallergabor.uw.hu/androidblog/dalvik_opcodes.html
好了,归纳总结一下: 做安卓逆向,就得学会跟Dalvik虚拟机打交道,而能与Dalvik虚拟机沟通的方式,就是Dalvik opcodes。
提升的方式: 汇编编程原理。 我不知道大家是怎么看待的,我是在理解的时候,感觉到这个Dalvik opcodes,其实跟汇编的原理差不多的,特别是在使用寄存器赋值,用寄存器传递值量的时候,除了换了一些字节码,基本上就跟汇编差不多。 这里不要求大家都能懂汇编,但是至少能知道汇编的编程方式是怎么进行的。
希望大家能对这种编码规则看得懂,多少要理解,这对将来更深入学习,能打下基础。
好了,谢谢大家观看,若有什么不足,写得不对的,还望能指出。
这里留下一个我没写的,但是大家可以尝试去做的,就是将删除的那部分Dalvik opcodes,按JAVA源码显示出来的代码,将二者代码一一对应起来。
致谢:逆向未来技术社区 |