您现在的位置: 天下网吧 >> 网吧天地 >> 天下码农 >> 架构师 >> 正文

提高代码质量及字节码如何防止内存错误

2008-4-8赛迪网佚名

    大多Java程序员知道他们的程序通常不会被编译为本机代码而是被编译为由java虚拟机(JVM)执行的字节码格式。然而,很少有java程序员曾经看过字节码因为他们的工具不鼓励他们去看。大多Java 调试工具不允许单步执行字节码,它们要么显示源代码行,要么什么也不显示。

    幸运的是JDK提供了javap,一个命令行工具,它使得查看字节码很容易。让我们看一个范例:

public class ByteCodeDemo {

public static void main(String[] args) {

System.out.println("Hello world");

}

}

    在编译这个类后,你可以用十六进制编辑器打开.class文件然后参照虚拟机规范翻译字节码。幸运的是有更简单的方法。JDK包含一个命令行的反汇编器:javap,它可以转换字节码为一种可读的助记符形式,可以像下面这样通过传递'-c'参数给javap得到字节码列表:

    javap -c ByteCodeDemo

    你应该会看到输出类似这样:

public class ByteCodeDemo extends java.lang.Object {

public ByteCodeDemo();

public static void main(java.lang.String[]);

}

Method ByteCodeDemo()

0 aload_0

1 invokespecial #1

4 return

Method void main(java.lang.String[])

0 getstatic #2

3 ldc #3

5 invokevirtual #4

8 return

    仅仅从这个短小的列表你可以学到很多字节码的知识。从main方法的第一个指令开始:

    0 getstatic #2

    开始的整数是方法中的指令的偏移值,因此第一个指令以0开始。紧随偏移量是指令的助记符(mnemonic)。在这个范例中,'getstatic' 指令将一个静态成员压入一个称为操作数堆栈的数据结构,后续的指令可以引用这个数据结构中的成员。getstatic 指令后是要压入的成员。在这个例子中,要压入的成员是"#2 " 。如果你直接检查字节码,你会看到成员信息没有直接嵌入指令而是像所有由java类使用的常量那样存储在一个共享池中。将成员信息存储在一个常量池中可以减小字节码指令的大小,因为指令只需要存储常量池中的一个索引而不是整个常量。在这个例子中,成员信息位于常量池中的#2处。常量池中的项目的顺序是和编译器相关的,因此在你的环境中看到的可能不是'#2' 。

    分析完第一个指令后很容易猜到其它指令的意思。'ldc' (load constant) 指令将常量"Hello, World."压入操作数栈。'invokevirtual'指令调用println方法,它从操作数栈弹出它的两个参数。不要忘记一个像println这样的实例方法有两个参数:上面的字符串,加上隐含的'this'引用。

    字节码如何预防内存错误

    Java语言经常被吹捧为开发互联网软件的"安全的"语言。表面上和c++如此相似的代码如何体现安全呢?它引入的一个重要的安全概念是防止内存相关的错误。计算机罪犯利用内存错误在其它情况下安全的程序中插入自己的恶意的代码。Java字节码是第一个可以预防这种攻击的,像下面的范例展示的:

public float add(float f, int n) {

return f + n;

}

    如果你将这个方法加入上面的范例中,重新编译它,然后运行javap,你将看到的字节码类似这个:

Method float add(float, int)

0 fload_1

1 iload_2

2 i2f

3 fadd

4 freturn

    在方法的开始,虚拟机将方法的参数放入一个称为局部变量表的数据结构中。将像名字暗示的那样,局部变量表也包含了你声明的任何局部变量。在这个例子中,方法以三个局部变量表的项开始,这些都是add方法的参数,位置0保存this引用,而位置1和2分别保存float和int参数。

    为了实际的操作这些变量,它们必须被加载(压入)到操作数栈。第一个指令fload_1将位置1处的float压入操作数栈,第二个指令iload_2将位置2处的int压入操作数栈。这些指令的一个引起注意的事情是指令中的'i'和'f'前缀,这说明Java字节码指令是强类型的。如果参数的类型和字节码的类型不匹配,VM将该字节码作为不安全的而加以拒绝。更好的是,字节码被设计为只需在类被加载时执行一次这样的类型安全检查。

    这个类型安全是如何加强安全的?如果一个攻击者能够欺骗虚拟机将一个int作为一个float或者相反,它就可以很容易的以一个预期的的方法破坏计算。如果这些计算涉及银行结余,那么隐含的安全性是很明显的。更危险的是欺骗VM将一个int作为一个Object引用。在大多情况下,这将导致VM崩溃,但是攻击者只需要找到一个漏洞。不要忘记攻击者不会手工搜索这个漏洞--写出一个程序产生数以亿计的错误字节码的排列是相当容易的,这些排列试图找到危害VM的幸运的那个。

    字节码的另一个内存安全防护是数组操作。'aastore' 和 'aaload' 字节码操作Java数组并且它们总是检查数组边界。如果调用程序越过了数组尾,这些字节码将抛出一个ArrayIndexOutOfBoundsException。也许所有最重要的检查都使用分支指令,例如,以if开始的字节码。在字节码中,分支指令只能转移到同一方法中的其它指令。在方法外可以传递的唯一控制是使它返回:抛出一个异常或者执行一个'invoke'指令。这不仅关闭了很多攻击,同时也防止由于摇荡引用(dangling reference)或者堆栈冲突而引发的令人厌恶的错误。如果你曾经使用系统调试器打开你的程序并定位到代码中的一个随机的位置,那么你会很熟悉这些错误。

欢迎访问最专业的网吧论坛,无盘论坛,网吧经营,网咖管理,网吧专业论坛https://bbs.txwb.com

关注天下网吧微信,了解网吧网咖经营管理,安装维护:


本文来源:赛迪网 作者:佚名

声明
本文来源地址:0
声明:本站所发表的文章、评论及图片仅代表作者本人观点,与本站立场无关。若文章侵犯了您的相关权益,请及时与我们联系,我们会及时处理,感谢您对本站的支持!联系邮箱:support@txwb.com.,本站所有有注明来源为天下网吧或天下网吧论坛的原创作品,各位转载时请注明来源链接!
天下网吧 网吧天下