1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
public
static
void
main(String[] args) throws IOException {
Locale locale =
new
Locale(
"en"
,
"US"
);
Locale.setDefault(locale);
CommandLineParser parser =
new
PosixParser();
CommandLine commandLine;
try
{
commandLine = parser.parse(options, args);
}
catch
(ParseException ex) {
usage();
return
;
}
baksmaliOptions options =
new
baksmaliOptions();
boolean disassemble =
true
;
// 需要反编译
...
//中间有一部分获取命令行参数的代码,暂时省略
String[] remainingArgs = commandLine.getArgs();
Option[] clOptions = commandLine.getOptions();
...
//解析完成命令行参数
//首先判断机器cpu的个数,确定多个cpu能够同时工作,以提高解析效率
if
(options.jobs <= 0) {
options.jobs = Runtime.getRuntime().availableProcessors();
if
(options.jobs > 6) {
options.jobs = 6;
}
}
//判断api的版本号,当大于17的时候,设置检测包的私有访问属性
if
(options.apiLevel >= 17) {
options.checkPackagePrivateAccess =
true
;
}
String inputDexFileName = remainingArgs[0];
//打开目标文件
File dexFileFile =
new
File(inputDexFileName);
if
(!dexFileFile.exists()) {
System.err.println(
"Can't find the file "
+ inputDexFileName);
System.
exit
(1);
}
//Read in and parse the dex file
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.apiLevel);
// 重点1
//主要判断odex文件的一些代码,省略
...
//反汇编dex文件,生成一个又一个的smali文件
boolean errorOccurred =
false
;
if
(disassemble) {
errorOccurred = !baksmali.disassembleDexFile(dexFile, options);
// 重点2
}
if
(doDump) {
if
(dumpFileName == null) {
dumpFileName = commandLine.getOptionValue(inputDexFileName +
".dump"
);
}
dump.dump(dexFile, dumpFileName, options.apiLevel);
}
if
(errorOccurred) {
System.
exit
(1);
}
}
|
1
2
|
//Read in and parse the dex file
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.apiLevel);
// 重点1
|
1
|
errorOccurred = !baksmali.disassembleDexFile(dexFile, options);
// 重点2
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
public
static
DexBackedDexFile loadDexFile(File dexFile, @Nonnull Opcodes opcodes) throws IOException {
...
//首先判断文件是否为一个压缩文件,如果是的话解压缩后提取dex文件进行解析
InputStream inputStream =
new
BufferedInputStream(
new
FileInputStream(dexFile));
try
{
return
DexBackedDexFile.fromInputStream(opcodes, inputStream);
// 重点 1
}
catch
(DexBackedDexFile.NotADexFile ex) {
// just eat it
}
// Note: DexBackedDexFile.fromInputStream will reset inputStream back to the same position, if it fails
try
{
return
DexBackedOdexFile.fromInputStream(opcodes, inputStream);
}
catch
(DexBackedOdexFile.NotAnOdexFile ex) {
// just eat it
}
throw
new
ExceptionWithContext(
"%s is not an apk, dex file or odex file."
, dexFile.getPath());
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
public
static
DexBackedDexFile fromInputStream(@Nonnull Opcodes opcodes, @Nonnull InputStream is)
throws IOException {
if
(!is.markSupported()) {
throw
new
IllegalArgumentException(
"InputStream must support mark"
);
}
is.mark(44);
byte[] partialHeader =
new
byte[44];
try
{
ByteStreams.readFully(is, partialHeader);
}
catch
(EOFException ex) {
throw
new
NotADexFile(
"File is too short"
);
} finally {
is.reset();
}
//验证一下魔幻数和dex文件头部
verifyMagicAndByteOrder(partialHeader, 0);
byte[] buf = ByteStreams.toByteArray(is);
return
new
DexBackedDexFile(opcodes, buf, 0,
false
);
//继续跟踪下去
}
private
DexBackedDexFile(Opcodes opcodes, @Nonnull byte[] buf,
int
offset, boolean verifyMagic) {
super(buf);
this
.opcodes = opcodes;
if
(verifyMagic) {
verifyMagicAndByteOrder(buf, offset);
}
stringCount = readSmallUint(HeaderItem.STRING_COUNT_OFFSET);
stringStartOffset = readSmallUint(HeaderItem.STRING_START_OFFSET);
typeCount = readSmallUint(HeaderItem.TYPE_COUNT_OFFSET);
typeStartOffset = readSmallUint(HeaderItem.TYPE_START_OFFSET);
protoCount = readSmallUint(HeaderItem.PROTO_COUNT_OFFSET);
protoStartOffset = readSmallUint(HeaderItem.PROTO_START_OFFSET);
fieldCount = readSmallUint(HeaderItem.FIELD_COUNT_OFFSET);
fieldStartOffset = readSmallUint(HeaderItem.FIELD_START_OFFSET);
methodCount = readSmallUint(HeaderItem.METHOD_COUNT_OFFSET);
methodStartOffset = readSmallUint(HeaderItem.METHOD_START_OFFSET);
classCount = readSmallUint(HeaderItem.CLASS_COUNT_OFFSET);
classStartOffset = readSmallUint(HeaderItem.CLASS_START_OFFSET);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
public
static
boolean disassembleDexFile(DexFile dexFile, final baksmaliOptions options) {
...
//根据传入的文件夹路径创建文件夹
File outputDirectoryFile =
new
File(options.outputDirectory);
if
(!outputDirectoryFile.exists()) {
if
(!outputDirectoryFile.mkdirs()) {
System.err.println(
"Can't create the output directory "
+ options.outputDirectory);
return
false
;
}
}
//排序并生成dex文件中的所有 类定义实例到类定义的列表中
//sort the classes, so that if we're on a case-insensitive file system and need to handle classes with file
//name collisions, then we'll use the same name for each class, if the dex file goes through multiple
//baksmali/smali cycles for some reason. If a class with a colliding name is added or removed, the filenames
//may still change of course
List<? extends ClassDef> classDefs = Ordering.natural().sortedCopy(dexFile.getClasses());
// 重点1
if
(!options.noAccessorComments) {
options.syntheticAccessorResolver =
new
SyntheticAccessorResolver(classDefs);
}
//生成文件的扩展名,为.smali
final ClassFileNameHandler fileNameHandler =
new
ClassFileNameHandler(outputDirectoryFile,
".smali"
);
//根据 options.jobs 的值来生成处理 smali文件的线程数量
ExecutorService executor = Executors.newFixedThreadPool(options.jobs);
List<Future<Boolean>> tasks = Lists.newArrayList();
for
(final ClassDef classDef: classDefs) {
tasks.add(executor.submit(
new
Callable<Boolean>() {
@Override
public
Boolean call() throws Exception {
return
disassembleClass(classDef, fileNameHandler, options);
//回调的解析函数,重点2
}
}));
}
...
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public
Set<? extends DexBackedClassDef> getClasses() {
return
new
FixedSizeSet<DexBackedClassDef>() {
@Nonnull
@Override
public
DexBackedClassDef readItem(
int
index) {
return
new
DexBackedClassDef(DexBackedDexFile.
this
, getClassDefItemOffset(index));
}
@Override
public
int
size() {
return
classCount;
}
};
}
|
1
2
3
4
5
6
|
public
int
getClassDefItemOffset(
int
classIndex) {
if
(classIndex < 0 || classIndex >= classCount) {
throw
new
InvalidItemIndex(classIndex,
"Class index out of bounds: %d"
, classIndex);
}
return
classStartOffset + classIndex*ClassDefItem.ITEM_SIZE;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public
DexBackedClassDef(
@Nonnull
DexBackedDexFile dexFile,
int
classDefOffset) {
this
.dexFile = dexFile;
//classDefOffset 是这个类结构体在dex文件中的偏移地址。
this
.classDefOffset = classDefOffset;
//获取类的数据部分的偏移
int
classDataOffset = dexFile.readSmallUint(classDefOffset + ClassDefItem.CLASS_DATA_OFFSET);
if
(classDataOffset ==
0
) {
staticFieldsOffset = -
1
;
staticFieldCount =
0
;
instanceFieldCount =
0
;
directMethodCount =
0
;
virtualMethodCount =
0
;
}
else
{
//如果不等于0,则要读取各种变量,方法的个数 保存到这个类的私有成员变量中,等到实际解析的时候
//再来使用
DexReader reader = dexFile.readerAt(classDataOffset);
staticFieldCount = reader.readSmallUleb128();
instanceFieldCount = reader.readSmallUleb128();
directMethodCount = reader.readSmallUleb128();
virtualMethodCount = reader.readSmallUleb128();
staticFieldsOffset = reader.getOffset();
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
return
disassembleClass(classDef, fileNameHandler, options);
private
static
boolean
disassembleClass(ClassDef classDef, ClassFileNameHandler fileNameHandler,
baksmaliOptions options) {
//获取类名
String classDescriptor = classDef.getType();
//validate that the descriptor is formatted like we expect
if
(classDescriptor.charAt(
0
) !=
'L'
||
classDescriptor.charAt(classDescriptor.length()-
1
) !=
';'
) {
System.err.println(
"Unrecognized class descriptor - "
+ classDescriptor +
" - skipping class"
);
return
false
;
}
//生成相应要输入smali文件的位置信息
File smaliFile = fileNameHandler.getUniqueFilenameForClass(classDescriptor);
//create and initialize the top level string template
ClassDefinition classDefinition =
new
ClassDefinition(options, classDef);
// 重点1
//write the disassembly
Writer writer =
null
;
try
{
File smaliParent = smaliFile.getParentFile();
if
(!smaliParent.exists()) {
if
(!smaliParent.mkdirs()) {
// check again, it's likely it was created in a different thread
if
(!smaliParent.exists()) {
System.err.println(
"Unable to create directory "
+ smaliParent.toString() +
" - skipping class"
);
return
false
;
}
}
}
if
(!smaliFile.exists()){
if
(!smaliFile.createNewFile()) {
System.err.println(
"Unable to create file "
+ smaliFile.toString() +
" - skipping class"
);
return
false
;
}
}
BufferedWriter bufWriter =
new
BufferedWriter(
new
OutputStreamWriter(
new
FileOutputStream(smaliFile),
"UTF8"
));
writer =
new
IndentingWriter(bufWriter);
classDefinition.writeTo((IndentingWriter)writer);
//重点2
}
catch
(Exception ex) {
System.err.println(
"\n\nError occurred while disassembling class "
+ classDescriptor.replace(
'/'
,
'.'
) +
" - skipping class"
);
ex.printStackTrace();
// noinspection ResultOfMethodCallIgnored
smaliFile.delete();
return
false
;
}
finally
{
if
(writer !=
null
) {
try
{
writer.close();
}
catch
(Throwable ex) {
System.err.println(
"\n\nError occurred while closing file "
+ smaliFile.toString());
ex.printStackTrace();
}
}
}
return
true
;
}
|
1
2
3
4
5
|
public
ClassDefinition(
@Nonnull
baksmaliOptions options,
@Nonnull
ClassDef classDef) {
this
.options = options;
this
.classDef = classDef;
fieldsSetInStaticConstructor = findFieldsSetInStaticConstructor();
}
|
1
2
3
4
5
6
7
8
9
10
11
|
public
void
writeTo(IndentingWriter writer)
throws
IOException {
writeClass(writer);
writeSuper(writer);
writeSourceFile(writer);
writeInterfaces(writer);
writeAnnotations(writer);
Set<String> staticFields = writeStaticFields(writer);
writeInstanceFields(writer, staticFields);
Set<String> directMethods = writeDirectMethods(writer);
writeVirtualMethods(writer, directMethods);
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
|
private
Set<String> writeDirectMethods(IndentingWriter writer)
throws
IOException {
boolean
wroteHeader =
false
;
Set<String> writtenMethods =
new
HashSet<String>();
Iterable<?
extends
Method> directMethods;
if
(classDef
instanceof
DexBackedClassDef) {
directMethods = ((DexBackedClassDef)classDef).getDirectMethods(
false
);
//重点1
}
else
{
directMethods = classDef.getDirectMethods();
}
for
(Method method: directMethods) {
if
(!wroteHeader) {
writer.write(
"\n\n"
);
writer.write(
"# direct methods"
);
wroteHeader =
true
;
}
writer.write(
'\n'
);
...
MethodImplementation methodImpl = method.getImplementation();
if
(methodImpl ==
null
) {
MethodDefinition.writeEmptyMethodTo(methodWriter, method, options);
}
else
{
MethodDefinition methodDefinition =
new
MethodDefinition(
this
, method, methodImpl);
//重点2
methodDefinition.writeTo(methodWriter);
//重点3
}
}
return
writtenMethods;
}
这个函数有三个重点
directMethods = ((DexBackedClassDef)classDef).getDirectMethods(
false
);
//重点1
public
Iterable<?
extends
DexBackedMethod> getDirectMethods(
final
boolean
skipDuplicates) {
if
(directMethodCount >
0
) {
//首先得到这个类中的 direct 方法的在dex文件中的偏移地址
DexReader reader = dexFile.readerAt(getDirectMethodsOffset());
final
AnnotationsDirectory annotationsDirectory = getAnnotationsDirectory();
final
int
methodsStartOffset = reader.getOffset();
//返回 new Iterable<DexBackedMethod>()给上层的调用函数,并且继承实现了
//iterator() 这个函数
return
new
Iterable<DexBackedMethod>() {
@Nonnull
@Override
public
Iterator<DexBackedMethod> iterator() {
final
AnnotationsDirectory.AnnotationIterator methodAnnotationIterator =
annotationsDirectory.getMethodAnnotationIterator();
final
AnnotationsDirectory.AnnotationIterator parameterAnnotationIterator =
annotationsDirectory.getParameterAnnotationIterator();
//返回了 new VariableSizeLookaheadIterator<DexBackedMethod>(dexFile, methodsStartOffset)
//这个对象,里面继承实现了 readNextItem 这个方法,这个方法通过传入的 方法开始偏移,从
//dex文件中 返回 DexBackedMethod 这个对象给上层
return
new
VariableSizeLookaheadIterator<DexBackedMethod>(dexFile, methodsStartOffset) {
private
int
count;
@Nullable
private
MethodReference previousMethod;
private
int
previousIndex;
@Nullable
@Override
protected
DexBackedMethod readNextItem(
@Nonnull
DexReader reader) {
while
(
true
) {
if
(++count > directMethodCount) {
virtualMethodsOffset = reader.getOffset();
return
null
;
}
// 生成一个 method的对象
DexBackedMethod item =
new
DexBackedMethod(reader, DexBackedClassDef.
this
,
previousIndex, methodAnnotationIterator, parameterAnnotationIterator);
//重点1
MethodReference currentMethod = previousMethod;
MethodReference nextMethod = ImmutableMethodReference.of(item);
previousMethod = nextMethod;
previousIndex = item.methodIndex;
if
(skipDuplicates && currentMethod !=
null
&& currentMethod.equals(nextMethod)) {
continue
;
}
return
item;
}
}
};
}
};
}
else
{
if
(directMethodsOffset >
0
) {
virtualMethodsOffset = directMethodsOffset;
}
return
ImmutableSet.of();
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
public
DexBackedMethod(
@Nonnull
DexReader reader,
@Nonnull
DexBackedClassDef classDef,
int
previousMethodIndex,
@Nonnull
AnnotationsDirectory.AnnotationIterator methodAnnotationIterator,
@Nonnull
AnnotationsDirectory.AnnotationIterator paramaterAnnotationIterator) {
this
.dexFile = reader.dexBuf;
this
.classDef = classDef;
// large values may be used for the index delta, which cause the cumulative index to overflow upon
// addition, effectively allowing out of order entries.
int
methodIndexDiff = reader.readLargeUleb128();
this
.methodIndex = methodIndexDiff + previousMethodIndex;
this
.accessFlags = reader.readSmallUleb128();
this
.codeOffset = reader.readSmallUleb128();
this
.methodAnnotationSetOffset = methodAnnotationIterator.seekTo(methodIndex);
this
.parameterAnnotationSetListOffset = paramaterAnnotationIterator.seekTo(methodIndex);
}
|
1
2
3
4
5
6
7
|
MethodImplementation methodImpl = method.getImplementation();
public
DexBackedMethodImplementation getImplementation() {
if
(codeOffset >
0
) {
return
new
DexBackedMethodImplementation(dexFile,
this
, codeOffset);
}
return
null
;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
MethodDefinition methodDefinition =
new
MethodDefinition(
this
, method, methodImpl);
public
MethodDefinition(
@Nonnull
ClassDefinition classDef,
@Nonnull
Method method,
@Nonnull
MethodImplementation methodImpl) {
this
.classDef = classDef;
this
.method = method;
this
.methodImpl = methodImpl;
//这里传入的method其实是 DexBackedMethod
try
{
//TODO: what about try/catch blocks inside the dead code? those will need to be commented out too. ugh.
//methodImpl.getInstructions() 其实是调用的是 public Iterable<? extends Instruction> getInstructions()
// 在 DexBackedMethodImplementation 这个类中实现的,主要是根据前面的偏移从dex文件中读取相应的指令数据
//放在指令列表中
instructions = ImmutableList.copyOf(methodImpl.getInstructions());
methodParameters = ImmutableList.copyOf(method.getParameters());
packedSwitchMap =
new
SparseIntArray(
0
);
sparseSwitchMap =
new
SparseIntArray(
0
);
instructionOffsetMap =
new
InstructionOffsetMap(instructions);
for
(
int
i=
0
; i<instructions.size(); i++) {
Instruction instruction = instructions.get(i);
//处理switch case 指令
Opcode opcode = instruction.getOpcode();
if
(opcode == Opcode.PACKED_SWITCH) {
boolean
valid =
true
;
int
codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
int
targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
try
{
targetOffset = findSwitchPayload(targetOffset, Opcode.PACKED_SWITCH_PAYLOAD);
}
catch
(InvalidSwitchPayload ex) {
valid =
false
;
}
if
(valid) {
packedSwitchMap.append(targetOffset, codeOffset);
}
}
else
if
(opcode == Opcode.SPARSE_SWITCH) {
boolean
valid =
true
;
int
codeOffset = instructionOffsetMap.getInstructionCodeOffset(i);
int
targetOffset = codeOffset + ((OffsetInstruction)instruction).getCodeOffset();
try
{
targetOffset = findSwitchPayload(targetOffset, Opcode.SPARSE_SWITCH_PAYLOAD);
}
catch
(InvalidSwitchPayload ex) {
valid =
false
;
// The offset to the payload instruction was invalid. Nothing to do, except that we won't
// add this instruction to the map.
}
if
(valid) {
sparseSwitchMap.append(targetOffset, codeOffset);
}
}
}
}
catch
(Exception ex) {
String methodString;
try
{
methodString = ReferenceUtil.getMethodDescriptor(method);
}
catch
(Exception ex2) {
throw
ExceptionWithContext.withContext(ex,
"Error while processing method"
);
}
throw
ExceptionWithContext.withContext(ex,
"Error while processing method %s"
, methodString);
}
}
|
1
2
3
4
5
6
7
8
|
for
(MethodParameter parameter: methodParameters) {
String type = parameter.getType();
writer.write(type);
parameterRegisterCount++;
if
(TypeUtils.isWideType(type)) {
parameterRegisterCount++;
}
}
|