Skip to content

Commit

Permalink
Merge pull request #133 from REAndroid/stronger-protector
Browse files Browse the repository at this point in the history
Strong resource protection
  • Loading branch information
REAndroid authored Oct 1, 2024
2 parents 5c15e00 + 2dff0e0 commit c9c30b2
Show file tree
Hide file tree
Showing 9 changed files with 586 additions and 105 deletions.
87 changes: 87 additions & 0 deletions src/main/java/com/reandroid/apkeditor/protect/Confuser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* 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.
*/
package com.reandroid.apkeditor.protect;

import com.reandroid.apk.APKLogger;
import com.reandroid.apk.ApkModule;
import com.reandroid.archive.InputSource;
import com.reandroid.archive.ZipEntryMap;
import com.reandroid.utils.collection.CollectionUtil;
import com.reandroid.utils.collection.ComputeIterator;

import java.util.Set;

public abstract class Confuser implements APKLogger {

private final Protector protector;
private final String logTag;
private Set<String> filePaths;

public Confuser(Protector protector, String logTag) {
this.protector = protector;
this.logTag = logTag;
}

public abstract void confuse();


public boolean containsFilePath(String path) {
return getFilePaths().contains(path);
}
public void onPathChanged(String original, String newPath) {
Set<String> filePaths = getFilePaths();
filePaths.add(newPath);
logVerbose(original + " -> " + newPath);
}
public Set<String> getFilePaths() {
if (this.filePaths == null) {

ZipEntryMap zipEntryMap = getApkModule().getZipEntryMap();

this.filePaths = CollectionUtil.toHashSet(
ComputeIterator.of(zipEntryMap.iterator(), InputSource::getAlias));
this.filePaths.addAll(CollectionUtil.toHashSet(
ComputeIterator.of(zipEntryMap.iterator(), InputSource::getName)));

}
return filePaths;
}
public boolean isKeepType(String type) {
return getOptions().keepTypes.contains(type);
}
public Protector getProtector() {
return protector;
}
public ProtectorOptions getOptions() {
return getProtector().getOptions();
}
public ApkModule getApkModule() {
return getProtector().getApkModule();
}

@Override
public void logMessage(String msg) {
protector.logMessage(logTag + msg);
}
@Override
public void logError(String msg, Throwable tr) {
protector.logError(msg, tr);
}
@Override
public void logVerbose(String msg) {
protector.logVerbose(msg);
}
}
128 changes: 128 additions & 0 deletions src/main/java/com/reandroid/apkeditor/protect/DirectoryConfuser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* 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.
*/
package com.reandroid.apkeditor.protect;

import com.reandroid.apk.ApkModule;
import com.reandroid.apk.DexFileInputSource;
import com.reandroid.apk.ResFile;
import com.reandroid.apk.UncompressedFiles;
import com.reandroid.apkeditor.utils.CyclicIterator;
import com.reandroid.archive.Archive;
import com.reandroid.utils.collection.CollectionUtil;

import java.util.List;

public class DirectoryConfuser extends Confuser {

private final CyclicIterator<String> namesIterator;

public DirectoryConfuser(Protector protector) {
super(protector, "DirectoryConfuser: ");
this.namesIterator = new CyclicIterator<>(loadDirNameList(protector.getApkModule()));
}

@Override
public void confuse() {
logMessage("Confusing ...");

ApkModule apkModule = getApkModule();
UncompressedFiles uf = apkModule.getUncompressedFiles();

for(ResFile resFile : getApkModule().listResFiles()){
int method = resFile.getInputSource().getMethod();
String pathNew = generateNewPath(resFile);
if(pathNew != null) {
String path = resFile.getFilePath();
if(method == Archive.STORED) {
uf.replacePath(path, pathNew);
}
resFile.setFilePath(pathNew);
onPathChanged(path, pathNew);
}
}
}
private String generateNewPath(ResFile resFile) {
if (isKeepType(resFile.pickOne().getTypeName())) {
return null;
}
return generateNewPath(resFile.getFilePath());
}
private String generateNewPath(String path) {
CyclicIterator<String> iterator = this.namesIterator;
iterator.resetCycleCount();
while (iterator.getCycleCount() == 0) {
String newPath = replaceDirectory(path, iterator.next());
if (!containsFilePath(newPath)) {
return newPath;
}
}
return null;
}

private static String replaceDirectory(String path, String dirName) {
int i = path.lastIndexOf('/');
if (i < 0) {
i = 0;
} else {
i = i + 1;
if (i == path.length()) {
i = i - 1;
}
}
String simpleName = path.substring(i);
if (dirName.length() != 0) {
dirName = dirName + "/";
}
return dirName + simpleName;
}
private static String[] loadDirNameList(ApkModule apkModule) {
List<String> nameList = CollectionUtil.asList(
"AndroidManifest.xml",
"/AndroidManifest.xml",
"resources.arsc",
"/resources.arsc",
"classes.dex",
"/classes.dex",
"kotlin",
"META-INF",
"",
"kotlin/annotation",
"kotlin/collections",
"kotlin/coroutines",
"kotlin/internal",
"kotlin/ranges",
"kotlin/reflect",
"res/values/arrays.xml",
"res/values/attrs.xml",
"res/values/bools.xml",
"res/values/colors.xml",
"res/values/dimens.xml",
"res/values/drawables.xml",
"res/values/ids.xml",
"res/values/integers.xml",
"res/values/plurals.xml",
"res/values/public.xml",
"res/values/strings.xml",
"res/values/styles.xml"
);
List<DexFileInputSource> dexList = apkModule.listDexFiles();
int size = dexList.size();
for (int i = 1; i < size; i++) {
nameList.add(dexList.get(i).getAlias());
}
return nameList.toArray(new String[0]);
}
}
114 changes: 114 additions & 0 deletions src/main/java/com/reandroid/apkeditor/protect/FileNameConfuser.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* 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.
*/
package com.reandroid.apkeditor.protect;

import com.reandroid.apk.ApkModule;
import com.reandroid.apk.ResFile;
import com.reandroid.apk.UncompressedFiles;
import com.reandroid.apkeditor.utils.CyclicIterator;
import com.reandroid.archive.Archive;

public class FileNameConfuser extends Confuser {

private final CyclicIterator<String> namesIterator;

public FileNameConfuser(Protector protector) {
super(protector, "FileNameConfuser: ");
this.namesIterator = new CyclicIterator<>(loadFileNames());
}

@Override
public void confuse() {
logMessage("Confusing ...");

ApkModule apkModule = getApkModule();
UncompressedFiles uf = apkModule.getUncompressedFiles();

for(ResFile resFile : getApkModule().listResFiles()){
int method = resFile.getInputSource().getMethod();
String pathNew = generateNewPath(resFile);
if(pathNew != null) {
String path = resFile.getFilePath();
if(method == Archive.STORED) {
uf.replacePath(path, pathNew);
}
resFile.setFilePath(pathNew);
onPathChanged(path, pathNew);
}
}
}
private String generateNewPath(ResFile resFile) {
if (isKeepType(resFile.pickOne().getTypeName())) {
return null;
}
return generateNewPath(resFile.getFilePath());
}

private String generateNewPath(String path) {
CyclicIterator<String> iterator = this.namesIterator;
iterator.resetCycleCount();
while (iterator.getCycleCount() == 0) {
String newPath = replaceSimpleName(path, iterator.next());
if (!containsFilePath(newPath)) {
return newPath;
}
}
return null;
}

private static String replaceSimpleName(String path, String symbol) {
int i = path.lastIndexOf('/');
String dirName;
String simpleName;
if (i < 0) {
dirName = "";
simpleName = path;
} else {
i = i + 1;
dirName = path.substring(0, i);
simpleName = path.substring(i);
}
i = simpleName.lastIndexOf('.');
String ext;
if (i < 0) {
ext = "";
} else {
if (simpleName.endsWith(".9.png")) {
ext = ".9.png";
} else {
ext = simpleName.substring(i);
}
}
return dirName + symbol + ext;
}
private static String[] loadFileNames() {
return new String[]{
".",
"//",
"///",
"////",
"\\\\",
"\\\\\\",
"\\/",
" ",
" ",
"classes.dex",
"AndroidManifest.xml",
"AndroidManifest",
"resources.arsc",
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright (C) 2022 github.com/REAndroid
*
* 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.
*/
package com.reandroid.apkeditor.protect;

import com.reandroid.apk.ApkModule;
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
import com.reandroid.arsc.chunk.xml.ResXmlAttribute;
import com.reandroid.arsc.chunk.xml.ResXmlElement;
import com.reandroid.utils.collection.CollectionUtil;

import java.util.List;
import java.util.Random;

public class ManifestConfuser extends Confuser {

public ManifestConfuser(Protector protector) {
super(protector, "ManifestConfuser: ");
}

@Override
public void confuse() {
if (getOptions().skipManifest) {
logMessage("Skip");
return;
}
ApkModule apkModule = getApkModule();
AndroidManifestBlock manifestBlock = apkModule.getAndroidManifest();
int defaultAttributeSize = 20;
List<ResXmlElement> elementList = CollectionUtil.toList(manifestBlock.recursiveElements());
Random random = new Random();
for (ResXmlElement element : elementList) {
int size = defaultAttributeSize + random.nextInt(6) + 1;
element.setAttributesUnitSize(size, false);
ResXmlAttribute attribute = element.newAttribute();
attribute.setName(" >\n </" + element.getName() + ">\n android:name", 0);
attribute.setValueAsBoolean(false);
}
manifestBlock.getManifestElement().setAttributesUnitSize(
defaultAttributeSize, false);
manifestBlock.refresh();
}
}
Loading

0 comments on commit c9c30b2

Please sign in to comment.