Skip to content

Commit e94712f

Browse files
committed
#11 DEX format - parse items
1 parent 92b6658 commit e94712f

File tree

19 files changed

+1043
-119
lines changed

19 files changed

+1043
-119
lines changed

CommonLib/src/main/java/org/freeinternals/commonlib/core/BytesTool.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,16 @@ public static String getBinaryString(int i){
3737
return String.format("%16s", Integer.toBinaryString(i)).replace(' ', '0');
3838
}
3939

40+
/**
41+
* Get binary string with leading zero.
42+
*
43+
* @param l Long value to convert
44+
* @return Binary string with leading zero
45+
*/
46+
public static String getBinaryString(long l){
47+
return String.format("%32s", Long.toBinaryString(l)).replace(' ', '0');
48+
}
49+
4050
/**
4151
* Get a string for the {@code hex} view of byte array {@code data}.
4252
*

CommonLib/src/main/java/org/freeinternals/commonlib/ui/UITool.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public final class UITool {
3030
public static final float POPUP_RATIO = 0.8f;
3131
/**
3232
* Max length for tree node string.
33-
*
33+
*
3434
* @see #left(String)
3535
*/
3636
public static final int TREENODE_STRING_MAXLEN = 30;
@@ -71,6 +71,15 @@ private static Icon icon(String url) {
7171
return iconCache.computeIfAbsent(url, k -> new ImageIcon(UITool.class.getResource(url)));
7272
}
7373

74+
/**
75+
* Icon for annotations.
76+
*
77+
* @return Icon for annotations
78+
*/
79+
public static Icon icon4Annotations() {
80+
return icon("/image/icons8-bookmark-16.png");
81+
}
82+
7483
/**
7584
* Icon for binary file.
7685
*
@@ -103,7 +112,7 @@ public static Icon icon4Checksum() {
103112
public static Icon icon4Counter() {
104113
return icon("/image/icons8-abacus-16.png");
105114
}
106-
115+
107116
/**
108117
* Icon for raw Data.
109118
*
Loading

FormatCLASS/src/main/java/org/freeinternals/format/classfile/AccessFlag.java

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import java.util.ArrayList;
1010
import java.util.Collections;
1111
import java.util.List;
12+
import org.freeinternals.commonlib.core.BytesTool;
1213
import org.freeinternals.format.classfile.attribute.InnerClasses_attribute;
1314
import org.freeinternals.format.classfile.attribute.MethodParameters_attribute;
1415
import org.freeinternals.format.classfile.attribute.Module_attribute;
@@ -148,12 +149,22 @@ public enum AccessFlag {
148149
* Indicates that this opening was implicitly declared in the source of the
149150
* module declaration.
150151
*/
151-
ACC_MANDATED(0x8000, "ACC_MANDATED");
152+
ACC_MANDATED( 0x8000, "ACC_MANDATED"),
153+
/**
154+
* Constructor method (class or instance initializer).
155+
* Used by Android dex.
156+
*/
157+
ACC_CONSTRUCTOR(0x10000, "ACC_CONSTRUCTOR"),
158+
/**
159+
* Declared <code>synchronized</code>.
160+
* Used by Android dex.
161+
*/
162+
ACC_DECLARED_SYNCHRONIZED(0x20000, "ACC_DECLARED_SYNCHRONIZED");
152163

153164
/**
154165
* Binary value in the {@link ClassFile}.
155166
*/
156-
public final int value;
167+
public final long value;
157168

158169
/**
159170
* Modifier in the java source file. Some modifier does not exist in the
@@ -279,7 +290,7 @@ public enum AccessFlag {
279290
* @param i Value in the Class file
280291
* @param m Modifier in the java source file
281292
*/
282-
AccessFlag(int i, String m) {
293+
private AccessFlag(long i, String m) {
283294
this.value = i;
284295
this.modifier = m;
285296
}
@@ -291,17 +302,22 @@ public enum AccessFlag {
291302
* @return <code>true</code> if the access flag matches the
292303
* <code>accFlags</code>, else <code>false</code>
293304
*/
294-
public boolean match(int accFlags) {
305+
public boolean match(long accFlags) {
295306
return (accFlags & this.value) > 0;
296307
}
297308

309+
@Override
310+
public String toString() {
311+
return String.format("%s value=%s modifier=%s", name(), BytesTool.getBinaryString(this.value), this.modifier);
312+
}
313+
298314
/**
299315
* Get the modifiers text for a {@link ClassFile}.
300316
*
301317
* @param value Value in the Class file
302318
* @return Modifier text
303319
*/
304-
public static String getClassModifier(int value) {
320+
public static String getClassModifier(long value) {
305321
return getModifier(value, AccessFlag.ForClass);
306322
}
307323

@@ -311,7 +327,7 @@ public static String getClassModifier(int value) {
311327
* @param value Value in the Class file
312328
* @return Modifier text
313329
*/
314-
public static String getFieldModifier(int value) {
330+
public static String getFieldModifier(long value) {
315331
return getModifier(value, AccessFlag.ForField);
316332
}
317333

@@ -321,7 +337,7 @@ public static String getFieldModifier(int value) {
321337
* @param value Value in the Class file
322338
* @return Modifier text
323339
*/
324-
public static String getMethodModifier(int value) {
340+
public static String getMethodModifier(long value) {
325341
return getModifier(value, AccessFlag.ForMethod);
326342
}
327343

@@ -331,7 +347,7 @@ public static String getMethodModifier(int value) {
331347
* @param value Value in the Class file
332348
* @return Modifier text
333349
*/
334-
public static String getInnerClassModifier(int value) {
350+
public static String getInnerClassModifier(long value) {
335351
return getModifier(value, AccessFlag.ForInnerClass);
336352
}
337353

@@ -341,20 +357,16 @@ public static String getInnerClassModifier(int value) {
341357
* @param value Value in the Class file
342358
* @return Modifier text
343359
*/
344-
public static String getMethodParametersModifier(int value) {
360+
public static String getMethodParametersModifier(long value) {
345361
return getModifier(value, AccessFlag.ForMethodParameters);
346362
}
347363

348-
public static String getModifier(int value, List<AccessFlag> list) {
349-
final StringBuilder sb = new StringBuilder(25);
350-
351-
for (AccessFlag af : list) {
352-
if (af.match(value)) {
353-
sb.append(af.modifier);
354-
sb.append(" ");
355-
}
356-
}
364+
public static String getModifier(long value, List<AccessFlag> list) {
365+
final List<String> modifiers = new ArrayList<>();
366+
list.stream().filter(flag -> (flag.match(value))).forEachOrdered(flag -> {
367+
modifiers.add(flag.modifier);
368+
});
357369

358-
return sb.toString();
370+
return String.join(" ", modifiers);
359371
}
360372
}

FormatDEX/src/main/java/org/freeinternals/format/dex/DexFile.java

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
* </pre>
4444
*/
4545
@SuppressWarnings({"java:S100", "java:S116", "java:S1104"})
46-
public class DexFile extends FileFormat {
46+
public final class DexFile extends FileFormat {
4747

4848
/**
4949
* The constant NO_INDEX is used to indicate that an index value is absent.
@@ -242,17 +242,33 @@ public DexFile(File file) throws IOException, FileFormatException {
242242

243243
// data
244244
for (Map.Entry<Long, Class<?>> todoItem : todoData.entrySet()) {
245-
// There is only 1 constructor in the item class
246-
Constructor<?> cons = todoItem.getValue().getDeclaredConstructors()[0];
247-
stream.flyTo(todoItem.getKey().intValue());
248-
try {
249-
this.data.put(todoItem.getKey(), (FileComponent) cons.newInstance(stream));
250-
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
251-
String msg = String.format("newInstance failed for data item at 0x%X of type %s", todoItem.getKey(), todoItem.getValue());
252-
LOGGER.severe(msg);
253-
throw new IOException(msg, e);
245+
this.parseData(todoItem.getKey(), todoItem.getValue(), stream);
246+
}
247+
}
248+
249+
void parseData(Long offset, Class<?> type, PosDataInputStreamDex stream) throws IOException, FileFormatException{
250+
int breakPos = stream.getPos();
251+
252+
Constructor<?> cons = type.getDeclaredConstructors()[0];
253+
stream.flyTo(offset.intValue());
254+
try {
255+
switch(cons.getParameterCount()) {
256+
case 1:
257+
this.data.put(offset, (FileComponent) cons.newInstance(stream));
258+
break;
259+
case 2:
260+
this.data.put(offset, (FileComponent) cons.newInstance(stream, this));
261+
break;
262+
default:
263+
throw new FileFormatException(String.format("Coding issue: no suitable constructor for type %s at 0x%X", type.getSimpleName(), offset.intValue()));
254264
}
265+
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
266+
String msg = String.format("newInstance failed for data item for type %s at 0x%X", type.getSimpleName(), offset.intValue());
267+
LOGGER.severe(msg);
268+
throw new IOException(msg, e);
255269
}
270+
271+
stream.flyTo(breakPos);
256272
}
257273

258274
static void check_uint(String fieldName, Type_uint uint, int streamPosition) throws FileFormatException {

FormatDEX/src/main/java/org/freeinternals/format/dex/JTreeDexFile.java

Lines changed: 7 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88

99
import java.nio.charset.StandardCharsets;
1010
import java.util.Map;
11-
import java.util.logging.Level;
1211
import java.util.logging.Logger;
1312
import javax.swing.Icon;
1413
import javax.swing.tree.DefaultMutableTreeNode;
@@ -117,35 +116,13 @@ private void generate_string_ids(DefaultMutableTreeNode parentNode, DexFile dexF
117116

118117
for (int i = 0; i < size; i++) {
119118
string_id_item item = dexFile.string_ids[i];
120-
FileComponent fc = dexFile.data.get(item.string_data_off.value);
121-
string_data_item strData = null;
122-
String strDataValue = "";
123-
if (fc instanceof string_data_item) {
124-
strData = (string_data_item) fc;
125-
strDataValue = strData.getString();
126-
} else {
127-
LOGGER.log(Level.SEVERE, String.format("This case should never happen. The string_ids did not point to string_data_item: index=%d offset=0x%08X", i, fc.getStartPos()));
128-
}
129-
130119
DefaultMutableTreeNode itemNode = new DefaultMutableTreeNode(new JTreeNodeFileComponent(
131120
item.getStartPos(),
132121
item.getLength(),
133-
String.format("string_id_item[%,d] : %s", i, UITool.left(strDataValue))
122+
String.format("string_id_item[%,d] : %s", i, UITool.left(dexFile.get_string_ids_string(i)))
134123
));
135124
node.add(itemNode);
136-
item.generateTreeNode(itemNode);
137-
138-
if (strData != null) {
139-
DefaultMutableTreeNode dataNode = new DefaultMutableTreeNode(new JTreeNodeFileComponent(
140-
strData.getStartPos(),
141-
strData.getLength(),
142-
"string_data_item",
143-
UITool.icon4Shortcut(),
144-
GenerateTreeNodeDexFile.MESSAGES.getString("msg_string_data_item")
145-
));
146-
itemNode.add(dataNode);
147-
strData.generateTreeNode(dataNode);
148-
}
125+
item.generateTreeNode(itemNode, dexFile);
149126
}
150127
}
151128

@@ -290,8 +267,11 @@ private void generate_data(DefaultMutableTreeNode parentNode, DexFile dexFile) {
290267
comp.getLength(),
291268
Type_uint.toString(startPos) + " - " + comp.getClass().getSimpleName()));
292269
node.add(itemNode);
293-
294-
if (comp instanceof GenerateTreeNode) {
270+
271+
if (comp instanceof string_data_item) {
272+
// Ignore string_data_item in data section, to avoid to many duplicate records
273+
continue;
274+
} else if (comp instanceof GenerateTreeNode) {
295275
((GenerateTreeNode) comp).generateTreeNode(itemNode);
296276
} else if (comp instanceof GenerateTreeNodeDexFile) {
297277
((GenerateTreeNodeDexFile) comp).generateTreeNode(itemNode, dexFile);
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* access_flag.java June 21, 2021, 10:18
3+
*
4+
* Copyright 2021, FreeInternals.org. All rights reserved.
5+
* Use is subject to license terms.
6+
*/
7+
package org.freeinternals.format.dex;
8+
9+
import java.util.ArrayList;
10+
import java.util.Collections;
11+
import java.util.List;
12+
import org.freeinternals.format.classfile.AccessFlag;
13+
14+
/**
15+
* Bit-fields of these flags are used to indicate the accessibility and overall
16+
* properties of classes and class members.
17+
*
18+
* @author Amos Shi
19+
*
20+
* <pre>
21+
* java:S101 - Class names should comply with a naming convention --- We respect the name from DEX Spec instead
22+
* java:S116 - Field names should comply with a naming convention --- We respect the DEX spec name instead
23+
* java:S1104 - Class variable fields should not have public accessibility --- No, we like the simplified final value manner
24+
* </pre>
25+
*/
26+
@SuppressWarnings({"java:S101", "java:S116", "java:S1104"})
27+
public class access_flag {
28+
29+
/**
30+
* Access flags for Classes (and InnerClass annotations).
31+
*/
32+
public static final List<AccessFlag> ForClass = Collections.synchronizedList(new ArrayList<>());
33+
/**
34+
* Access flags for Fields.
35+
*/
36+
public static final List<AccessFlag> ForField = Collections.synchronizedList(new ArrayList<>());
37+
/**
38+
* Access flags for Methods.
39+
*/
40+
public static final List<AccessFlag> ForMethod = Collections.synchronizedList(new ArrayList<>());
41+
42+
static {
43+
// For Classes (and InnerClass annotations)
44+
access_flag.ForClass.add(AccessFlag.ACC_PUBLIC);
45+
access_flag.ForClass.add(AccessFlag.ACC_PRIVATE);
46+
access_flag.ForClass.add(AccessFlag.ACC_PROTECTED);
47+
access_flag.ForClass.add(AccessFlag.ACC_STATIC);
48+
access_flag.ForClass.add(AccessFlag.ACC_FINAL);
49+
access_flag.ForClass.add(AccessFlag.ACC_INTERFACE);
50+
access_flag.ForClass.add(AccessFlag.ACC_ABSTRACT);
51+
access_flag.ForClass.add(AccessFlag.ACC_SYNTHETIC);
52+
access_flag.ForClass.add(AccessFlag.ACC_ANNOTATION);
53+
access_flag.ForClass.add(AccessFlag.ACC_ENUM);
54+
55+
// For Fields
56+
access_flag.ForField.add(AccessFlag.ACC_PUBLIC);
57+
access_flag.ForField.add(AccessFlag.ACC_PRIVATE);
58+
access_flag.ForField.add(AccessFlag.ACC_PROTECTED);
59+
access_flag.ForField.add(AccessFlag.ACC_STATIC);
60+
access_flag.ForField.add(AccessFlag.ACC_FINAL);
61+
access_flag.ForField.add(AccessFlag.ACC_VOLATILE);
62+
access_flag.ForField.add(AccessFlag.ACC_TRANSIENT);
63+
access_flag.ForField.add(AccessFlag.ACC_SYNTHETIC);
64+
access_flag.ForField.add(AccessFlag.ACC_ENUM);
65+
66+
// For Methods
67+
access_flag.ForMethod.add(AccessFlag.ACC_PUBLIC);
68+
access_flag.ForMethod.add(AccessFlag.ACC_PRIVATE);
69+
access_flag.ForMethod.add(AccessFlag.ACC_PROTECTED);
70+
access_flag.ForMethod.add(AccessFlag.ACC_STATIC);
71+
access_flag.ForMethod.add(AccessFlag.ACC_FINAL);
72+
access_flag.ForMethod.add(AccessFlag.ACC_SYNCHRONIZED);
73+
access_flag.ForMethod.add(AccessFlag.ACC_BRIDGE);
74+
access_flag.ForMethod.add(AccessFlag.ACC_VARARGS);
75+
access_flag.ForMethod.add(AccessFlag.ACC_ABSTRACT);
76+
access_flag.ForMethod.add(AccessFlag.ACC_STRICT);
77+
access_flag.ForMethod.add(AccessFlag.ACC_SYNTHETIC);
78+
access_flag.ForMethod.add(AccessFlag.ACC_CONSTRUCTOR);
79+
access_flag.ForMethod.add(AccessFlag.ACC_DECLARED_SYNCHRONIZED);
80+
}
81+
82+
/**
83+
* Private constructor for utility class.
84+
*/
85+
private access_flag() {
86+
}
87+
88+
public static String getClassModifier(long value) {
89+
return AccessFlag.getModifier(value, access_flag.ForClass);
90+
}
91+
92+
public static String getFieldModifier(int value) {
93+
return AccessFlag.getModifier(value, access_flag.ForField);
94+
}
95+
96+
public static String getMethodModifier(int value) {
97+
return AccessFlag.getModifier(value, access_flag.ForMethod);
98+
}
99+
}

0 commit comments

Comments
 (0)