逆向基础(十三) JAVA (二)

author:糖果

54.7 线性同余伪随机数生成器

这次来看一个简单的伪随机函数生成器,之前我在书中提到过一次。

在上面的代码中我们可以看到开始的地方有两个类字段被初始化。不过java究竟是如何进行初始化的呢,我们可以通过javap的输出看到类构造的方式。

从上面的代码我们可以直观的看出变量如何被初始化,RNG_a和iRNG_C分别占用了第三以及第四储存位,并使用puststatic指令将常量put进储存位置。

下面的my_srand()函数将输入值存储到rand_state中;

iload_1 取得输入值并将其压入栈。但为什么不用iload_0? 因为函数中可能使用了类字段,所以这个变量被作为第0个参数传递给了函数,我们可以看到rand_state字段在类中占用第二个储存位。之前的putstatic会从栈顶复制数据并且将其压入第二储存位。

现在的my_rand():

它仅是加载了所有对象字段的值。并且用putstatic指令对rand_state的值进行更新。

因为之前我们通过putstatic指令将rand_state的值丢弃,所以在20行的位置,再次加载rand_state值。这种方式其实效率不高,不过我们还是要承认jvm在某些地方所做的优化还是很不错的。

54.8 条件跳转

我们来举个条件跳转的栗子:

上面代码中ifge指令的作用是:当栈顶的值大于等于0的时候跳转到偏移位7,需要注意的是,任何的ifXX指令都会将栈中的值弹出用于进行比较。

现在来看另外一个例子

我们得到的是:

if_icmple会从栈中弹出两个值进行比较,如果第二个小于或者等于第一个,那么跳转到偏移位7.

我们看另一个max函数的例子:

从下面可以看出代码都差不多,唯一的区别是最后两个iload指令(偏移位5和偏移位7)互换了。

更复杂的例子。。

if_icmpge出栈两个值,并且比较两个数值,如果第的二个值大于第一个,跳转到偏移位14,if_icmpne和if_icmple做的工作类似,但是使用不同的判断条件。

在行偏移43的ifne指令,它的名字不是很恰当,我更愿意把它命名为ifnz(if not zero 可能是冷笑话)(如果栈定的值不是0则跳转),当不是0的时候,跳转到偏移54,如果输入的值不是另,如果是0,执行流程进入偏移46,并且打印字符串“==0”。

JVM没有无符号数据类型,所以,只能通过符号整数值进行比较指令操作。

54.9 传递参数值

让我们稍微扩展一下min()和max()这个例子。

下面是main()函数的代码。

栈中的参数被传递给其他函数,并且将返回值置于栈顶。

54.10位。

java中的位操作其实与其他的一些ISA(指令集架构)类似:

iconst_m1加载-1入栈,这数其实就是16进制的0xFFFFFFFF,将0xFFFFFFFF作为XOR-ing指令执行的操作数。起到的效果就是把所有bits位反向,(A.6.2在1406页)

我将所有数据类型,扩展成64为长整型。

代码是相同的,但是操作64位值的指令的前缀变成了L,并且第二个函数参数还是int类型,当32位需要升级为64位值时,会使用i21指令把整型扩展成64位长整型.

54.11循环

icont_1将1推入栈顶,istore_1将其存入到局部数组变量的储存位1。

可以注意到没有使用第0个储存位,因为main()函数只有一个指向其的引用的参数(String数组),就位于第0号槽中。

因此,本地变量i 总是在第1储存位中。 在行偏移3和行偏移5的位置,指令将i和10进行比较。如果i大于10,执行流将进入偏移21,之后函数会结束,如果i小于或等于10,则调用println。我们可以看到i在偏移11进行了重新加载,用于调用println。

多说一句,我们调用pringln打印数据类型是整型,我们看注释,“i,v”,i的意思是整型,v的意思是返回void。

当println函数结束时,i进入偏移15,通过指令iinc将参数槽1的值,数值1与本地变量相加。

goto指令就是跳转,它跳转偏移2,就是循环体的开始地址.

下面让我们来处理更复杂的例子

评论

小猪。2015-07-27 14:43:25

感谢,学习很多