透视JAVA——反编译、修补和逆向工程技术 读书笔记
1、 Java source is not compiled to binary machine code like C/C++ source is.
2、 Because the bytecode does not represent the lowest-level machine language, the format of the code closely resembles the source code.
3、public String getDisplayName() {
return getUserName() + “ (“ + getHostName() + “)”;
}
is represented by the following bytecode:
0 new #4 <java/lang/StringBuffer>
3 dup
4 aload_0
5 invokevirtual #5 <covertjava/decompile/MessageInfoComplex.getUserName>
8 invokestatic #6 <java/lang/String.valueOf>
11 invokestatic #6 <java/lang/String.valueOf>
14 invokespecial #7 <java/lang/StringBuffer.<init>>
17 ldc #8 < (>
19 invokevirtual #9 <java/lang/StringBuffer.append>
22 aload_0
23 invokevirtual #10 <covertjava/decompile/MessageInfoComplex.getHostName>
26 invokevirtual #9 <java/lang/StringBuffer.append>
29 ldc #11 <)>
31 invokevirtual #9 <java/lang/StringBuffer.append>
34 invokestatic #6 <java/lang/String.valueOf>
37 invokestatic #6 <java/lang/String.valueOf>
40 areturn
3、 Potential Problems with Decompiled Code(混淆器)
Most of the time, decompiling produces a readable file that can be changed and recompiled.However, on some occasions decompiling does not render a file that can be compiled again.
Reason: This can happen if the bytecode was obfuscated, and the names given by the obfuscator result in ambiguity at the compilation. The bytecode is verified when loaded, but the verifications assume that the compiler has checked for a number of errors.
obfuscated by the Zelix ClassMaster obfuscator.
static class c
implements Runnable
{
public void run()
{
boolean flag = a.b;
System.out.println(a(“*4%p 02 26&kj 16 135”));
b b1 = new b(a(“2 00 06_”; ”), a(“3 ‘w 05 2778u 22”));
System.out.println(a(“5$8m 37$kw 17X|k”).concat(String.valueOf
➥(String.valueOf(b1.d()))));
b1 = new b(null, a(“2 00 06_”; ”));
System.out.println(a(“5$8m 37$kw 17X|k”).concat(String.valueOf
➥(String.valueOf(b1.d()))));
if(flag)
b.c = !b.c;
}
private static String a(String s)
{
char ac[];
int i;
int j;
ac = s.toCharArray();
i = ac.length;
j = 0;
if(i > 1) goto _L2; else goto _L1
_L1:
ac;
j;
_L10:
JVM INSTR dup2 ;
JVM INSTR caload ;
j % 5;
JVM INSTR tableswitch 0 3: default 72
// 0 52
// 1 57
// 2 62
// 3 67;
goto _L3 _L4 _L5 _L6 _L7
_L4:
0x78;
goto _L8
_L5:
65;
goto _L8
_L6:
75;
goto _L8
_L7:
30;
goto _L8
_L3:
107;
_L8:
JVM INSTR ixor ;
(char);
JVM INSTR castore ;
j++;
if(i != 0) goto _L2; else goto _L9
_L9:
ac;
i;
goto _L10
_L2:
if(j >= i)
return new String(ac);
if(true) goto _L1; else goto _L11
_L11:
}
}
很多商业项目会用这招,为了保护代码。
即使使用了混淆器,可以保证源代码不会被反编译以后直接运用,但是我们可以通过反编译找出你源代码的逻辑或者创新点。这对研发代码的开发很不公平,那么像保护它,只能应用Patents(专利)。
Advanced obfuscators go further and change the control flow of Java code by restructuring the existing logic and inserting bogus code that will not execute.
Debug information is not needed to run the class but is used by debuggers to associate the bytecode with the source code.
When the debug information is stripped out, the names that were stored are lost, so decom-pilers have to generate their own names. In our case, after the stripping, sendMessage para-meter names would appear as s1and s2instead of host and message.
^*^
Encoding Java Strings
Even if class and method names are changed, the strings written by methods to a log file or console can betray the method purpose.In our case, ChatServer.sendMessageoutputs a trace message using the following:
System.out.println(“Sending message to host “ + host + “: “ + message);
String encoding is a powerful feature that should be provided by a commercial-strength obfuscator.
The best obfuscators are capable of transforming the execution flow of bytecode by inserting bogus conditional and goto statements. This can slow down the execution somewhat, but it might be a small price to pay for the increased protection of the IP. Listing 3.3 shows what sendMessage has become after all the transformations discussed earlier have been applied.
混淆前后比照
混淆前:
public void a(String s, String s1)
throws Exception
{
if(s == null || s.trim().length() == 0)
{
throw new Exception(“Please specify host name”);
} else
{
System.out.println(String.valueOf(String.valueOf((
new StringBuffer(“Sending message to host “)
).append(s).append(“: “).append(s1))));
String s2 = String.valueOf(String.valueOf((
new StringBuffer(“//”)).append(s).append(“:”)
.append(b).append(“/chatserver”)));
b b1 = (b)Naming.lookup(s2);
MessageInfo messageinfo = new MessageInfo(e, f);
b1.receiveMessage(s1, messageinfo);
System.out.println(“Message sent to host “.concat(
String.valueOf(String.valueOf(s))));
return;
}
}
混淆后:public void a(String s, String s1)
throws Exception
{
boolean flag = MessageInfo.c;
s;
if(flag) goto _L2; else goto _L1
_L1:
JVM INSTR ifnull 29;
goto _L3 _L4
_L3:
s.trim();
_L2:
if(flag) goto _L6; else goto _L5
_L5:
length();
JVM INSTR ifne 42;
goto _L4 _L7
_L4:
throw new Exception(a(“