ctf-learn Bite-code

Bite-code

Bite-code这道题目是ctf-learn网站中reversing中的一道相对来说比较简单的题目,该题目的链接地址如下题目地址

将题目中的文件下载下来,是一个txt文件,通过观察文件中的信息,发现该文件是java的字节码,作者的意思应该是通过字节码文件逆行分析出函数的功能。

txt文件中的字节码文件如下所示:

public static boolean checkNum(int);
    descriptor: (I)Z
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: iload_0
         1: iconst_3
         2: ishl
         3: istore_1
         4: iload_0
         5: ldc           #2                  // int 525024598
         7: ixor
         8: istore_2
         9: iload_1
        10: iload_2
        11: ixor
        12: ldc           #3                  // int -889275714
        14: if_icmpne     21
        17: iconst_1
        18: goto          22
        21: iconst_0
        22: ireturn
      LineNumberTable:
        line 3: 0
        line 4: 4
        line 5: 9
      StackMapTable: number_of_entries = 2
        frame_type = 253 /* append */
          offset_delta = 21
          locals = [ int, int ]
        frame_type = 64 /* same_locals_1_stack_item */
          stack = [ int ]

在上面的文件中我们需要关心的是code下的指令,这是该方法的执行指令。

接下来对上面的字节码文件进行分析:
public static boolean checkNum(int);这个语句可以看出函数的定义。当然下面的信息也可以看出

descriptor中描述的是该函数有一个形参,形参的类型是Int类型,同时该函数的返回值是布尔类型(I:表示整型, Z:表示)

flags:表示该函数的访问修饰,ACC_PUBLIC表示该函数是public类型,ACC_STATIC表示该函数是static

code:表示了该函数的指令,在JVM是基于堆栈来实现指令的执行的

在理解code下的指令之前,我们首先通过一个简单的例子来了解一下字节码中的指令。

一个简单的java程序例子如下所示;

# Test.java
public class Test {
    public static void main(String[] args) {
        int a = 1;
        int b = 2;
        int c = a + b;
    }
}

对上面的程序进行编译,编译命令javac Test.java,编译完成之后通过命令javap -v Test.class就可以得到字节码文件,字节码文件如下所示(这里只展示code部分):

    Code:
      stack=2, locals=4, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_2
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: istore_3
         8: return

上面的字节码上,stack表示栈的深度,locals表示局部变量的个数,args_size表示形参的个数。
iconst_1:将常量1压入栈中
istore_1: 将栈顶中的元素存储到索引值为1的内存区域,相对于把1赋给一个变量(a)
iconst_2:将常量2压入栈中
istore_2: 将栈顶中的元素存储到索引值为2的内存区域,相对于把1赋给一个变量(b)
**iload_1:将索引值为1的内存区域的值压栈,相当于把a的值入栈
iload_2: 将索引值为2的内存区域的值压栈, 相当于把b的值入栈
iadd:将栈顶的两个元素出栈,然后相加,把相加后的结果入栈
istore_3: 将栈顶中的元素存储到索引值为3的内存区域,相对于把a+b赋给一个变量(c)

通过上面的解释,大概了解指令的意义。我们可以得出之前的代码的函数,如下:

public static boolean checkNum(int x){
  n1 = 525024598
  n2 = -889275714
  n3 = (x << 3) ^ (x ^ n1)
  if (n3 == n2) {
    return true;
  }
  else
    return false;
}

因此,我们可以通过暴力破解来获取我们的flag,暴力破解程序如下:

public class Solution {
    public static void main(String[] args) {
        int n1 = 525024598;
        int n2 = -889275714;

        for (int i = Integer.MIN_VALUE; i < Integer.MAX_VALUE; i++) {
            int n3 = (i << 3) ^ (n1 ^ i);
            if (n3 == n2) {
                System.out.println("Proper input: " + i);
                break;
            }
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容