zoukankan      html  css  js  c++  java
  • Java逆向系列-基础指令:条件跳转 位运算 循环

    条件跳转的例子,绝对值

    public class abs
    {
        public static int abs(int a)
        {
            if (a<0)
                return -a;
            return a;
        }
    }

    编译

    javac abs.java

    反编译

    javap -c -verbose abs.class
      public static int abs(int);
        descriptor: (I)I
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=1, locals=1, args_size=1
             0: iload_0
             1: ifge          7
             4: iload_0
             5: ineg
             6: ireturn
             7: iload_0
             8: ireturn

    其中ifge 7 意思是,当栈顶的值大于等于0的时候跳转到偏移位7,任何的ifXX指令都会将栈中的值弹出用于进行比较

    其中ineg 意思是将栈顶int型数值取负并将结果压入栈顶

    再看一个例子,取最小值

    public class min
    {
    	public static int min (int a, int b)
    	{
    		if (a>b)
    			return b;
    		return a;
    	}
    }

    反编译

      public static int min(int, int);
        descriptor: (II)I
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=2, args_size=2
             0: iload_0
             1: iload_1
             2: if_icmple     7
             5: iload_1
             6: ireturn
             7: iload_0
             8: ireturn

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

    从上面例子可以看出在Java代码中if条件中的测试与在字节码中是完全相反的

    max函数例子

    public class max
    {
    	public static int max (int a, int b)
    	{
    		if (a>b)
    			return a;
    		return b;
    	}
    }

    反编译

      public static int max(int, int);
        descriptor: (II)I
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=2, args_size=2
             0: iload_0
             1: iload_1
             2: if_icmple     7
             5: iload_0
             6: ireturn
             7: iload_1
             8: ireturn

    代码和min的差不多,唯一的区别是最后两个iload指令(偏移位5和偏移位7)互换了

    更复杂的例子

    public class cond
    {
        public static void f(int i)
        {
            if (i<100)
                System.out.print("<100");
            if (i==100)
                System.out.print("==100");
            if (i>100)
                System.out.print(">100");
            if (i==0)
                System.out.print("==0");
        }
    }

    反编译

      public static void f(int);
        descriptor: (I)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=1, args_size=1
             0: iload_0
             1: bipush        100
             3: if_icmpge     14
             6: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
             9: ldc           #3                  // String <100
            11: invokevirtual #4                  // Method java/io/PrintStream.print:(Ljava/lang/String;)V
            14: iload_0
            15: bipush        100
            17: if_icmpne     28
            20: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            23: ldc           #5                  // String ==100
            25: invokevirtual #4                  // Method java/io/PrintStream.print:(Ljava/lang/String;)V
            28: iload_0
            29: bipush        100
            31: if_icmple     42
            34: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            37: ldc           #6                  // String >100
            39: invokevirtual #4                  // Method java/io/PrintStream.print:(Ljava/lang/String;)V
            42: iload_0
            43: ifne          54
            46: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            49: ldc           #7                  // String ==0
            51: invokevirtual #4                  // Method java/io/PrintStream.print:(Ljava/lang/String;)V
            54: return

    if_icmpge 14 栈顶弹出两个值,并且比较两个数值,如果第的二个值大于或等于第一个,跳转到偏移位14

    if_icmpne  28 栈顶弹出两个值,并且比较两个数值,如果第的二个值不等于第一个,跳转到偏移位28

    ifne 54 当栈顶int型数值不等于0时跳转到偏移位54

    传参例子

    public class minmax
    {
        public static int min (int a, int b)
        {
            if (a>b)
                return b;
            return a;
        }
        public static int max (int a, int b)
        {
            if (a>b)
                return a;
            return b;
        }
        public static void main(String[] args)
        {
            int a=123, b=456;
            int max_value=max(a, b);
            int min_value=min(a, b);
            System.out.println(min_value);
            System.out.println(max_value);
        }
    }

    反编译

      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=5, args_size=1
             0: bipush        123
             2: istore_1
             3: sipush        456
             6: istore_2
             7: iload_1
             8: iload_2
             9: invokestatic  #2                  // Method max:(II)I
            12: istore_3
            13: iload_1
            14: iload_2
            15: invokestatic  #3                  // Method min:(II)I
            18: istore        4
            20: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
            23: iload         4
            25: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
            28: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
            31: iload_3
            32: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
            35: return

    istore_1 将栈顶元素弹出并存到本地变量数组1号元素,因为0号给了this

    位操作

    public class bitop
    {
    	public static int set (int a, int b)
    	{
    		return a | 1<<b;
    	}
    
    	public static int clear (int a, int b)
    	{
    		return a & (~(1<<b));
    	}
    }

    反编译

      public static int set(int, int);
        descriptor: (II)I
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=3, locals=2, args_size=2
             0: iload_0
             1: iconst_1
             2: iload_1
             3: ishl
             4: ior
             5: ireturn
          LineNumberTable:
            line 5: 0
    
      public static int clear(int, int);
        descriptor: (II)I
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=3, locals=2, args_size=2
             0: iload_0
             1: iconst_1
             2: iload_1
             3: ishl
             4: iconst_m1
             5: ixor
             6: iand
             7: ireturn
          LineNumberTable:
            line 10: 0

    set函数的指令解释

    0: iload_0 //载入第0个参数即a,压入栈

    1: iconst_1 //数字1压入栈

    2: iload_1 ///载入第1个参数即b,压入栈

    3: ishl //弹出栈顶两个元素,将int型数值左移位指定位数并将结果压入栈顶,第一个弹出值(b)为左移的位数,第二个弹出的值(1)为要对其进行左移的数,即对1左移b位

    4: ior //将栈顶两int型数值作“按位或”并将结果压入栈顶 

    5: ireturn //返回栈顶值

    clear函数中iconst_m1指令将-1压入栈,-1就是16进制的0xFFFFFFFF, ixor是进行异或操作,操作结果相当于取反

    将上面的例子扩展成long类型

    public class longbitop
    {
    	public static long lset (long a, int b)
    	{
    		return a | 1<<b;
    	}
    	public static long lclear (long a, int b)
    	{
    		return a & (~(1<<b));
    	}
    }

    反编译

      public static long lset(long, int);
        descriptor: (JI)J
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=4, locals=3, args_size=2
             0: lload_0
             1: iconst_1
             2: iload_2
             3: ishl
             4: i2l
             5: lor
             6: lreturn
          LineNumberTable:
            line 5: 0
    
      public static long lclear(long, int);
        descriptor: (JI)J
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=4, locals=3, args_size=2
             0: lload_0
             1: iconst_1
             2: iload_2
             3: ishl
             4: iconst_m1
             5: ixor
             6: i2l
             7: land
             8: lreturn
          LineNumberTable:
            line 9: 0

    需要注意的指令

    i2l 将栈顶int型数值强制转换成long型数值并将结果压入栈顶

    32位需要升级为64位值时,会使用i21指令把整型扩展成64位长整型.

    循环

    看个简单的例子

    public class Loop
    {
        public static void main(String[] args)
        {
            for (int i = 1; i <= 10; i++)
            {
                System.out.println(i);
            }
        }
    }

    反编译

      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=2, args_size=1
             0: iconst_1
             1: istore_1
             2: iload_1
             3: bipush        10
             5: if_icmpgt     21
             8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            11: iload_1
            12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
            15: iinc          1, 1
            18: goto          2
            21: return

    指令解释

    0: iconst_1 //常数1压入栈

    1: istore_1 //栈顶弹出存入本地变量数组1号元素

    2: iload_1 //本地变量1号元素压入栈顶

    3: bipush        10 //常数10压入栈顶

    5: if_icmpgt     21 //  比较栈顶两int型数值大小,当结果大于0时跳转到偏移位21,比较都是拿第二个弹出值与第一个比较即i与10比较

    8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;

    11: iload_1

    12: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V //8~12这里输出变量i

    15: iinc          1, 1  //将指定int型变量增加指定值(常用于i++,i--,i+=2) ,这里指本地变量1号元素加1

    18: goto          2 //跳转到偏移位2

    21: return

    多说一句,我们调用println打印数据类型是整型,我们看注释,“(I)V”,I的意思是整型,V的意思是返回void

    再看个复杂的,斐波那契数列

    public class Fibonacci
    {
        public static void main(String[] args)
        {
            int limit = 20, f = 0, g = 1;
            for (int i = 1; i <= limit; i++)
            {
                f = f + g;
                g = f - g;
                System.out.println(f);
            } 
        }
    }

    反编译

      public static void main(java.lang.String[]);
        descriptor: ([Ljava/lang/String;)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=5, args_size=1
             0: bipush        20
             2: istore_1
             3: iconst_0
             4: istore_2
             5: iconst_1
             6: istore_3
             7: iconst_1
             8: istore        4
            10: iload         4
            12: iload_1
            13: if_icmpgt     37
            16: iload_2
            17: iload_3
            18: iadd
            19: istore_2
            20: iload_2
            21: iload_3
            22: isub
            23: istore_3
            24: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            27: iload_2
            28: invokevirtual #3                  // Method java/io/PrintStream.println:(I)V
            31: iinc          4, 1
            34: goto          10
            37: return

    拿几个指令说一下

    if_icmpgt 37 比较栈顶两int型数值大小,当结果大于0时跳转到偏移位37,比较都是拿栈的第二个弹出值(本地变量4号)与第一个比较(本地变量1号)

    isub 栈顶的两个元素相减并将结果压入栈顶,谁减谁,仍然是栈顶弹出的第二个减第一个

    iinc 4, 1 表示是本地变量4号元素加1,结果存在本地变量4号

    这里的英文作者又吐槽了一下

    8: istore        4

    10: iload         4

    这两句中的iload 4,感觉多余。。

    switch例子

    public class tableswitch {
    	public static void f(int a) {
    		switch (a) {
    		case 0:
    			System.out.println("zero");
    			break;
    		case 1:
    			System.out.println("one
    ");
    			break;
    		case 2:
    			System.out.println("two
    ");
    			break;
    		case 3:
    			System.out.println("three
    ");
    			break;
    		case 4:
    			System.out.println("four
    ");
    			break;
    		default:
    			System.out.println("something unknown
    ");
    			break;
    		}
    	}
    }

    反编译

      public static void f(int);
        descriptor: (I)V
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
          stack=2, locals=1, args_size=1
             0: iload_0
             1: tableswitch   { // 0 to 4
                           0: 36
                           1: 47
                           2: 58
                           3: 69
                           4: 80
                     default: 91
                }
            36: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            39: ldc           #3                  // String zero
            41: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            44: goto          99
            47: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            50: ldc           #5                  // String one
    
            52: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            55: goto          99
            58: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            61: ldc           #6                  // String two
    
            63: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            66: goto          99
            69: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            72: ldc           #7                  // String three
    
            74: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            77: goto          99
            80: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            83: ldc           #8                  // String four
    
            85: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            88: goto          99
            91: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
            94: ldc           #9                  // String something unknown
    
            96: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
            99: return

    其中需要注意的是tableswitch是在JVM级别上直接支持switch 语句,非常有意思的是,编译器编译的时候,会根据case条件的不同翻译成tableswitch或者lookupswitch

      1: tableswitch   { // 0 to 4

                           0: 36

                           1: 47

                           2: 58

                           3: 69

                           4: 80

                     default: 91

                }

    的意思,将栈顶元素与大括号内冒号前的元素逐个比较,若相等,则跳转到右边对应的偏移块号

    break对应其中的goto 99,如果没有这句,栈顶元素会继续和后面的大括号内后续元素进行比较

    本文参考:逆向工程权威指南.下册.pdf 和http://blog.51cto.com/7317859/2105269

  • 相关阅读:
    STM32F107的DAC配置
    步进电机工作原理
    winform笔记本蓝牙与外部蓝牙设备通信
    C#里三种强制类型转换
    IE6/7BUG之OL有序列表没顺序
    IE6/7BUG之列表UL楼梯
    IE6/7BUG之A超链接无效
    IE6/7BUG之overflow:hidden无效
    linux shell 管道命令(pipe)使用及与shell重定向区别
    UGUI研究院之全面理解图集与使用
  • 原文地址:https://www.cnblogs.com/llhl/p/9648609.html
Copyright © 2011-2022 走看看