我們都知道JAVA是一種解析型語言,這就決定JAVA文件編譯后不是機(jī)器碼,而是一個(gè)字節(jié)碼文件,也就是CLASS文件。而這樣的文件是存在規(guī)律的,經(jīng)過反編譯工具是可以還原回來的。例如Decafe、FrontEnd,YingJAD和Jode等等軟件。下面是《Nokia中Short數(shù)組轉(zhuǎn)換算法》
thread.jspa?threadID=872&tstart=0
類中Main函數(shù)的ByteCode:
0?ldc?#16?
2?invokestatic?#18?
5?astore_1
6?return
其源代碼是:short?[]?pixels?=?parseImage(\"/ef1s.png\");
我們通過反編譯工具是可以還原出以上源代碼的。而通過簡(jiǎn)單的分析,我們也能自己寫出源代碼的。
第一行:ldc?#16?
ldc為虛擬機(jī)的指令,作用是:壓入常量池的項(xiàng),形式如下
ldc?index
這個(gè)index就是上面的16,也就是在常量池中的有效索引,當(dāng)我們?nèi)タ闯A砍氐臅r(shí)候,我們就會(huì)找到index為16的值為String_info,里面存了/ef1s.png.
所以這行的意思就是把/ef1s.pn作為一個(gè)String存在常量池中,其有效索引為16。
第二行:2?invokestatic?#18?
invokestatic為虛擬機(jī)指令,作用是:調(diào)用類(static)方法,形式如下
invokestatic?indexbyte1?indexbyte2
其中indexbyte1和indexbyte2必須是在常量池中的有效索引,而是指向的類型必須有Methodref標(biāo)記,對(duì)類名,方法名和方法的描述符的引用。
所以當(dāng)我們看常量池中索引為18的地方,我們就會(huì)得到以下信息:
Class?Name?:?cp_info#1?
Name?Type?:?cp_info#19?
1?和19都是常量池中的有效索引,值就是右邊<>中的值,再往下跟蹤我就不多說了,有興趣的朋友可以去JAVA虛擬機(jī)規(guī)范。
這里我簡(jiǎn)單介紹一下parseImage(Ljava/lang/String;)[S?的意思。
這就是parseImage這個(gè)函數(shù)的運(yùn)行,我們反過來看看parseImage的原型就明白了
short?[]?parseImage(String)
那么Ljava/lang/String;就是說需要傳入一個(gè)String對(duì)象,而為什么前面要有一個(gè)L呢,這是JAVA虛擬機(jī)用來表示這是一個(gè)Object。如果是基本類型,這里就不需要有L了。然后返回為short的一維數(shù)組,也就是對(duì)應(yīng)的[S。是不是很有意思,S對(duì)應(yīng)著Short類型,而“[”對(duì)應(yīng)一維數(shù)組,那有些朋友要問了,兩維呢,那就“[[”,呵呵,是不是很有意思。
好了,調(diào)用了函數(shù),返回的值要保存下來吧。那么就是第三行要做的事情了。
第三行:5?astore_1
呵呵,很簡(jiǎn)單的。但是卻有文章,也是比較容易混亂的地方。
astore_為虛擬機(jī)指令,作用為:將當(dāng)前reference存儲(chǔ)到局部變量中去。而必須是對(duì)當(dāng)前框架的局部變量的有效索引。打個(gè)比方,可能我們這個(gè)函數(shù)中可能還要用到這個(gè)局部變量,我們可以通過來找到它。例如調(diào)用虛擬機(jī)指令:
aload_1,就能得到該值。
第四行:6?return
同樣的,return也是虛擬機(jī)指令了,它的作用為:從方法返回void。
這里也就是退出main函數(shù)。
----------------------------------------------------------------------------
ok,終于啰嗦完畢了。有些朋友可能要問,這么復(fù)雜,才四行就說這么多,呵呵,可能是我這人廢話過多,當(dāng)然如果你熟悉了,一點(diǎn)就能看懂了。通過肉眼就可以反編譯程序了。目前所有的反編譯工具都無法做到完美反編譯,在有問題的地方還需要人去修正。
好了,說了半天如何反編譯,我們就來看看如果在你的程序如果防止別人來反編譯。好不容易寫好的程序被人反編譯了,多郁悶。哈哈。工欲善其事,必先利其器,這句話用對(duì)了嗎?
什么混淆等等的方法,我就不說了,我這里主要是要說一種通過添加代碼來在某種程度來避免當(dāng)前流行的反編譯工具對(duì)你的代碼進(jìn)行反編譯。
方案一。
1,首先要添加一個(gè)參數(shù)為Exception類型的函數(shù),例如這樣。
public?static?void?Fake(Exception?e)
{
e.toString();
}
一定要有e.toString();,因?yàn)橐乐鼓愕幕煜靼褵o用的代碼過濾。
2,然后在每個(gè)類中調(diào)用這個(gè)函數(shù),放在try...catch(Exception?e)..中的catch里面,例如:
try
{
...
}
catch?(Exception?e)
{
Fake(e);
}
請(qǐng)注意?,一定要放在catch才有用,其他地方無用。
方案二。
如果以上方法還不夠?qū)I(yè),我們?cè)賮硪粋€(gè)。呵呵~
1,同樣的,我們定義一個(gè)類,這個(gè)類叫做AntiCrack.。名字好像有點(diǎn)大。。。代碼如下:
public?class?AntiCrack
{
private?AntiCrack()
{
}
public?static?Throwable?Fake(Throwable?throwable,?Throwable?throwable1)
{
try
{
throwable.getClass().getMethod(\"initCause\",?new?Class[]?{
java.lang.Throwable.class
}).invoke(throwable,?new?Object[]?{
throwable1
});
}
catch(Exception?exception)?{?}
return?throwable;
}
}
2,同樣的,我們?cè)赾atch里面調(diào)用該函數(shù)。例如如下。
try
{
//your?code?here?
}
catch(IOException?ioexception)
{
IllegalArgumentException?illegalargumentexception?=?new?IllegalArgumentException(ioexception.toString());
AntiCrack.fake(illegalargumentexception,?ioexception);
throw?illegalargumentexception;
}
或者也可以這樣
public?class?AntiException?extends?Exception
{
public?AntiException()
{
}
public?AntiException(String?s)
{
super(s);
}
public?AntiException(String?s,?Throwable?throwable)
{
super(s);
AntiCrack.fake(this,?throwable);
}
}
然后在你的程序里面?
try
{
}
catch(IoException?e)
{
throw?new?AntiException(ioexception.toString(),?ioexception);
}
當(dāng)采用以上方式后,任何類只要調(diào)用了該函數(shù),生成的class反編譯后出錯(cuò),得不到結(jié)果。
Decafe、FrontEnd和YingJAD,反編譯時(shí)都有exception,然后無法進(jìn)行下去。大家可以多測(cè)試變得反編譯工具。建議推薦用第二個(gè)方法。
thread.jspa?threadID=872&tstart=0
類中Main函數(shù)的ByteCode:
0?ldc?#16?
2?invokestatic?#18?
5?astore_1
6?return
其源代碼是:short?[]?pixels?=?parseImage(\"/ef1s.png\");
我們通過反編譯工具是可以還原出以上源代碼的。而通過簡(jiǎn)單的分析,我們也能自己寫出源代碼的。
第一行:ldc?#16?
ldc為虛擬機(jī)的指令,作用是:壓入常量池的項(xiàng),形式如下
ldc?index
這個(gè)index就是上面的16,也就是在常量池中的有效索引,當(dāng)我們?nèi)タ闯A砍氐臅r(shí)候,我們就會(huì)找到index為16的值為String_info,里面存了/ef1s.png.
所以這行的意思就是把/ef1s.pn作為一個(gè)String存在常量池中,其有效索引為16。
第二行:2?invokestatic?#18?
invokestatic為虛擬機(jī)指令,作用是:調(diào)用類(static)方法,形式如下
invokestatic?indexbyte1?indexbyte2
其中indexbyte1和indexbyte2必須是在常量池中的有效索引,而是指向的類型必須有Methodref標(biāo)記,對(duì)類名,方法名和方法的描述符的引用。
所以當(dāng)我們看常量池中索引為18的地方,我們就會(huì)得到以下信息:
Class?Name?:?cp_info#1?
Name?Type?:?cp_info#19?
1?和19都是常量池中的有效索引,值就是右邊<>中的值,再往下跟蹤我就不多說了,有興趣的朋友可以去JAVA虛擬機(jī)規(guī)范。
這里我簡(jiǎn)單介紹一下parseImage(Ljava/lang/String;)[S?的意思。
這就是parseImage這個(gè)函數(shù)的運(yùn)行,我們反過來看看parseImage的原型就明白了
short?[]?parseImage(String)
那么Ljava/lang/String;就是說需要傳入一個(gè)String對(duì)象,而為什么前面要有一個(gè)L呢,這是JAVA虛擬機(jī)用來表示這是一個(gè)Object。如果是基本類型,這里就不需要有L了。然后返回為short的一維數(shù)組,也就是對(duì)應(yīng)的[S。是不是很有意思,S對(duì)應(yīng)著Short類型,而“[”對(duì)應(yīng)一維數(shù)組,那有些朋友要問了,兩維呢,那就“[[”,呵呵,是不是很有意思。
好了,調(diào)用了函數(shù),返回的值要保存下來吧。那么就是第三行要做的事情了。
第三行:5?astore_1
呵呵,很簡(jiǎn)單的。但是卻有文章,也是比較容易混亂的地方。
astore_為虛擬機(jī)指令,作用為:將當(dāng)前reference存儲(chǔ)到局部變量中去。而必須是對(duì)當(dāng)前框架的局部變量的有效索引。打個(gè)比方,可能我們這個(gè)函數(shù)中可能還要用到這個(gè)局部變量,我們可以通過來找到它。例如調(diào)用虛擬機(jī)指令:
aload_1,就能得到該值。
第四行:6?return
同樣的,return也是虛擬機(jī)指令了,它的作用為:從方法返回void。
這里也就是退出main函數(shù)。
----------------------------------------------------------------------------
ok,終于啰嗦完畢了。有些朋友可能要問,這么復(fù)雜,才四行就說這么多,呵呵,可能是我這人廢話過多,當(dāng)然如果你熟悉了,一點(diǎn)就能看懂了。通過肉眼就可以反編譯程序了。目前所有的反編譯工具都無法做到完美反編譯,在有問題的地方還需要人去修正。
好了,說了半天如何反編譯,我們就來看看如果在你的程序如果防止別人來反編譯。好不容易寫好的程序被人反編譯了,多郁悶。哈哈。工欲善其事,必先利其器,這句話用對(duì)了嗎?
什么混淆等等的方法,我就不說了,我這里主要是要說一種通過添加代碼來在某種程度來避免當(dāng)前流行的反編譯工具對(duì)你的代碼進(jìn)行反編譯。
方案一。
1,首先要添加一個(gè)參數(shù)為Exception類型的函數(shù),例如這樣。
public?static?void?Fake(Exception?e)
{
e.toString();
}
一定要有e.toString();,因?yàn)橐乐鼓愕幕煜靼褵o用的代碼過濾。
2,然后在每個(gè)類中調(diào)用這個(gè)函數(shù),放在try...catch(Exception?e)..中的catch里面,例如:
try
{
...
}
catch?(Exception?e)
{
Fake(e);
}
請(qǐng)注意?,一定要放在catch才有用,其他地方無用。
方案二。
如果以上方法還不夠?qū)I(yè),我們?cè)賮硪粋€(gè)。呵呵~
1,同樣的,我們定義一個(gè)類,這個(gè)類叫做AntiCrack.。名字好像有點(diǎn)大。。。代碼如下:
public?class?AntiCrack
{
private?AntiCrack()
{
}
public?static?Throwable?Fake(Throwable?throwable,?Throwable?throwable1)
{
try
{
throwable.getClass().getMethod(\"initCause\",?new?Class[]?{
java.lang.Throwable.class
}).invoke(throwable,?new?Object[]?{
throwable1
});
}
catch(Exception?exception)?{?}
return?throwable;
}
}
2,同樣的,我們?cè)赾atch里面調(diào)用該函數(shù)。例如如下。
try
{
//your?code?here?
}
catch(IOException?ioexception)
{
IllegalArgumentException?illegalargumentexception?=?new?IllegalArgumentException(ioexception.toString());
AntiCrack.fake(illegalargumentexception,?ioexception);
throw?illegalargumentexception;
}
或者也可以這樣
public?class?AntiException?extends?Exception
{
public?AntiException()
{
}
public?AntiException(String?s)
{
super(s);
}
public?AntiException(String?s,?Throwable?throwable)
{
super(s);
AntiCrack.fake(this,?throwable);
}
}
然后在你的程序里面?
try
{
}
catch(IoException?e)
{
throw?new?AntiException(ioexception.toString(),?ioexception);
}
當(dāng)采用以上方式后,任何類只要調(diào)用了該函數(shù),生成的class反編譯后出錯(cuò),得不到結(jié)果。
Decafe、FrontEnd和YingJAD,反編譯時(shí)都有exception,然后無法進(jìn)行下去。大家可以多測(cè)試變得反編譯工具。建議推薦用第二個(gè)方法。