fix some sonar warnings
This commit is contained in:
@@ -1,14 +1,5 @@
|
||||
package jadx.api;
|
||||
|
||||
import jadx.api.ResourceFile.ZipRef;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.ResTableParser;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
@@ -24,8 +15,16 @@ import java.util.zip.ZipFile;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.ResourceFile.ZipRef;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.files.InputFile;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.ResTableParser;
|
||||
|
||||
import static jadx.core.utils.files.FileUtils.READ_BUFFER_SIZE;
|
||||
import static jadx.core.utils.files.FileUtils.close;
|
||||
import static jadx.core.utils.files.FileUtils.copyStream;
|
||||
|
||||
// TODO: move to core package
|
||||
@@ -53,44 +52,30 @@ public final class ResourcesLoader {
|
||||
}
|
||||
|
||||
public static ResContainer decodeStream(ResourceFile rf, ResourceDecoder decoder) throws JadxException {
|
||||
ZipFile zipFile = null;
|
||||
InputStream inputStream = null;
|
||||
ResContainer result;
|
||||
try {
|
||||
long size;
|
||||
ZipRef zipRef = rf.getZipRef();
|
||||
if (zipRef == null) {
|
||||
File file = new File(rf.getName());
|
||||
inputStream = new BufferedInputStream(new FileInputStream(file));
|
||||
size = file.length();
|
||||
try (InputStream inputStream = new BufferedInputStream(new FileInputStream(file))) {
|
||||
return decoder.decode(file.length(), inputStream);
|
||||
}
|
||||
} else {
|
||||
zipFile = new ZipFile(zipRef.getZipFile());
|
||||
ZipEntry entry = zipFile.getEntry(zipRef.getEntryName());
|
||||
|
||||
if(!ZipSecurity.isValidZipEntry(entry)) {
|
||||
return null;
|
||||
try (ZipFile zipFile = new ZipFile(zipRef.getZipFile())) {
|
||||
ZipEntry entry = zipFile.getEntry(zipRef.getEntryName());
|
||||
if (entry == null) {
|
||||
throw new IOException("Zip entry not found: " + zipRef);
|
||||
}
|
||||
if (!ZipSecurity.isValidZipEntry(entry)) {
|
||||
return null;
|
||||
}
|
||||
try (InputStream inputStream = new BufferedInputStream(zipFile.getInputStream(entry))) {
|
||||
return decoder.decode(entry.getSize(), inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
if (entry == null) {
|
||||
throw new IOException("Zip entry not found: " + zipRef);
|
||||
}
|
||||
inputStream = new BufferedInputStream(zipFile.getInputStream(entry));
|
||||
size = entry.getSize();
|
||||
}
|
||||
result = decoder.decode(size, inputStream);
|
||||
} catch (Exception e) {
|
||||
throw new JadxException("Error decode: " + rf.getName(), e);
|
||||
} finally {
|
||||
try {
|
||||
if (zipFile != null) {
|
||||
zipFile.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error close zip file: {}", rf.getName(), e);
|
||||
}
|
||||
close(inputStream);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf) {
|
||||
@@ -106,7 +91,7 @@ public final class ResourcesLoader {
|
||||
}
|
||||
|
||||
private static ResContainer loadContent(JadxDecompiler jadxRef, ResourceFile rf,
|
||||
InputStream inputStream, long size) throws IOException {
|
||||
InputStream inputStream, long size) throws IOException {
|
||||
switch (rf.getType()) {
|
||||
case MANIFEST:
|
||||
case XML:
|
||||
@@ -118,39 +103,31 @@ public final class ResourcesLoader {
|
||||
|
||||
case IMG:
|
||||
return ResContainer.singleImageFile(rf.getName(), inputStream);
|
||||
|
||||
default:
|
||||
if (size > LOAD_SIZE_LIMIT) {
|
||||
return ResContainer.singleFile(rf.getName(),
|
||||
new CodeWriter().add("File too big, size: " + String.format("%.2f KB", size / 1024.)));
|
||||
}
|
||||
return ResContainer.singleFile(rf.getName(), loadToCodeWriter(inputStream));
|
||||
}
|
||||
if (size > LOAD_SIZE_LIMIT) {
|
||||
return ResContainer.singleFile(rf.getName(),
|
||||
new CodeWriter().add("File too big, size: " + String.format("%.2f KB", size / 1024.)));
|
||||
}
|
||||
return ResContainer.singleFile(rf.getName(), loadToCodeWriter(inputStream));
|
||||
}
|
||||
|
||||
private void loadFile(List<ResourceFile> list, File file) {
|
||||
if (file == null) {
|
||||
return;
|
||||
}
|
||||
ZipFile zip = null;
|
||||
try {
|
||||
zip = new ZipFile(file);
|
||||
try (ZipFile zip = new ZipFile(file)) {
|
||||
Enumeration<? extends ZipEntry> entries = zip.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
ZipEntry entry = entries.nextElement();
|
||||
if(ZipSecurity.isValidZipEntry(entry)) {
|
||||
if (ZipSecurity.isValidZipEntry(entry)) {
|
||||
addEntry(list, file, entry);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
LOG.debug("Not a zip file: {}", file.getAbsolutePath());
|
||||
addResourceFile(list, file);
|
||||
} finally {
|
||||
if (zip != null) {
|
||||
try {
|
||||
zip.close();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Zip file close error: {}", file.getAbsolutePath(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +135,7 @@ public final class ResourcesLoader {
|
||||
String name = file.getAbsolutePath();
|
||||
ResourceType type = ResourceType.getFileType(name);
|
||||
ResourceFile rf = ResourceFile.createResourceFileInstance(jadxRef, name, type);
|
||||
if(rf != null) {
|
||||
if (rf != null) {
|
||||
list.add(rf);
|
||||
}
|
||||
}
|
||||
@@ -170,7 +147,7 @@ public final class ResourcesLoader {
|
||||
String name = entry.getName();
|
||||
ResourceType type = ResourceType.getFileType(name);
|
||||
ResourceFile rf = ResourceFile.createResourceFileInstance(jadxRef, name, type);
|
||||
if(rf != null) {
|
||||
if (rf != null) {
|
||||
rf.setZipRef(new ZipRef(zipFile, name));
|
||||
list.add(rf);
|
||||
}
|
||||
|
||||
@@ -21,4 +21,7 @@ public class Consts {
|
||||
public static final String ANONYMOUS_CLASS_PREFIX = "AnonymousClass";
|
||||
|
||||
public static final String MTH_TOSTRING_SIGNATURE = "toString()Ljava/lang/String;";
|
||||
|
||||
private Consts() {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,24 @@
|
||||
package jadx.core.codegen;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.attributes.AttrNode;
|
||||
import jadx.core.dex.attributes.nodes.EnumClassAttr;
|
||||
import jadx.core.dex.attributes.nodes.EnumClassAttr.EnumField;
|
||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.info.AccessInfo;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
@@ -23,30 +36,7 @@ import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.android.dx.rop.code.AccessFlags;
|
||||
|
||||
public class ClassGen {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ClassGen.class);
|
||||
|
||||
public static final Comparator<MethodNode> METHOD_LINE_COMPARATOR = new Comparator<MethodNode>() {
|
||||
@Override
|
||||
public int compare(MethodNode a, MethodNode b) {
|
||||
return Utils.compare(a.getSourceLine(), b.getSourceLine());
|
||||
}
|
||||
};
|
||||
|
||||
private final ClassNode cls;
|
||||
private final ClassGen parentGen;
|
||||
@@ -276,7 +266,7 @@ public class ClassGen {
|
||||
|
||||
private static List<MethodNode> sortMethodsByLine(List<MethodNode> methods) {
|
||||
List<MethodNode> out = new ArrayList<>(methods);
|
||||
Collections.sort(out, METHOD_LINE_COMPARATOR);
|
||||
out.sort(Comparator.comparingInt(LineAttrNode::getSourceLine));
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
package jadx.core.dex.info;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.core.dex.attributes.AType;
|
||||
import jadx.core.dex.instructions.args.LiteralArg;
|
||||
@@ -10,14 +18,6 @@ import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.ResRefField;
|
||||
import jadx.core.dex.nodes.parser.FieldInitAttr;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ConstStorage {
|
||||
|
||||
private static final class ValueStorage {
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
package jadx.core.dex.nodes.parser;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dex.Dex.Section;
|
||||
|
||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
||||
import jadx.core.dex.instructions.args.InsnArg;
|
||||
import jadx.core.dex.instructions.args.RegisterArg;
|
||||
@@ -9,16 +13,8 @@ import jadx.core.dex.nodes.InsnNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.android.dex.Dex.Section;
|
||||
|
||||
public class DebugInfoParser {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(DebugInfoParser.class);
|
||||
private static final int DBG_END_SEQUENCE = 0x00;
|
||||
private static final int DBG_ADVANCE_PC = 0x01;
|
||||
private static final int DBG_ADVANCE_LINE = 0x02;
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
package jadx.core.dex.nodes.parser;
|
||||
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.android.dex.Dex.Section;
|
||||
import com.android.dex.Leb128;
|
||||
|
||||
import jadx.core.dex.info.FieldInfo;
|
||||
import jadx.core.dex.info.MethodInfo;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
|
||||
public class EncValueParser {
|
||||
|
||||
private static final int ENCODED_BYTE = 0x00;
|
||||
@@ -91,8 +91,10 @@ public class EncValueParser {
|
||||
|
||||
case ENCODED_ANNOTATION:
|
||||
return AnnotationsParser.readAnnotation(dex, in, false);
|
||||
|
||||
default:
|
||||
throw new DecodeException("Unknown encoded value type: 0x" + Integer.toHexString(type));
|
||||
}
|
||||
throw new DecodeException("Unknown encoded value type: 0x" + Integer.toHexString(type));
|
||||
}
|
||||
|
||||
private int parseUnsignedInt(int byteCount) {
|
||||
|
||||
@@ -1,32 +1,19 @@
|
||||
package jadx.core.dex.visitors;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import jadx.api.IJadxArgs;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.utils.exceptions.CodegenException;
|
||||
import jadx.core.utils.files.ZipSecurity;
|
||||
|
||||
import java.io.File;
|
||||
public class SaveCode {
|
||||
|
||||
public class SaveCode extends AbstractVisitor {
|
||||
private final File dir;
|
||||
private final IJadxArgs args;
|
||||
|
||||
public SaveCode(File dir, IJadxArgs args) {
|
||||
this.args = args;
|
||||
this.dir = dir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean visit(ClassNode cls) throws CodegenException {
|
||||
save(dir, args, cls);
|
||||
return false;
|
||||
}
|
||||
private SaveCode() {}
|
||||
|
||||
public static void save(File dir, IJadxArgs args, ClassNode cls) {
|
||||
CodeWriter clsCode = cls.getCode();
|
||||
String fileName = cls.getClassInfo().getFullPath() + ".java";
|
||||
if (args.isFallbackMode()) {
|
||||
if (args.isFallbackMode()) {
|
||||
fileName += ".jadx";
|
||||
}
|
||||
clsCode.save(dir, fileName);
|
||||
|
||||
@@ -1,5 +1,14 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.dex.attributes.AFlag;
|
||||
import jadx.core.dex.attributes.IAttributeNode;
|
||||
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
|
||||
@@ -7,16 +16,6 @@ import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.utils.exceptions.JadxOverflowException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ErrorsCounter {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ErrorsCounter.class);
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
package jadx.core.utils;
|
||||
|
||||
import jadx.api.JadxDecompiler;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
import jadx.api.JadxDecompiler;
|
||||
|
||||
public class Utils {
|
||||
|
||||
public static final String JADX_API_PACKAGE = JadxDecompiler.class.getPackage().getName();
|
||||
@@ -95,8 +95,4 @@ public class Utils {
|
||||
th.setStackTrace(Arrays.copyOfRange(stackTrace, 0, cutIndex));
|
||||
}
|
||||
}
|
||||
|
||||
public static int compare(int x, int y) {
|
||||
return x < y ? -1 : x == y ? 0 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package jadx.core.utils.android;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.codegen.ClassGen;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.info.ClassInfo;
|
||||
@@ -7,17 +12,15 @@ import jadx.core.dex.nodes.ClassNode;
|
||||
import jadx.core.dex.nodes.DexNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* Android resources specific handlers
|
||||
*/
|
||||
public class AndroidResourcesUtils {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AndroidResourcesUtils.class);
|
||||
|
||||
private AndroidResourcesUtils() {
|
||||
}
|
||||
|
||||
public static ClassNode searchAppResClass(RootNode root) {
|
||||
String appPackage = root.getAppPackage();
|
||||
String fullName = appPackage != null ? appPackage + ".R" : "R";
|
||||
@@ -49,7 +52,7 @@ public class AndroidResourcesUtils {
|
||||
|
||||
private static ClassNode makeClass(RootNode root, String clsName) {
|
||||
List<DexNode> dexNodes = root.getDexNodes();
|
||||
if (dexNodes.size() == 0) {
|
||||
if (dexNodes.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
DexNode firstDex = dexNodes.get(0);
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
/**
|
||||
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package jadx.core.utils.android;
|
||||
@@ -22,70 +22,70 @@ import java.io.IOException;
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
abstract public class DataInputDelegate implements DataInput {
|
||||
protected final DataInput mDelegate;
|
||||
public abstract class DataInputDelegate implements DataInput {
|
||||
protected final DataInput mDelegate;
|
||||
|
||||
public DataInputDelegate(DataInput delegate) {
|
||||
this.mDelegate = delegate;
|
||||
}
|
||||
public DataInputDelegate(DataInput delegate) {
|
||||
this.mDelegate = delegate;
|
||||
}
|
||||
|
||||
public int skipBytes(int n) throws IOException {
|
||||
return mDelegate.skipBytes(n);
|
||||
}
|
||||
public int skipBytes(int n) throws IOException {
|
||||
return mDelegate.skipBytes(n);
|
||||
}
|
||||
|
||||
public int readUnsignedShort() throws IOException {
|
||||
return mDelegate.readUnsignedShort();
|
||||
}
|
||||
public int readUnsignedShort() throws IOException {
|
||||
return mDelegate.readUnsignedShort();
|
||||
}
|
||||
|
||||
public int readUnsignedByte() throws IOException {
|
||||
return mDelegate.readUnsignedByte();
|
||||
}
|
||||
public int readUnsignedByte() throws IOException {
|
||||
return mDelegate.readUnsignedByte();
|
||||
}
|
||||
|
||||
public String readUTF() throws IOException {
|
||||
return mDelegate.readUTF();
|
||||
}
|
||||
public String readUTF() throws IOException {
|
||||
return mDelegate.readUTF();
|
||||
}
|
||||
|
||||
public short readShort() throws IOException {
|
||||
return mDelegate.readShort();
|
||||
}
|
||||
public short readShort() throws IOException {
|
||||
return mDelegate.readShort();
|
||||
}
|
||||
|
||||
public long readLong() throws IOException {
|
||||
return mDelegate.readLong();
|
||||
}
|
||||
public long readLong() throws IOException {
|
||||
return mDelegate.readLong();
|
||||
}
|
||||
|
||||
public String readLine() throws IOException {
|
||||
return mDelegate.readLine();
|
||||
}
|
||||
public String readLine() throws IOException {
|
||||
return mDelegate.readLine();
|
||||
}
|
||||
|
||||
public int readInt() throws IOException {
|
||||
return mDelegate.readInt();
|
||||
}
|
||||
public int readInt() throws IOException {
|
||||
return mDelegate.readInt();
|
||||
}
|
||||
|
||||
public void readFully(byte[] b, int off, int len) throws IOException {
|
||||
mDelegate.readFully(b, off, len);
|
||||
}
|
||||
public void readFully(byte[] b, int off, int len) throws IOException {
|
||||
mDelegate.readFully(b, off, len);
|
||||
}
|
||||
|
||||
public void readFully(byte[] b) throws IOException {
|
||||
mDelegate.readFully(b);
|
||||
}
|
||||
public void readFully(byte[] b) throws IOException {
|
||||
mDelegate.readFully(b);
|
||||
}
|
||||
|
||||
public float readFloat() throws IOException {
|
||||
return mDelegate.readFloat();
|
||||
}
|
||||
public float readFloat() throws IOException {
|
||||
return mDelegate.readFloat();
|
||||
}
|
||||
|
||||
public double readDouble() throws IOException {
|
||||
return mDelegate.readDouble();
|
||||
}
|
||||
public double readDouble() throws IOException {
|
||||
return mDelegate.readDouble();
|
||||
}
|
||||
|
||||
public char readChar() throws IOException {
|
||||
return mDelegate.readChar();
|
||||
}
|
||||
public char readChar() throws IOException {
|
||||
return mDelegate.readChar();
|
||||
}
|
||||
|
||||
public byte readByte() throws IOException {
|
||||
return mDelegate.readByte();
|
||||
}
|
||||
public byte readByte() throws IOException {
|
||||
return mDelegate.readByte();
|
||||
}
|
||||
|
||||
public boolean readBoolean() throws IOException {
|
||||
return mDelegate.readBoolean();
|
||||
}
|
||||
}
|
||||
public boolean readBoolean() throws IOException {
|
||||
return mDelegate.readBoolean();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +1,108 @@
|
||||
/**
|
||||
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package jadx.core.utils.android;
|
||||
|
||||
import java.io.*;
|
||||
import java.io.DataInput;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*/
|
||||
public class ExtDataInput extends DataInputDelegate {
|
||||
public ExtDataInput(InputStream in) {
|
||||
this((DataInput) new DataInputStream(in));
|
||||
}
|
||||
public ExtDataInput(InputStream in) {
|
||||
this((DataInput) new DataInputStream(in));
|
||||
}
|
||||
|
||||
public ExtDataInput(DataInput delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
public ExtDataInput(DataInput delegate) {
|
||||
super(delegate);
|
||||
}
|
||||
|
||||
public int[] readIntArray(int length) throws IOException {
|
||||
int[] array = new int[length];
|
||||
for(int i = 0; i < length; i++) {
|
||||
array[i] = readInt();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
public int[] readIntArray(int length) throws IOException {
|
||||
int[] array = new int[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
array[i] = readInt();
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
public void skipInt() throws IOException {
|
||||
skipBytes(4);
|
||||
}
|
||||
public void skipInt() throws IOException {
|
||||
skipBytes(4);
|
||||
}
|
||||
|
||||
public void skipCheckInt(int expected) throws IOException {
|
||||
int got = readInt();
|
||||
if (got != expected) {
|
||||
throw new IOException(String.format(
|
||||
"Expected: 0x%08x, got: 0x%08x", expected, got));
|
||||
}
|
||||
}
|
||||
public void skipCheckInt(int expected) throws IOException {
|
||||
int got = readInt();
|
||||
if (got != expected) {
|
||||
throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got));
|
||||
}
|
||||
}
|
||||
|
||||
public void skipCheckShort(short expected) throws IOException {
|
||||
short got = readShort();
|
||||
if (got != expected) {
|
||||
throw new IOException(String.format(
|
||||
"Expected: 0x%08x, got: 0x%08x", expected, got));
|
||||
}
|
||||
}
|
||||
public void skipCheckShort(short expected) throws IOException {
|
||||
short got = readShort();
|
||||
if (got != expected) {
|
||||
throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got));
|
||||
}
|
||||
}
|
||||
|
||||
public void skipCheckByte(byte expected) throws IOException {
|
||||
byte got = readByte();
|
||||
if (got != expected) {
|
||||
throw new IOException(String.format(
|
||||
"Expected: 0x%08x, got: 0x%08x", expected, got));
|
||||
}
|
||||
}
|
||||
public void skipCheckByte(byte expected) throws IOException {
|
||||
byte got = readByte();
|
||||
if (got != expected) {
|
||||
throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got));
|
||||
}
|
||||
}
|
||||
|
||||
public void skipCheckChunkTypeInt(int expected, int possible) throws IOException {
|
||||
int got = readInt();
|
||||
public void skipCheckChunkTypeInt(int expected, int possible) throws IOException {
|
||||
int got = readInt();
|
||||
if (got == possible) {
|
||||
skipCheckChunkTypeInt(expected, -1);
|
||||
} else if (got != expected) {
|
||||
throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got));
|
||||
}
|
||||
}
|
||||
|
||||
if (got == possible) {
|
||||
skipCheckChunkTypeInt(expected, -1);
|
||||
} else if (got != expected) {
|
||||
throw new IOException(String.format("Expected: 0x%08x, got: 0x%08x", expected, got));
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The general contract of DataInput doesn't guarantee all the bytes requested will be skipped
|
||||
* and failure can occur for many reasons. We override this to try harder to skip all the bytes
|
||||
* requested (this is similar to DataInputStream's wrapper).
|
||||
*/
|
||||
@Override
|
||||
public final int skipBytes(int n) throws IOException {
|
||||
int total = 0;
|
||||
int cur = 0;
|
||||
|
||||
/**
|
||||
* The general contract of DataInput doesn't guarantee all the bytes requested will be skipped
|
||||
* and failure can occur for many reasons. We override this to try harder to skip all the bytes
|
||||
* requested (this is similar to DataInputStream's wrapper).
|
||||
*/
|
||||
public final int skipBytes(int n) throws IOException {
|
||||
int total = 0;
|
||||
int cur = 0;
|
||||
while ((total < n) && ((cur = super.skipBytes(n - total)) > 0)) {
|
||||
total += cur;
|
||||
}
|
||||
return total;
|
||||
}
|
||||
|
||||
while ((total < n) && ((cur = (int) super.skipBytes(n - total)) > 0)) {
|
||||
total += cur;
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public String readNullEndedString(int length, boolean fixed)
|
||||
throws IOException {
|
||||
StringBuilder string = new StringBuilder(16);
|
||||
while(length-- != 0) {
|
||||
short ch = readShort();
|
||||
if (ch == 0) {
|
||||
break;
|
||||
}
|
||||
string.append((char) ch);
|
||||
}
|
||||
if (fixed) {
|
||||
skipBytes(length * 2);
|
||||
}
|
||||
|
||||
return string.toString();
|
||||
}
|
||||
}
|
||||
public String readNullEndedString(int length, boolean fixed) throws IOException {
|
||||
StringBuilder string = new StringBuilder(16);
|
||||
while (length-- != 0) {
|
||||
short ch = readShort();
|
||||
if (ch == 0) {
|
||||
break;
|
||||
}
|
||||
string.append((char) ch);
|
||||
}
|
||||
if (fixed) {
|
||||
skipBytes(length * 2);
|
||||
}
|
||||
return string.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
/**
|
||||
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
* Copyright 2014 Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||
* <p>
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package jadx.core.utils.android;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.DataInput;
|
||||
@@ -25,7 +24,7 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
@@ -34,105 +33,107 @@ import jadx.core.utils.exceptions.JadxException;
|
||||
*/
|
||||
public class Res9patchStreamDecoder {
|
||||
|
||||
public void decode(InputStream in, OutputStream out) throws JadxException {
|
||||
try {
|
||||
byte[] data = IOUtils.toByteArray(in);
|
||||
public void decode(InputStream in, OutputStream out) throws JadxException {
|
||||
try {
|
||||
byte[] data = IOUtils.toByteArray(in);
|
||||
|
||||
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
|
||||
int w = im.getWidth(), h = im.getHeight();
|
||||
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
|
||||
int w = im.getWidth();
|
||||
int h = im.getHeight();
|
||||
|
||||
BufferedImage im2 = new BufferedImage(w+2, h+2, BufferedImage.TYPE_INT_ARGB);
|
||||
im2.createGraphics().drawImage(im, 1, 1, w, h, null);
|
||||
BufferedImage im2 = new BufferedImage(w + 2, h + 2, BufferedImage.TYPE_INT_ARGB);
|
||||
im2.createGraphics().drawImage(im, 1, 1, w, h, null);
|
||||
|
||||
NinePatch np = getNinePatch(data);
|
||||
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
|
||||
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
|
||||
NinePatch np = getNinePatch(data);
|
||||
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
|
||||
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
|
||||
|
||||
int[] xDivs = np.xDivs;
|
||||
for (int i = 0; i < xDivs.length; i += 2) {
|
||||
drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]);
|
||||
}
|
||||
int[] xDivs = np.xDivs;
|
||||
for (int i = 0; i < xDivs.length; i += 2) {
|
||||
drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]);
|
||||
}
|
||||
|
||||
int[] yDivs = np.yDivs;
|
||||
for (int i = 0; i < yDivs.length; i += 2) {
|
||||
drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);
|
||||
}
|
||||
int[] yDivs = np.yDivs;
|
||||
for (int i = 0; i < yDivs.length; i += 2) {
|
||||
drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);
|
||||
}
|
||||
|
||||
ImageIO.write(im2, "png", out);
|
||||
} catch (IOException | NullPointerException ex) {
|
||||
throw new JadxException(ex.toString());
|
||||
}
|
||||
}
|
||||
ImageIO.write(im2, "png", out);
|
||||
} catch (IOException | NullPointerException ex) {
|
||||
throw new JadxException(ex.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private NinePatch getNinePatch(byte[] data) throws JadxException,
|
||||
IOException {
|
||||
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
|
||||
find9patchChunk(di);
|
||||
return NinePatch.decode(di);
|
||||
}
|
||||
private NinePatch getNinePatch(byte[] data) throws JadxException, IOException {
|
||||
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
|
||||
find9patchChunk(di);
|
||||
return NinePatch.decode(di);
|
||||
}
|
||||
|
||||
private void find9patchChunk(DataInput di) throws JadxException,
|
||||
IOException {
|
||||
di.skipBytes(8);
|
||||
while (true) {
|
||||
int size;
|
||||
try {
|
||||
size = di.readInt();
|
||||
} catch (IOException ex) {
|
||||
throw new JadxException("Cant find nine patch chunk", ex);
|
||||
}
|
||||
if (di.readInt() == NP_CHUNK_TYPE) {
|
||||
return;
|
||||
}
|
||||
di.skipBytes(size + 4);
|
||||
}
|
||||
}
|
||||
private void find9patchChunk(DataInput di) throws JadxException, IOException {
|
||||
di.skipBytes(8);
|
||||
while (true) {
|
||||
int size;
|
||||
try {
|
||||
size = di.readInt();
|
||||
} catch (IOException ex) {
|
||||
throw new JadxException("Cant find nine patch chunk", ex);
|
||||
}
|
||||
if (di.readInt() == NP_CHUNK_TYPE) {
|
||||
return;
|
||||
}
|
||||
di.skipBytes(size + 4);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawHLine(BufferedImage im, int y, int x1, int x2) {
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
im.setRGB(x, y, NP_COLOR);
|
||||
}
|
||||
}
|
||||
private void drawHLine(BufferedImage im, int y, int x1, int x2) {
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
im.setRGB(x, y, NP_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
private void drawVLine(BufferedImage im, int x, int y1, int y2) {
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
im.setRGB(x, y, NP_COLOR);
|
||||
}
|
||||
}
|
||||
private void drawVLine(BufferedImage im, int x, int y1, int y2) {
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
im.setRGB(x, y, NP_COLOR);
|
||||
}
|
||||
}
|
||||
|
||||
private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc
|
||||
private static final int NP_COLOR = 0xff000000;
|
||||
private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc
|
||||
private static final int NP_COLOR = 0xff000000;
|
||||
|
||||
private static class NinePatch {
|
||||
public final int padLeft, padRight, padTop, padBottom;
|
||||
public final int[] xDivs, yDivs;
|
||||
private static class NinePatch {
|
||||
public final int padLeft;
|
||||
public final int padRight;
|
||||
public final int padTop;
|
||||
public final int padBottom;
|
||||
public final int[] xDivs;
|
||||
public final int[] yDivs;
|
||||
|
||||
public NinePatch(int padLeft, int padRight, int padTop, int padBottom,
|
||||
int[] xDivs, int[] yDivs) {
|
||||
this.padLeft = padLeft;
|
||||
this.padRight = padRight;
|
||||
this.padTop = padTop;
|
||||
this.padBottom = padBottom;
|
||||
this.xDivs = xDivs;
|
||||
this.yDivs = yDivs;
|
||||
}
|
||||
public NinePatch(int padLeft, int padRight, int padTop, int padBottom,
|
||||
int[] xDivs, int[] yDivs) {
|
||||
this.padLeft = padLeft;
|
||||
this.padRight = padRight;
|
||||
this.padTop = padTop;
|
||||
this.padBottom = padBottom;
|
||||
this.xDivs = xDivs;
|
||||
this.yDivs = yDivs;
|
||||
}
|
||||
|
||||
public static NinePatch decode(ExtDataInput di) throws IOException {
|
||||
di.skipBytes(1);
|
||||
byte numXDivs = di.readByte();
|
||||
byte numYDivs = di.readByte();
|
||||
di.skipBytes(1);
|
||||
di.skipBytes(8);
|
||||
int padLeft = di.readInt();
|
||||
int padRight = di.readInt();
|
||||
int padTop = di.readInt();
|
||||
int padBottom = di.readInt();
|
||||
di.skipBytes(4);
|
||||
int[] xDivs = di.readIntArray(numXDivs);
|
||||
int[] yDivs = di.readIntArray(numYDivs);
|
||||
public static NinePatch decode(ExtDataInput di) throws IOException {
|
||||
di.skipBytes(1);
|
||||
byte numXDivs = di.readByte();
|
||||
byte numYDivs = di.readByte();
|
||||
di.skipBytes(1);
|
||||
di.skipBytes(8);
|
||||
int padLeft = di.readInt();
|
||||
int padRight = di.readInt();
|
||||
int padTop = di.readInt();
|
||||
int padBottom = di.readInt();
|
||||
di.skipBytes(4);
|
||||
int[] xDivs = di.readIntArray(numXDivs);
|
||||
int[] yDivs = di.readIntArray(numYDivs);
|
||||
|
||||
return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs,
|
||||
yDivs);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs, yDivs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
@@ -179,20 +180,10 @@ public class FileUtils {
|
||||
}
|
||||
|
||||
private static boolean isZipFileCanBeOpen(File file) {
|
||||
ZipFile zipFile = null;
|
||||
try {
|
||||
zipFile = new ZipFile(file);
|
||||
try (ZipFile zipFile = new ZipFile(file)) {
|
||||
return zipFile.entries().hasMoreElements();
|
||||
} catch (Exception e) {
|
||||
return false;
|
||||
} finally {
|
||||
if (zipFile != null) {
|
||||
try {
|
||||
zipFile.close();
|
||||
} catch (IOException e) {
|
||||
LOG.error(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,8 +205,8 @@ public class FileUtils {
|
||||
LOG.debug("Failed to detect filesystem case-sensitivity by file creation", e);
|
||||
} finally {
|
||||
try {
|
||||
caseCheckUpper.delete();
|
||||
caseCheckLow.delete();
|
||||
Files.deleteIfExists(caseCheckUpper.toPath());
|
||||
Files.deleteIfExists(caseCheckLow.toPath());
|
||||
} catch (Exception e) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package jadx.core.utils.files;
|
||||
|
||||
import jadx.core.utils.AsmUtils;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
@@ -16,13 +11,16 @@ import java.util.jar.JarOutputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
import com.android.dex.Dex;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.android.dex.Dex;
|
||||
import jadx.core.utils.AsmUtils;
|
||||
import jadx.core.utils.exceptions.DecodeException;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import static jadx.core.utils.files.FileUtils.close;
|
||||
import static jadx.core.utils.files.FileUtils.isApkFile;
|
||||
import static jadx.core.utils.files.FileUtils.isZipDexFile;
|
||||
|
||||
@@ -30,7 +28,7 @@ public class InputFile {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(InputFile.class);
|
||||
|
||||
private final File file;
|
||||
private final List<DexFile> dexFiles = new ArrayList<DexFile>();
|
||||
private final List<DexFile> dexFiles = new ArrayList<>();
|
||||
|
||||
public static void addFilesFrom(File file, List<InputFile> list, boolean... skipSources) throws IOException, DecodeException {
|
||||
InputFile inputFile = new InputFile(file);
|
||||
@@ -38,7 +36,7 @@ public class InputFile {
|
||||
list.add(inputFile);
|
||||
}
|
||||
|
||||
private InputFile(File file) throws IOException, DecodeException {
|
||||
private InputFile(File file) throws IOException {
|
||||
if (!file.exists()) {
|
||||
throw new IOException("File not found: " + file.getAbsolutePath());
|
||||
}
|
||||
@@ -80,11 +78,11 @@ public class InputFile {
|
||||
throw new DecodeException("Unsupported input file format: " + file);
|
||||
}
|
||||
|
||||
private void addDexFile(Dex dexBuf) throws IOException {
|
||||
private void addDexFile(Dex dexBuf) {
|
||||
addDexFile("", dexBuf);
|
||||
}
|
||||
|
||||
private void addDexFile(String fileName, Dex dexBuf) throws IOException {
|
||||
private void addDexFile(String fileName, Dex dexBuf) {
|
||||
dexFiles.add(new DexFile(this, fileName, dexBuf));
|
||||
}
|
||||
|
||||
@@ -92,44 +90,41 @@ public class InputFile {
|
||||
int index = 0;
|
||||
try (ZipFile zf = new ZipFile(file)) {
|
||||
// Input file could be .apk or .zip files
|
||||
// we should consider the input file could contain only one single dex, multi-dex, or instantRun support dex for Android .apk files
|
||||
// we should consider the input file could contain only one single dex, multi-dex,
|
||||
// or instantRun support dex for Android .apk files
|
||||
String instantRunDexSuffix = "classes" + ext;
|
||||
for (Enumeration<? extends ZipEntry> e = zf.entries(); e.hasMoreElements(); ) {
|
||||
ZipEntry entry = e.nextElement();
|
||||
String entryName = entry.getName();
|
||||
|
||||
// security check
|
||||
if(!ZipSecurity.isValidZipEntry(entry)) {
|
||||
if (!ZipSecurity.isValidZipEntry(entry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
InputStream inputStream = zf.getInputStream(entry);
|
||||
try {
|
||||
String entryName = entry.getName();
|
||||
try (InputStream inputStream = zf.getInputStream(entry)) {
|
||||
if ((entryName.startsWith("classes") && entryName.endsWith(ext))
|
||||
|| entryName.endsWith(instantRunDexSuffix)) {
|
||||
if (ext.equals(".dex")) {
|
||||
index++;
|
||||
addDexFile(entryName, new Dex(inputStream));
|
||||
} else if (ext.equals(".jar")) {
|
||||
index++;
|
||||
File jarFile = FileUtils.createTempFile(entryName);
|
||||
FileOutputStream fos = new FileOutputStream(jarFile);
|
||||
try {
|
||||
IOUtils.copy(inputStream, fos);
|
||||
} finally {
|
||||
close(fos);
|
||||
}
|
||||
addDexFile(entryName, loadFromJar(jarFile));
|
||||
} else {
|
||||
throw new JadxRuntimeException("Unexpected extension in zip: " + ext);
|
||||
switch (ext) {
|
||||
case ".dex":
|
||||
index++;
|
||||
addDexFile(entryName, new Dex(inputStream));
|
||||
break;
|
||||
|
||||
case ".jar":
|
||||
index++;
|
||||
File jarFile = FileUtils.createTempFile(entryName);
|
||||
try (FileOutputStream fos = new FileOutputStream(jarFile)) {
|
||||
IOUtils.copy(inputStream, fos);
|
||||
}
|
||||
addDexFile(entryName, loadFromJar(jarFile));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new JadxRuntimeException("Unexpected extension in zip: " + ext);
|
||||
}
|
||||
} else if (entryName.equals("instant-run.zip") && ext.equals(".dex")) {
|
||||
File jarFile = FileUtils.createTempFile("instant-run.zip");
|
||||
FileOutputStream fos = new FileOutputStream(jarFile);
|
||||
try {
|
||||
try (FileOutputStream fos = new FileOutputStream(jarFile)) {
|
||||
IOUtils.copy(inputStream, fos);
|
||||
} finally {
|
||||
close(fos);
|
||||
}
|
||||
InputFile tempFile = new InputFile(jarFile);
|
||||
tempFile.loadFromZip(ext);
|
||||
@@ -139,8 +134,6 @@ public class InputFile {
|
||||
this.dexFiles.addAll(dexFiles);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
close(inputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,7 +149,7 @@ public class InputFile {
|
||||
throw new JadxException("Empty dx output");
|
||||
}
|
||||
return new Dex(ba);
|
||||
} catch (Throwable e) {
|
||||
} catch (Exception e) {
|
||||
throw new DecodeException("java class to dex conversion error:\n " + e.getMessage(), e);
|
||||
} finally {
|
||||
if (j2d.isError()) {
|
||||
@@ -167,19 +160,12 @@ public class InputFile {
|
||||
|
||||
private static Dex loadFromClassFile(File file) throws IOException, DecodeException {
|
||||
File outFile = FileUtils.createTempFile("cls.jar");
|
||||
FileOutputStream out = null;
|
||||
JarOutputStream jo = null;
|
||||
try {
|
||||
out = new FileOutputStream(outFile);
|
||||
jo = new JarOutputStream(out);
|
||||
try (JarOutputStream jo = new JarOutputStream(new FileOutputStream(outFile))) {
|
||||
String clsName = AsmUtils.getNameFromClassFile(file);
|
||||
if (clsName == null || ZipSecurity.isValidZipEntryName(clsName)) {
|
||||
throw new IOException("Can't read class name from file: " + file);
|
||||
}
|
||||
FileUtils.addFileToJar(jo, file, clsName + ".class");
|
||||
} finally {
|
||||
close(jo);
|
||||
close(out);
|
||||
}
|
||||
return loadFromJar(outFile);
|
||||
}
|
||||
|
||||
@@ -5,17 +5,14 @@ import java.io.ByteArrayOutputStream;
|
||||
import com.android.dx.command.dexer.DxContext;
|
||||
import com.android.dx.command.dexer.Main;
|
||||
import com.android.dx.command.dexer.Main.Arguments;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
|
||||
public class JavaToDex {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(JavaToDex.class);
|
||||
private static final String CHARSET_NAME = "UTF-8";
|
||||
|
||||
public static class DxArgs extends Arguments {
|
||||
private static class DxArgs extends Arguments {
|
||||
public DxArgs(DxContext context, String dexFile, String[] input) {
|
||||
super(context);
|
||||
outName = dexFile;
|
||||
|
||||
@@ -8,55 +8,55 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
public class ZipSecurity {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ZipSecurity.class);
|
||||
|
||||
|
||||
// size of uncompressed zip entry shouldn't be bigger of compressed in MAX_SIZE_DIFF times
|
||||
private static final int MAX_SIZE_DIFF = 25;
|
||||
|
||||
private static boolean isInSubDirectory(File base, File file) {
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
if (file.equals(base)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isInSubDirectory(base, file.getParentFile());
|
||||
private ZipSecurity() {}
|
||||
|
||||
private static boolean isInSubDirectory(File base, File file) {
|
||||
if (file == null) {
|
||||
return false;
|
||||
}
|
||||
if (file.equals(base)) {
|
||||
return true;
|
||||
}
|
||||
return isInSubDirectory(base, file.getParentFile());
|
||||
}
|
||||
|
||||
|
||||
// checks that entry name contains no any traversals
|
||||
// and prevents cases like "../classes.dex", to limit output only to the specified directory
|
||||
public static boolean isValidZipEntryName(String entryName) {
|
||||
try {
|
||||
File currentPath = new File(".").getCanonicalFile();
|
||||
File canonical = new File(currentPath, entryName).getCanonicalFile();
|
||||
if(isInSubDirectory(currentPath, canonical)) {
|
||||
if (isInSubDirectory(currentPath, canonical)) {
|
||||
return true;
|
||||
}
|
||||
LOG.error("Path traversal attack detected, invalid name: {}", entryName);
|
||||
return false;
|
||||
}
|
||||
catch(Exception e) {
|
||||
} catch (Exception e) {
|
||||
LOG.error("Path traversal attack detected, invalid name: {}", entryName);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean isZipBomb(ZipEntry entry) {
|
||||
long compressedSize = entry.getCompressedSize();
|
||||
long compressedSize = entry.getCompressedSize();
|
||||
long uncompressedSize = entry.getSize();
|
||||
if(compressedSize < 0 || uncompressedSize < 0) {
|
||||
if (compressedSize < 0 || uncompressedSize < 0) {
|
||||
LOG.error("Zip bomp attack detected, invalid sizes: compressed {}, uncompressed {}, name {}",
|
||||
compressedSize, uncompressedSize, entry.getName());
|
||||
return true;
|
||||
}
|
||||
if(compressedSize * MAX_SIZE_DIFF < uncompressedSize) {
|
||||
LOG.error("Zip bomp attack detected, invalid sizes: compressed {}, uncompressed {}, name {}",
|
||||
if (compressedSize * MAX_SIZE_DIFF < uncompressedSize) {
|
||||
LOG.error("Zip bomb attack detected, invalid sizes: compressed {}, uncompressed {}, name {}",
|
||||
compressedSize, uncompressedSize, entry.getName());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static boolean isValidZipEntry(ZipEntry entry) {
|
||||
return isValidZipEntryName(entry.getName())
|
||||
&& !isZipBomb(entry);
|
||||
|
||||
@@ -1,14 +1,5 @@
|
||||
package jadx.core.xmlgen;
|
||||
|
||||
import jadx.api.ResourcesLoader;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.info.ConstStorage;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.xmlgen.entry.ValuesParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
@@ -18,6 +9,16 @@ import java.util.Map;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.ResourcesLoader;
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.dex.info.ConstStorage;
|
||||
import jadx.core.dex.instructions.args.ArgType;
|
||||
import jadx.core.dex.nodes.FieldNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.xmlgen.entry.ValuesParser;
|
||||
|
||||
/* TODO:
|
||||
Don't die when error occurs
|
||||
Check error cases, maybe checked const values are not always the same
|
||||
@@ -34,10 +35,12 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BinaryXMLParser.class);
|
||||
private static final String ANDROID_R_STYLE_CLS = "android.R$style";
|
||||
private static final boolean ATTR_NEW_LINE = false;
|
||||
private final Map<Integer, String> styleMap = new HashMap<Integer, String>();
|
||||
private final Map<Integer, FieldNode> localStyleMap = new HashMap<Integer, FieldNode>();
|
||||
|
||||
private final Map<Integer, String> styleMap = new HashMap<>();
|
||||
private final Map<Integer, FieldNode> localStyleMap = new HashMap<>();
|
||||
private final Map<Integer, String> resNames;
|
||||
private final Map<String, String> nsMap = new HashMap<>();
|
||||
|
||||
private CodeWriter writer;
|
||||
private String[] strings;
|
||||
private String currentTag = "ERROR";
|
||||
@@ -48,14 +51,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
|
||||
public BinaryXMLParser(RootNode root) {
|
||||
try {
|
||||
try {
|
||||
Class<?> rStyleCls = Class.forName(ANDROID_R_STYLE_CLS);
|
||||
for (Field f : rStyleCls.getFields()) {
|
||||
styleMap.put(f.getInt(f.getType()), f.getName());
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
LOG.error("R class loading failed", th);
|
||||
}
|
||||
readAndroidRStyleClass();
|
||||
// add application constants
|
||||
ConstStorage constStorage = root.getConstValues();
|
||||
Map<Object, FieldNode> constFields = constStorage.getGlobalConstFields();
|
||||
@@ -72,6 +68,17 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
}
|
||||
}
|
||||
|
||||
private void readAndroidRStyleClass() {
|
||||
try {
|
||||
Class<?> rStyleCls = Class.forName(ANDROID_R_STYLE_CLS);
|
||||
for (Field f : rStyleCls.getFields()) {
|
||||
styleMap.put(f.getInt(f.getType()), f.getName());
|
||||
}
|
||||
} catch (Exception th) {
|
||||
LOG.error("Android R class loading failed", th);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized CodeWriter parse(InputStream inputStream) throws IOException {
|
||||
is = new ParserStream(inputStream);
|
||||
if (!isBinaryXml()) {
|
||||
@@ -186,13 +193,11 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
int strIndex = is.readInt32();
|
||||
String str = strings[strIndex];
|
||||
|
||||
writer.startLine().addIndent();
|
||||
writer.attachSourceLine(lineNumber);
|
||||
writer.add(StringUtils.escapeXML(str.trim()));
|
||||
|
||||
//TODO: what's this for?
|
||||
/*writer.startLine().addIndent();
|
||||
writer.attachSourceLine(lineNumber);
|
||||
writer.add(StringUtils.escapeXML(str.trim()));*/
|
||||
|
||||
int size = is.readInt16();
|
||||
long size = is.readInt16();
|
||||
is.skip(size - 2);
|
||||
}
|
||||
|
||||
@@ -236,7 +241,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
writer.add(" xmlns:" + entry.getValue() + "=\"").add(entry.getKey()).add("\"");
|
||||
}
|
||||
}
|
||||
boolean attrNewLine = attributeCount == 1 ? false : ATTR_NEW_LINE;
|
||||
boolean attrNewLine = attributeCount != 1 && ATTR_NEW_LINE;
|
||||
for (int i = 0; i < attributeCount; i++) {
|
||||
parseAttribute(i, attrNewLine);
|
||||
}
|
||||
@@ -284,7 +289,6 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
if (attributeNS != -1) {
|
||||
writer.add(nsMap.get(strings[attributeNS])).add(':');
|
||||
}
|
||||
LOG.debug("decodeAttribute: " + attributeNS + " " + name);
|
||||
writer.add("style/").add(name.replaceAll("_", "."));
|
||||
} else {
|
||||
FieldNode field = localStyleMap.get(attrValData);
|
||||
@@ -304,7 +308,7 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
}
|
||||
writer.add(resName);
|
||||
} else {
|
||||
resName = ValuesParser.androidResMap.get(attrValData);
|
||||
resName = ValuesParser.getAndroidResMap().get(attrValData);
|
||||
if (resName != null) {
|
||||
writer.add("@android:").add(resName);
|
||||
} else if (attrValData == 0) {
|
||||
|
||||
@@ -56,7 +56,7 @@ public class ManifestAttributes {
|
||||
try {
|
||||
instance = new ManifestAttributes();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
LOG.error("Failed to create ManifestAttributes", e);
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
package jadx.core.xmlgen;
|
||||
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.utils.android.Res9patchStreamDecoder;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
@@ -17,9 +12,17 @@ import java.util.List;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.core.codegen.CodeWriter;
|
||||
import jadx.core.utils.android.Res9patchStreamDecoder;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
|
||||
public class ResContainer implements Comparable<ResContainer> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ResContainer.class);
|
||||
|
||||
private final String name;
|
||||
private final List<ResContainer> subFiles;
|
||||
|
||||
@@ -34,21 +37,21 @@ public class ResContainer implements Comparable<ResContainer> {
|
||||
}
|
||||
|
||||
public static ResContainer singleFile(String name, CodeWriter content) {
|
||||
ResContainer resContainer = new ResContainer(name, Collections.<ResContainer>emptyList());
|
||||
ResContainer resContainer = new ResContainer(name, Collections.emptyList());
|
||||
resContainer.content = content;
|
||||
return resContainer;
|
||||
}
|
||||
|
||||
public static ResContainer singleImageFile(String name, InputStream content) {
|
||||
ResContainer resContainer = new ResContainer(name, Collections.<ResContainer>emptyList());
|
||||
ResContainer resContainer = new ResContainer(name, Collections.emptyList());
|
||||
InputStream newContent = content;
|
||||
if (name.endsWith(".9.png")) {
|
||||
Res9patchStreamDecoder decoder = new Res9patchStreamDecoder();
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
try {
|
||||
decoder.decode(content, os);
|
||||
} catch (JadxException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to decode 9-patch png image", e);
|
||||
}
|
||||
newContent = new ByteArrayInputStream(os.toByteArray());
|
||||
}
|
||||
@@ -61,7 +64,7 @@ public class ResContainer implements Comparable<ResContainer> {
|
||||
}
|
||||
|
||||
public static ResContainer multiFile(String name) {
|
||||
return new ResContainer(name, new ArrayList<ResContainer>());
|
||||
return new ResContainer(name, new ArrayList<>());
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
package jadx.core.xmlgen;
|
||||
|
||||
import jadx.core.utils.Utils;
|
||||
import jadx.core.xmlgen.entry.ResourceEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -11,14 +8,9 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class ResourceStorage {
|
||||
import jadx.core.xmlgen.entry.ResourceEntry;
|
||||
|
||||
private static final Comparator<ResourceEntry> COMPARATOR = new Comparator<ResourceEntry>() {
|
||||
@Override
|
||||
public int compare(ResourceEntry a, ResourceEntry b) {
|
||||
return Utils.compare(a.getId(), b.getId());
|
||||
}
|
||||
};
|
||||
public class ResourceStorage {
|
||||
|
||||
private final List<ResourceEntry> list = new ArrayList<>();
|
||||
private String appPackage;
|
||||
@@ -32,12 +24,12 @@ public class ResourceStorage {
|
||||
}
|
||||
|
||||
public void finish() {
|
||||
Collections.sort(list, COMPARATOR);
|
||||
list.sort(Comparator.comparingInt(ResourceEntry::getId));
|
||||
}
|
||||
|
||||
public ResourceEntry getByRef(int refId) {
|
||||
ResourceEntry key = new ResourceEntry(refId);
|
||||
int index = Collections.binarySearch(list, key, COMPARATOR);
|
||||
int index = Collections.binarySearch(list, key, Comparator.comparingInt(ResourceEntry::getId));
|
||||
if (index < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -4,23 +4,24 @@ import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
public class XmlSecurity {
|
||||
|
||||
private static DocumentBuilderFactory secureDbf = null;
|
||||
|
||||
|
||||
private XmlSecurity() {}
|
||||
|
||||
public static DocumentBuilderFactory getSecureDbf() throws ParserConfigurationException {
|
||||
synchronized(XmlSecurity.class) {
|
||||
if(secureDbf == null) {
|
||||
synchronized (XmlSecurity.class) {
|
||||
if (secureDbf == null) {
|
||||
secureDbf = DocumentBuilderFactory.newInstance();
|
||||
secureDbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
|
||||
secureDbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
|
||||
secureDbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
secureDbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
secureDbf.setFeature("http://apache.org/xml/features/dom/create-entity-ref-nodes", false);
|
||||
secureDbf.setXIncludeAware(false);
|
||||
secureDbf.setExpandEntityReferences(false);
|
||||
secureDbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
|
||||
secureDbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
|
||||
secureDbf.setFeature("http://apache.org/xml/features/dom/create-entity-ref-nodes", false);
|
||||
secureDbf.setXIncludeAware(false);
|
||||
secureDbf.setExpandEntityReferences(false);
|
||||
}
|
||||
}
|
||||
return secureDbf;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -18,12 +18,12 @@ import jadx.core.xmlgen.ResTableParser;
|
||||
public class ValuesParser extends ParserConstants {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ValuesParser.class);
|
||||
|
||||
private static String[] androidStrings;
|
||||
private static Map<Integer, String> androidResMap;
|
||||
|
||||
private final String[] strings;
|
||||
private final Map<Integer, String> resMap;
|
||||
|
||||
public static String[] androidStrings;
|
||||
public static Map<Integer, String> androidResMap;
|
||||
|
||||
public ValuesParser(String[] strings, Map<Integer, String> resMap) {
|
||||
this.strings = strings;
|
||||
this.resMap = resMap;
|
||||
@@ -31,14 +31,14 @@ public class ValuesParser extends ParserConstants {
|
||||
if (androidStrings == null && androidResMap == null) {
|
||||
try {
|
||||
decodeAndroid();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Failed to decode Android Resource file", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void decodeAndroid() throws IOException {
|
||||
InputStream inputStream = new BufferedInputStream(getClass().getResourceAsStream("/resources.arsc"));
|
||||
private static void decodeAndroid() throws IOException {
|
||||
InputStream inputStream = new BufferedInputStream(ValuesParser.class.getResourceAsStream("/resources.arsc"));
|
||||
ResTableParser androidParser = new ResTableParser();
|
||||
androidParser.decode(inputStream);
|
||||
androidStrings = androidParser.getStrings();
|
||||
@@ -212,4 +212,8 @@ public class ValuesParser extends ParserConstants {
|
||||
f.setMinimumIntegerDigits(1);
|
||||
return f.format(value);
|
||||
}
|
||||
|
||||
public static Map<Integer, String> getAndroidResMap() {
|
||||
return androidResMap;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,16 +7,8 @@ import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.swing.Icon;
|
||||
import javax.swing.ImageIcon;
|
||||
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.gui.JadxWrapper;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.gui.JadxWrapper;
|
||||
import jadx.gui.treemodel.JResource.JResType;
|
||||
import jadx.gui.utils.Utils;
|
||||
|
||||
import jadx.gui.treemodel.JResource.JResType;
|
||||
import jadx.gui.utils.Utils;
|
||||
|
||||
|
||||
@@ -6,8 +6,6 @@ import javax.swing.table.TableCellRenderer;
|
||||
import javax.swing.table.TableColumn;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.KeyAdapter;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.awt.event.MouseAdapter;
|
||||
@@ -90,12 +88,7 @@ public abstract class CommonSearchDialog extends JDialog {
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowOpened(WindowEvent e) {
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
openInit();
|
||||
}
|
||||
});
|
||||
SwingUtilities.invokeLater(CommonSearchDialog.this::openInit);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -119,12 +112,7 @@ public abstract class CommonSearchDialog extends JDialog {
|
||||
|
||||
protected void initCommon() {
|
||||
KeyStroke stroke = KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0);
|
||||
getRootPane().registerKeyboardAction(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
dispose();
|
||||
}
|
||||
}, stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
getRootPane().registerKeyboardAction(e -> dispose(), stroke, JComponent.WHEN_IN_FOCUSED_WINDOW);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -132,17 +120,9 @@ public abstract class CommonSearchDialog extends JDialog {
|
||||
progressPane = new ProgressPanel(mainWindow, false);
|
||||
|
||||
JButton cancelButton = new JButton(NLS.str("search_dialog.cancel"));
|
||||
cancelButton.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
cancelButton.addActionListener(event -> dispose());
|
||||
JButton openBtn = new JButton(NLS.str("search_dialog.open"));
|
||||
openBtn.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent event) {
|
||||
openSelectedItem();
|
||||
}
|
||||
});
|
||||
openBtn.addActionListener(event -> openSelectedItem());
|
||||
getRootPane().setDefaultButton(openBtn);
|
||||
|
||||
JPanel buttonPane = new JPanel();
|
||||
|
||||
Reference in New Issue
Block a user