ProGuard源码下载地址
https://sourceforge.net/projects/proguard/
整个PG.jar的编译脚本在buildscripts/functions.sh
这里不关心他们的编译处理
核心处理类在core包下。
PG.core的包结构
proguard 用户调用接口
proguard.ant ant逻辑相关
proguard.classfile class处理
proguard.evaluation 执行相关
proguard.gui gui界面
proguard.io IO流,处理类似jar和zip,classpath文件流
proguard.obfuscate 混淆相关
proguard.optimize 优化
proguard.preverify 预编译相关
proguard.retrace 追踪
proguard.shrink 压缩相关
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有没有异常的字符,只有全对才能继续
parser.parse(configuration);
}
finally
{
parser.close();
}
// Execute ProGuard with these options.
new ProGuard(configuration).execute();//真正的实现方法
}
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的输入检查工作
当检查结果无异常,新构造一个Proguard类并调用excute方法执行
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类是真正做混淆处理的类,比如这里的成员混淆者MemberObfuscator
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)));
其初始化完毕后,内部循环调用nameFactory.nextName方法进行混淆
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);
}
混淆规则就在诸如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);
}
/**
* 随机产生五个字符串内容
*
* @param index
* @return
*/
private String stringAt(int index) {
// return new String(new char[]{
// '谈', '笑', '风', '声', '蛤'
// });
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因为是个不可变类,初始化后就放在字符串池里,完全可以复用,PG也是这样做的。
在name方法里,generateMixedCaseNames是其构造方法传来的,
用处是判断:拿大小写混合字符缓存池,还是纯小写字符缓存池。
说白了就是混淆用大小写,还是纯小写。
通过index判断,之前有没有new过该string,有的话就直接从缓存池拿,否则就new,并且放入缓存池。
网友评论