清單 54.10: IncorrectMonthException.java
#!java
public class IncorrectMonthException extends Exception
{
private int index;
public IncorrectMonthException(int index)
{
this.index = index;
}
public int getIndex()
{
return index;
}
}
清單 54.11: Month2.java
#!java
class Month2
{
public static String[] months =
{
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
public static String get_month (int i) throws ?
? IncorrectMonthException
{
if (i<0 || i>11)
throw new IncorrectMonthException(i);
return months[i];
};
public static void main (String[] args)
{
try
{
System.out.println(get_month(100));
}
catch(IncorrectMonthException e)
{
System.out.println("incorrect month ?
? index: "+ e.getIndex());
e.printStackTrace();
}
};
}
本質上,IncorrectMonthExceptinClass類只是做了對象構造,還有訪問器方法。 IncorrectMonthExceptinClass是繼承于Exception類,所以,IncorrectMonth類構造之前,構造父類Exception,然后傳遞整數給IncorrectMonthException類作為唯一的屬性值。
public IncorrectMonthException(int);
flags: ACC_PUBLIC
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: invokespecial #1 // Method java/?
? lang/Exception."<init>":()V
4: aload_0
5: iload_1
6: putfield #2 // Field index:I
9: return
getIndex()只是一個訪問器,引用到IncorrectMothnException類,被傳到LVA的0槽(this指針),用aload_0指令取得, 用getfield指令取得對象的整數值,用ireturn指令將其返回。
public int getIndex();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #2 // Field index:I
4: ireturn
現在來看下month.class的get_month方法。
清單 54.12: Month2.class
public static java.lang.String get_month(int) throws ?
? IncorrectMonthException;
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=1, args_size=1
0: iload_0
1: iflt 10
4: iload_0
5: bipush 11
7: if_icmple 19
10: new #2 // class ?
? IncorrectMonthException
13: dup
14: iload_0
15: invokespecial #3 // Method ?
? IncorrectMonthException."<init>":(I)V
18: athrow
19: getstatic #4 // Field months:[?
? Ljava/lang/String;
22: iload_0
23: aaload
24: areturn
949
iflt 在行偏移1 ,如果小于的話,
這種情況其實是無效的索引,在行偏移10創建了一個對象,對象類型是作為操作書傳遞指令的。(這個IncorrectMonthException的構造屆時,下標整數是被通過TOS傳遞的。行15偏移) 時間流程走到了行18偏移,對象已經被構造了,現在athrow指令取得新構對象的引用,然后發信號給JVM去找個合適的異常句柄。
athrow指令在這個不返回到控制流,行19偏移的其他的個基本模塊,和異常無關,我們能得到到行7偏移。 句柄怎么工作? main()在inmonth2.class
清單 54.13: Month2.class
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=3, locals=2, args_size=1
0: getstatic #5 // Field java/?
? lang/System.out:Ljava/io/PrintStream;
3: bipush 100
5: invokestatic #6 // Method ?
? get_month:(I)Ljava/lang/String;
8: invokevirtual #7 // Method java/io?
? /PrintStream.println:(Ljava/lang/String;)V
11: goto 47
14: astore_1
15: getstatic #5 // Field java/?
? lang/System.out:Ljava/io/PrintStream;
18: new #8 // class java/?
? lang/StringBuilder
21: dup
22: invokespecial #9 // Method java/?
? lang/StringBuilder."<init>":()V
25: ldc #10 // String ?
? incorrect month index:
27: invokevirtual #11 // Method java/?
? lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/?
? StringBuilder;
30: aload_1
31: invokevirtual #12 // Method ?
? IncorrectMonthException.getIndex:()I
34: invokevirtual #13 // Method java/?
? lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
37: invokevirtual #14 // Method java/?
? lang/StringBuilder.toString:()Ljava/lang/String;
40: invokevirtual #7 // Method java/io?
? /PrintStream.println:(Ljava/lang/String;)V
43: aload_1
44: invokevirtual #15 // Method ?
? IncorrectMonthException.printStackTrace:()V
47: return
Exception table:
from to target type
0 11 14 Class IncorrectMonthException
950 這是一個異常表,在行偏移0-11(包括)行,一個IncorrectinMonthException異常可能發生,如果發生,控制流到達14行偏移,確實main程序在11行偏移結束,在14行異常開始, 沒有進入此區域條件(condition/uncondition)設定,是不可能到打這個位置的。(PS:就是沒有異常捕獲的設定,就不會有異常流被調用執行。)
但是JVM會傳遞并覆蓋執行這個異常case。 第一個astore_1(在行偏移14)取得,將到來的異常對象的引用,存儲在LVA的槽參數1之后。getIndex()方法(這個異常對象) 會被在31行偏移調用。引用當前的異常對象,是在30行偏移之前。 所有的這些代碼重置都是字符串操作代碼:第一個整數值使用的是getIndex()方法,被轉換成字符串使用的是toString()方法,它會和“正確月份下標”的文本字符來鏈接(像我們之前考慮的那樣)。 println()和printStackTrace(1)會被調用,PrintStackTrace(1)調用 結束之后,異常被捕獲,我們可以處理正常的函數,在47行偏移,return結束main()函數 , 如果沒有發生異常,不會執行任何的代碼。
這有個例子,IDA是如何顯示異常范圍:
清單54.14 我從我的計算機中找到 random.class 這個文件
.catch java/io/FileNotFoundException from met001_335 to ?
? met001_360\
using met001_360
.catch java/io/FileNotFoundException from met001_185 to ?
? met001_214\
using met001_214
.catch java/io/FileNotFoundException from met001_181 to ?
? met001_192\
using met001_195
951
CHAPTER 54. JAVA 54.16. CLASSES
.catch java/io/FileNotFoundException from met001_155 to ?
? met001_176\
using met001_176
.catch java/io/FileNotFoundException from met001_83 to ?
? met001_129 using \
met001_129
.catch java/io/FileNotFoundException from met001_42 to ?
? met001_66 using \
met001_69
.catch java/io/FileNotFoundException from met001_begin to ?
? met001_37\
using met001_37
清單 54.15: test.java
#!java
public class test
{
public static int a;
private static int b;
public test()
{
a=0;
b=0;
}
public static void set_a (int input)
{
a=input;
}
public static int get_a ()
{
return a;
}
public static void set_b (int input)
{
b=input;
}
public static int get_b ()
{
return b;
}
}
構造函數,只是把兩個之段設置成0.
public test();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/?
? lang/Object."<init>":()V
4: iconst_0
5: putstatic #2 // Field a:I
8: iconst_0
9: putstatic #3 // Field b:I
12: return
a的設定器
public static void set_a(int);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
0: iload_0
1: putstatic #2 // Field a:I
4: return
a的取得器
public static int get_a();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: getstatic #2 // Field a:I
3: ireturn
b的設定器
public static void set_b(int);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=1, args_size=1
0: iload_0
1: putstatic #3 // Field b:I
4: return
b的取得器
public static int get_b();
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=0, args_size=0
0: getstatic #3 // Field b:I
3: ireturn
953 類中的公有和私有字段代碼沒什么區別。 但是類型信息會在in.class 文件中表示,并且,無論如何私有變量是不可以被訪問的。
讓我們創建對象并調用方法: 清單 54.16: ex1.java
954 新指令創建對象,但不調用構造函數(它在4行偏移被調用)set_a()方法被在16行偏移被調用,字段訪問使用的getstatic指令,在行偏移21。
Listing 54.16: ex1.java
#!java
public class ex1
{
public static void main(String[] args)
{
test obj=new test();
obj.set_a (1234);
System.out.println(obj.a);
}
}
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: new #2 // class test
3: dup
4: invokespecial #3 // Method test."<?
? init>":()V
7: astore_1
8: aload_1
9: pop
10: sipush 1234
13: invokestatic #4 // Method test.?
? set_a:(I)V
16: getstatic #5 // Field java/?
? lang/System.out:Ljava/io/PrintStream;
19: aload_1
20: pop
21: getstatic #6 // Field test.a:I
24: invokevirtual #7 // Method java/io?
? /PrintStream.println:(I)V
27: return
54.17.1 第一個例子
讓我們進入簡單的一個例子。
#!java
public class nag
{
public static void nag_screen()
{
System.out.println("This program is not ?
? registered");
};
public static void main(String[] args)
{
System.out.println("Greetings from the mega-?
? software");
nag_screen();
}
}
我們如何去除打印輸出"This program is registered".
最會在IDA中加載.class文件。
清單54.1: IDA
我們patch到函數的第一個自己到177(返回指令操作碼) Figure 54.2 : IDA
這個在JDK1.7中不工作
Exception in thread "main" java.lang.VerifyError: Expecting a ?
? stack map frame
Exception Details:
Location:
nag.nag_screen()V @1: nop
Reason:
Error exists in the bytecode
Bytecode:
0000000: b100 0212 03b6 0004 b1
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java?
? :2615)
at java.lang.Class.getMethod0(Class.java:2856)
at java.lang.Class.getMethod(Class.java:1668)
at sun.launcher.LauncherHelper.getMainMethod(?
? LauncherHelper.java:494)
at sun.launcher.LauncherHelper.checkAndLoadMain(?
? LauncherHelper.java:486)
956 也許,JVM有一些檢查,關聯到棧映射。 好的,為了讓path不同,我們使用remove call nag()
清單:54.5 IDA NOP的操作碼是0: 現在工作起來了
54.17.2第二個例子
現在是另外一個簡單的crackme例子。
#!java
public class password
{
public static void main(String[] args)
{
System.out.println("Please enter the password")?
? ;
String input = System.console().readLine();
if (input.equals("secret"))
System.out.println("password is correct?");
else
System.out.println("password is not ?
? correct");
}
}
圖54.4:IDA
我們看ifeq指令是怎么工作的,他的名字的意思是如果等于。 這是不恰當的,我更愿意命名if (ifz if zero) 如果棧頂值是0,他就會跳轉,在我們這個例子,如果密碼 不正確他就跳轉。(equal方法返回的是0) 首先第一個主意是patch這個指令... iefq是兩個bytes的操作碼 編碼和跳轉偏移,讓這個指令定制,我們必須設定byte3 3byte(因為3是添加到當前地址結束總是跳轉同下一條指令) 因為ifeq的指令長度是3bytes.
958 圖54.5IDA
這個在JDK1.7中不工作
Exception in thread "main" java.lang.VerifyError: Expecting a ?
? stackmap frame at branch target 24
Exception Details:
Location:
password.main([Ljava/lang/String;)V @21: ifeq
Reason:
Expected stackmap frame at this location.
Bytecode:
0000000: b200 0212 03b6 0004 b800 05b6 0006 4c2b
0000010: 1207 b600 0899 0003 b200 0212 09b6 0004
0000020: a700 0bb2 0002 120a b600 04b1
Stackmap Table:
append_frame(@35,Object[#20])
same_frame(@43)
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java?
? :2615)
at java.lang.Class.getMethod0(Class.java:2856)
at java.lang.Class.getMethod(Class.java:1668)
at sun.launcher.LauncherHelper.getMainMethod(?
? LauncherHelper.java:494)
959
CHAPTER 54. JAVA 54.18. SUMMARY
at sun.launcher.LauncherHelper.checkAndLoadMain(?
? LauncherHelper.java:486)
不用說了,它工作在JRE1.6 我也嘗試替換所有3ifeq操作嗎bytes,使用0字節(NOP),它仍然會工作,好 可能沒有更多的堆棧映射在JRE1.7中被檢查出來。
好的,我替換整個equal調用方法,使用icore_1指令加上NOPS的爭強patch.
11總是在棧頂,當ifeq指令別執行...所以ifeq不會被執行。
工作了。
54.18總結
960 和C/C+比較java少了一些什么? 結構體:使用類 聯合:使用集團類 無附加數據類型,多說一句,還有一些在Java中實現的加密算法的硬編碼。 函數指針。