ProGuard源碼下載地址
https://sourceforge.net/projects/proguard/
整個PG.jar的編譯腳本在buildscripts/functions.sh
這里不關(guān)心他們的編譯處理
核心處理類在core包下啡莉。
PG.core的包結(jié)構(gòu)
proguard 用戶調(diào)用接口
proguard.ant ant邏輯相關(guān)
proguard.classfile class處理
proguard.evaluation 執(zhí)行相關(guān)
proguard.gui gui界面
proguard.io IO流,處理類似jar和zip,classpath文件流
proguard.obfuscate 混淆相關(guān)
proguard.optimize 優(yōu)化
proguard.preverify 預(yù)編譯相關(guān)
proguard.retrace 追蹤
proguard.shrink 壓縮相關(guān)
proguard.util 工具類
proguard.java的入口方法
public static void main(String[] args)
{
if (args.length == 0)
{
System.out.println(VERSION);
System.out.println("Usage: java proguard.ProGuard [options ...]");
System.exit(1);
}
// Create the default options.
Configuration configuration = new Configuration();
try
{
// Parse the options specified in the command line arguments.
ConfigurationParser parser = new ConfigurationParser(args,
System.getProperties());
try
{
//檢查configuration有沒有異常的字符恨搓,只有全對才能繼續(xù)
parser.parse(configuration);
}
finally
{
parser.close();
}
// Execute ProGuard with these options.
new ProGuard(configuration).execute();//真正的實(shí)現(xiàn)方法
}
catch (Exception ex)
{
if (configuration.verbose)
{
// Print a verbose stack trace.
ex.printStackTrace();
}
else
{
// Print just the stack trace message.
System.err.println("Error: "+ex.getMessage());
}
System.exit(1);
}
System.exit(0);
}
}
main方法主要在做configuration的輸入檢查工作
當(dāng)檢查結(jié)果無異常,新構(gòu)造一個Proguard類并調(diào)用excute方法執(zhí)行
excute方法這里著重看obfuscate方法
private void obfuscate() throws IOException
{
if (configuration.verbose)
{
System.out.println("Obfuscating...");
// We'll apply a mapping, if requested.
if (configuration.applyMapping != null)
{
System.out.println("Applying mapping [" + PrintWriterUtil.fileName(configuration.applyMapping) + "]");
}
// We'll print out the mapping, if requested.
if (configuration.printMapping != null)
{
System.out.println("Printing mapping to [" + PrintWriterUtil.fileName(configuration.printMapping) + "]...");
}
}
// Perform the actual obfuscation.
new Obfuscator(configuration).execute(programClassPool,
libraryClassPool);
}
Obfuscator類是真正做混淆處理的類,比如這里的成員混淆者M(jìn)emberObfuscator
NameFactory nameFactory = new SimpleNameFactory();
if (configuration.obfuscationDictionary != null)
{
nameFactory =
new DictionaryNameFactory(configuration.obfuscationDictionary,
nameFactory);
}
WarningPrinter warningPrinter = new WarningPrinter(System.err, configuration.warn);
// Maintain a map of names to avoid [descriptor - new name - old name].
Map descriptorMap = new HashMap();
// Do the class member names have to be globally unique?
if (configuration.useUniqueClassMemberNames)
{
// Collect all member names in all classes.
programClassPool.classesAccept(
new AllMemberVisitor(
new MemberNameCollector(configuration.overloadAggressively,
descriptorMap)));
// Assign new names to all members in all classes.
programClassPool.classesAccept(
new AllMemberVisitor(
new MemberObfuscator(configuration.overloadAggressively,
nameFactory,
descriptorMap)));
其初始化完畢后囤萤,內(nèi)部循環(huán)調(diào)用nameFactory.nextName方法進(jìn)行混淆
String newName = newMemberName(member);
// Assign a new one, if necessary.
if (newName == null)
{
// Find an acceptable new name.
nameFactory.reset();
do
{
newName = nameFactory.nextName();
}
while (nameMap.containsKey(newName));
// Remember not to use the new name again in this name space.
nameMap.put(newName, name);
// Assign the new name.
setNewMemberName(member, newName);
}
混淆規(guī)則就在諸如SimpleNameFactory類的工廠類里。
public String nextName() {
return name(index++);
}
private String name(int index) {
// Which cache do we need?
List cachedNames = generateMixedCaseNames ?
cachedMixedCaseNames :
cachedLowerCaseNames;
// Do we have the name in the cache?
if (index < cachedNames.size()) {
return (String) cachedNames.get(index);
}
// Create a new name and cache it.
String name = newName(index);
cachedNames.add(index, name);
return name;
}
/**
* Creates and returns the name at the given index.
*/
private String newName(int index) {
// If we're allowed to generate mixed-case names, we can use twice as
// many characters.
int totalCharacterCount = generateMixedCaseNames ?
2 * CHARACTER_COUNT :
CHARACTER_COUNT;
int baseIndex = index / totalCharacterCount;
System.out.println(baseIndex);
int offset = index % totalCharacterCount;
System.out.println(offset);
char newChar = charAt(offset);
String newName = baseIndex == 0 ?
new String(new char[]{newChar}) :
(name(baseIndex - 1) + newChar);
return newName;
//修改后的方法
// String newStr = stringAt(offset);
// String newStrName = baseIndex == 0 ? new String(newStr) : (name(baseIndex - 1) + newStr);
// return newStrName;
}
/**
* Returns the character with the given index, between 0 and the number of
* acceptable characters.
*/
private char charAt(int index) {
return (char) ((index < CHARACTER_COUNT ?
// 'o' : 'O'));//修改
'a' - 0 : 'A' - CHARACTER_COUNT) + index);
}
/**
* 隨機(jī)產(chǎn)生五個字符串內(nèi)容
*
* @param index
* @return
*/
private String stringAt(int index) {
// return new String(new char[]{
// '談', '笑', '風(fēng)', '聲', '蛤'
// });
return new String(new char[]{
(char) (CHARACTER_START + index),
(char) (CHARACTER_START + 1 + index),
(char) (CHARACTER_START + 2 + index),
(char) (CHARACTER_START + 3 + index),
(char) (CHARACTER_START + 4 + index)
});
}
混淆就是用簡單字符替換原有字符
這里值得注意的是逆巍,string因?yàn)槭莻€不可變類惧辈,初始化后就放在字符串池里,完全可以復(fù)用铆遭,PG也是這樣做的硝桩。
在name方法里,generateMixedCaseNames是其構(gòu)造方法傳來的枚荣,
用處是判斷:拿大小寫混合字符緩存池碗脊,還是純小寫字符緩存池。
說白了就是混淆用大小寫橄妆,還是純小寫衙伶。
通過index判斷,之前有沒有new過該string害碾,有的話就直接從緩存池拿矢劲,否則就new,并且放入緩存池慌随。