Java是一種跨平台的
解釋型語言
Java 源代碼編譯中間
字節碼
存儲於class文件中
Class文件是一種字節碼形式的中間代碼
該字節碼中包括了很多源代碼的信息
例如變量名
方法名等
因此
Java中間代碼的反編譯就變得非常容易
目前市場上有許多免費的
商用的反編譯軟件
都能夠生成高質量的反編譯後的源代碼
所以
對開發人員來說
如何保護Java程序就變成了一個非常重要的挑戰
本文首先討論了保護Java程序的基本方法
然後對代碼混淆問題進行深入研究
最後結合一個實際的應用程序
分析如何在實踐中保護Java程序
反編譯成為保護Java程序的最大挑戰
通常C
C++等編程語言開發的程序都被編譯成目標代碼
這些目標代碼都是本機器的二進制可執行代碼
通常所有的源文件被編譯
鏈接成一個可執行文件
在這些可執行文件中
編譯器刪除了程序中的變量名稱
方法名稱等信息
這些信息往往是由內存地址表示
例如如果需要使用一個變量
往往是通過這個變量的地址來訪問的
因此
反編譯這些本地的目標代碼就是非常困難的
Java語言的出現
使得反編譯變得非常容易而有效
原因如下
由於跨平台的需求
Java的指令集比較簡單而通用
較容易得出程序的語義信息
Java編譯器將每一個類編譯成一個單獨的文件
這也簡化了反編譯的工作
Java 的Class文件中
仍然保留所有的方法名稱
變量名稱
並且通過這些名稱來訪問變量和方法
這些符號往往帶有許多語義信息
由於Java程序自身的特點
對於不經過處理的Java程序反編譯的效果非常好
目前
市場上有許多Java的反編譯工具
有免費的
也有商業使用的
還有的是開放源代碼的
這些工具的反編譯速度和效果都非常不錯
好的反編譯軟件
能夠反編譯出非常接近源代碼的程序
因此
通過反編譯器
黑客能夠對這些程序進行更改
或者復用其中的程序
因此
如何保護Java程序不被反編譯
是非常重要的一個問題
常用的保護技術
由於Java字節碼的抽象級別較高
因此它們較容易被反編譯
本節介紹了幾種常用的方法
用於保護Java字節碼不被反編譯
通常
這些方法不能夠絕對防止程序被反編譯
而是加大反編譯的難度而已
因為這些方法都有自己的使用環境和弱點
隔離Java程序
最簡單的方法就是讓用戶不能夠訪問到Java Class程序
這種方法是最根本的方法
具體實現有多種方式
例如
開發人員可以將關鍵的Java Class放在服務器端
客戶端通過訪問服務器的相關接口來獲得服務
而不是直接訪問Class文件
這樣黑客就沒有辦法反編譯Class文件
目前
通過接口提供服務的標准和協議也越來越多
例如 HTTP
Web Service
RPC等
但是有很多應用都不適合這種保護方式
例如對於單機運行的程序就無法隔離Java程序
這種保護方式見圖
所示
圖
隔離Java程序示意圖
對Class文件進行加密
為了防止Class文件被直接反編譯
許多開發人員將一些關鍵的Class文件進行加密
例如對注冊碼
序列號管理相關的類等
在使用這些被加密的類之前
程序首先需要對這些類進行解密
而後再將這些類裝載到JVM當中
這些類的解密可以由硬件完成
也可以使用軟件完成
在實現時
開發人員往往通過自定義ClassLoader類來完成加密類的裝載(注意由於安全性的原因
Applet不能夠支持自定義的ClassLoader)
自定義的ClassLoader首先找到加密的類
而後進行解密
最後將解密後的類裝載到JVM當中
在這種保護方式中
自定義的ClassLoader是非常關鍵的類
由於它本身不是被加密的
因此它可能成為黑客最先攻擊的目標
如果相關的解密密鑰和算法被攻克
那麼被加密的類也很容易被解密
這種保護方式示意圖見圖
圖
對Class文件進行加密示意圖
轉換成本地代碼
將程序轉換成本地代碼也是一種防止反編譯的有效方法
因為本地代碼往往難以被反編譯
開發人員可以選擇將整個應用程序轉換成本地代碼
也可以選擇關鍵模塊轉換
如果僅僅轉換關鍵部分模塊
Java程序在使用這些模塊時
需要使用JNI技術進行調用
當然
在使用這種技術保護Java程序的同時
也犧牲了Java的跨平台特性
對於不同的平台
我們需要維護不同版本的本地代碼
這將加重軟件支持和維護的工作
不過對於一些關鍵的模塊
有時這種方案往往是必要的
為了保證這些本地代碼不被修改和替代
通常需要對這些代碼進行數字簽名
在使用這些本地代碼之前
往往需要對這些本地代碼進行認證
確保這些代碼沒有被黑客更改
如果簽名檢查通過
則調用相關JNI方法
這種保護方式示意圖見圖
代碼混淆
圖
轉換成本地代碼示意圖
代碼混淆是對Class文件進行重新組織和處理
使得處理後的代碼與處理前代碼完成相同的功能(語義)
但是混淆後的代碼很難被反編譯
即反編譯後得出的代碼是非常難懂
晦澀的
因此反編譯人員很難得出程序的真正語義
從理論上來說
黑客如果有足夠的時間
被混淆的代碼仍然可能被破解
甚至目前有些人正在研制反混淆的工具
但是從實際情況來看
由於混淆技術的多元化發展
混淆理論的成熟
經過混淆的Java代碼還是能夠很好地防止反編譯
下面我們會詳細介紹混淆技術
因為混淆是一種保護Java程序的重要技術
圖
是代碼混淆的示意圖
圖
代碼混淆示意圖
幾種技術的總結
以上幾種技術都有不同的應用環境
各自都有自己的弱點
表
是相關特點的比較
混淆技術介紹
表
不同保護技術比較表
到目前為止
對於Java程序的保護
混淆技術還是最基本的保護方法
Java混淆工具也非常多
包括商業的
免費的
開放源代碼的
Sun公司也提供了自己的混淆工具
它們大多都是對Class文件進行混淆處理
也有少量工具首先對源代碼進行處理
然後再對Class進行處理
這樣加大了混淆處理的力度
目前
商業上比較成功的混淆工具包括JProof公司的
stBarrier系列
Eastridge公司的JShrink和的SourceGuard等
主要的混淆技術按照混淆目標可以進行如下分類
它們分別為符號混淆(Lexical Obfuscation)
數據混淆(Data Obfuscation)
控制混淆(Control Obfuscation)
預防性混淆(Prevent Transformation)
符號混淆
在Class中存在許多與程序執行本身無關的信息
例如方法名稱
變量名稱
這些符號的名稱往往帶有一定的含義
例如某個方法名為getKeyLength()
那麼這個方法很可能就是用來返回Key的長度
符號混淆就是將這些信息打亂
把這些信息變成無任何意義的表示
例如將所有的變量從vairant_
開始編號
對於所有的方法從method_
開始編號
這將對反編譯帶來一定的困難
對於私有函數
局部變量
通常可以改變它們的符號
而不影響程序的運行
但是對於一些接口名稱
公有函數
成員變量
如果有其它外部模塊需要引用這些符號
我們往往需要保留這些名稱
否則外部模塊找不到這些名稱的方法和變量
因此
多數的混淆工具對於符號混淆
都提供了豐富的選項
讓用戶選擇是否
如何進行符號混淆
數據混淆
圖
改變數據訪問
數據混淆是對程序使用的數據進行混淆
混淆的方法也有多種
主要可以分為改變數據存儲及編碼(Store and Encode Transform)
改變數據訪問(Access Transform)
改變數據存儲和編碼可以打亂程序使用的數據存儲方式
例如將一個有
個成員的數組
拆開為
個變量
並且打亂這些變量的名字
將一個兩維數組轉化為一個一維數組等
對於一些復雜的數據結構
我們將打亂它的數據結構
例如用多個類代替一個復雜的類等
另外一種方式是改變數據訪問
例如訪問數組的下標時
我們可以進行一定的計算
圖
就是一個例子
在實踐混淆處理中
這兩種方法通常是綜合使用的
在打亂數據存儲的同時
也打亂數據訪問的方式
經過對數據混淆
程序的語義變得復雜了
這樣增大了反編譯的難度
控制混淆
控制混淆就是對程序的控制流進行混淆
使得程序的控制流更加難以反編譯
通常控制流的改變需要增加一些額外的計算和控制流
因此在性能上會給程序帶來一定的負面影響
有時
需要在程序的性能和混淆程度之間進行權衡
控制混淆的技術最為復雜
技巧也最多
這些技術可以分為如下幾類
增加混淆控制 通過增加額外的
復雜的控制流
可以將程序原來的語義隱藏起來
例如
對於按次序執行的兩個語句A
B
我們可以增加一個控制條件
以決定B的執行
通過這種方式加大反匯編的難度
但是所有的干擾控制都不應該影響B的執行
圖
就給出三種方式
為這個例子增加混淆控制
圖
增加混淆控制的三種方式
控制流重組 重組控制流也是重要的混淆方法
例如
程序調用一個方法
在混淆後
可以將該方法代碼嵌入到調用程序當中
反過來
程
From:http://tw.wingwit.com/Article/program/Java/JSP/201311/19576.html