-
Notifications
You must be signed in to change notification settings - Fork 162
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #133 from REAndroid/stronger-protector
Strong resource protection
- Loading branch information
Showing
9 changed files
with
586 additions
and
105 deletions.
There are no files selected for viewing
87 changes: 87 additions & 0 deletions
87
src/main/java/com/reandroid/apkeditor/protect/Confuser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
128
src/main/java/com/reandroid/apkeditor/protect/DirectoryConfuser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
114
src/main/java/com/reandroid/apkeditor/protect/FileNameConfuser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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", | ||
}; | ||
} | ||
} |
55 changes: 55 additions & 0 deletions
55
src/main/java/com/reandroid/apkeditor/protect/ManifestConfuser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |
Oops, something went wrong.