Merge upstream

This commit is contained in:
oSumAtrIX 2024-02-14 00:17:46 +01:00
commit b0f3957320
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
36 changed files with 157 additions and 223 deletions

View File

@ -1,3 +0,0 @@
./docker
./github
*.md

View File

@ -72,12 +72,12 @@ jobs:
java: [ 8, 11, 17, 21 ]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: ${{ matrix.java }}
- name: Build and test
uses: gradle/gradle-build-action@v2.9.0
uses: gradle/gradle-build-action@v2.10.0
with:
arguments: build shadowJar proguard
@ -91,12 +91,12 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17
- name: Build
uses: gradle/gradle-build-action@v2.9.0
uses: gradle/gradle-build-action@v2.10.0
with:
dependency-graph: generate-and-submit
arguments: build shadowJar proguard

View File

@ -1,46 +0,0 @@
name: Build and push docker (beta)
on:
push:
branches:
- master
env:
REGISTRY: ghcr.io
IMAGE_NAME: ibotpeaches/apktool
jobs:
build_and_push:
name: Build and push
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Get hash
id: hash
run: echo "APKTOOL_HASH=$(echo $GITHUB_SHA | cut -c 1-6)" >> $GITHUB_ENV
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64
file: ./docker/Dockerfile
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.APKTOOL_HASH }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:snapshot

View File

@ -1,48 +0,0 @@
name: Build and push docker (release)
on:
release:
types: [published]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ibotpeaches/apktool
jobs:
build_and_push:
name: Build and push
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Log in to the Container registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Get hash
id: hash
run: echo "APKTOOL_HASH=$(echo $GITHUB_SHA | cut -c 1-6)" >> $GITHUB_ENV
- name: Get release tag
id: tag
run: echo "APKTOOL_TAG=$(echo ${GITHUB_REF:-no-tag} |sed 's|refs/tags/||g')" >> $GITHUB_ENV
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64
file: ./docker/Dockerfile
push: true
tags: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.APKTOOL_TAG }}
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:latest

View File

@ -17,7 +17,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v3
- uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17

View File

@ -21,7 +21,6 @@ If you discover a security vulnerability within Apktool, please send an e-mail t
- [Downloads Mirror](https://connortumbleson.com/apktool/)
- [How to Build](https://ibotpeaches.github.io/Apktool/build/)
- [Documentation](https://ibotpeaches.github.io/Apktool/documentation/)
- [Use in Docker](./docker/README.md)
- [Bug Reports](https://github.com/iBotPeaches/Apktool/issues)
- [Changelog/Information](https://ibotpeaches.github.io/Apktool/changes/)
- [XDA Post](https://forum.xda-developers.com/t/util-dec-2-2020-apktool-tool-for-reverse-engineering-apk-files.1755243/)

View File

@ -183,6 +183,9 @@ public class AaptInvoker {
// TODO: Add this back, once AAPT2 from platform-tools 34.0.4 is stable
// cmd.add("--no-compile-sdk-metadata");
// #3427 - Ignore stricter parsing during aapt2
cmd.add("--warn-manifest-validation");
if (mApkInfo.sparseResources) {
cmd.add("--enable-sparse-encoding");
}

View File

@ -482,7 +482,7 @@ public class ApkBuilder {
File inputFile;
try {
inputFile = new File(unknownFileDir, BrutIO.sanitizeUnknownFile(unknownFileDir, unknownFileInfo.getKey()));
inputFile = new File(unknownFileDir, BrutIO.sanitizeFilepath(unknownFileDir, unknownFileInfo.getKey()));
} catch (RootUnknownFileException | InvalidUnknownFileException | TraversalUnknownFileException exception) {
LOGGER.warning(String.format("Skipping file %s (%s)", unknownFileInfo.getKey(), exception.getMessage()));
continue;

View File

@ -159,12 +159,12 @@ public class ResourcesDecoder {
decoders.setDecoder("xml", new XmlPullStreamDecoder(axmlParser, getResXmlSerializer()));
ResFileDecoder fileDecoder = new ResFileDecoder(decoders);
Directory in, out;
Directory in, out, outRes;
try {
out = new FileDirectory(outDir);
in = mApkInfo.getApkFile().getDirectory();
out = out.createDir("res");
outRes = out.createDir("res");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
@ -174,14 +174,14 @@ public class ResourcesDecoder {
LOGGER.info("Decoding file-resources...");
for (ResResource res : pkg.listFiles()) {
fileDecoder.decode(res, in, out, mResFileMapping);
fileDecoder.decode(res, in, outRes, mResFileMapping);
}
LOGGER.info("Decoding values */* XMLs...");
for (ResValuesFile valuesFile : pkg.listValuesFiles()) {
generateValuesFile(valuesFile, out, xmlSerializer);
generateValuesFile(valuesFile, outRes, xmlSerializer);
}
generatePublicXml(pkg, out, xmlSerializer);
generatePublicXml(pkg, outRes, xmlSerializer);
}
AndrolibException decodeError = axmlParser.getFirstError();

View File

@ -32,9 +32,7 @@ public class ResValueFactory {
public ResScalarValue factory(int type, int value, String rawValue) throws AndrolibException {
switch (type) {
case TypedValue.TYPE_NULL:
if (value == TypedValue.DATA_NULL_UNDEFINED) { // Special case $empty as explicitly defined empty value
return new ResStringValue(null, value);
} else if (value == TypedValue.DATA_NULL_EMPTY) {
if (value == TypedValue.DATA_NULL_EMPTY) {
return new ResEmptyValue(value, rawValue, type);
}
return new ResReferenceValue(mPackage, 0, null);

View File

@ -275,7 +275,7 @@ public class ARSCDecoder {
int typeFlags = mIn.readByte();
mIn.skipBytes(2); // reserved
int entryCount = mIn.readInt();
mIn.skipInt(); // entriesStart
int entriesStart = mIn.readInt();
ResConfigFlags flags = readConfigFlags();
@ -313,6 +313,13 @@ public class ARSCDecoder {
mType = flags.isInvalid && !mKeepBroken ? null : mPkg.getOrCreateConfig(flags);
int noEntry = isOffset16 ? NO_ENTRY_OFFSET16 : NO_ENTRY;
// #3428 - In some applications the res entries are padded for alignment.
int entriesStartAligned = mHeader.startPosition + entriesStart;
if (mIn.position() < entriesStartAligned) {
long bytesSkipped = mIn.skip(entriesStartAligned - mIn.position());
LOGGER.fine("Skipping: " + bytesSkipped + " byte(s) to align with ResTable_entry start.");
}
for (int i : entryOffsetMap.keySet()) {
mResId = (mResId & 0xffff0000) | i;
int offset = entryOffsetMap.get(i);

View File

@ -25,6 +25,7 @@ import brut.androlib.res.data.value.ResFileValue;
import brut.directory.DirUtil;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import brut.util.BrutIO;
import java.io.*;
import java.util.Map;
@ -44,8 +45,15 @@ public class ResFileDecoder {
ResFileValue fileValue = (ResFileValue) res.getValue();
String inFilePath = fileValue.toString();
String inFileName = fileValue.getStrippedPath();
String outResName = res.getFilePath();
String typeName = res.getResSpec().getType().getName();
String outResName = res.getFilePath();
if (BrutIO.detectPossibleDirectoryTraversal(outResName)) {
outResName = inFileName;
LOGGER.warning(String.format(
"Potentially malicious file path: %s, using instead %s", res.getFilePath(), outResName
));
}
String ext = null;
String outFileName;

View File

@ -194,6 +194,11 @@ public class BuildAndDecodeTest extends BaseTest {
compareXmlFiles("res/xml/references.xml");
}
@Test
public void xmlAccessibilityTest() throws BrutException {
compareXmlFiles("res/xml/accessibility_service_config.xml");
}
@Test
public void xmlXsdFileTest() throws BrutException {
compareXmlFiles("res/xml/ww_box_styles_schema.xsd");

View File

@ -71,6 +71,11 @@ public class BuildAndDecodeTest extends BaseTest {
assertTrue(sTestNewDir.isDirectory());
}
@Test
public void valuesColorsTest() throws BrutException {
compareValuesFiles("values/colors.xml");
}
@Test
public void valuesStringsTest() throws BrutException {
compareValuesFiles("values/strings.xml");
@ -144,6 +149,11 @@ public class BuildAndDecodeTest extends BaseTest {
compareXmlFiles("res/xml/ww_box_styles_schema.xsd");
}
@Test
public void xmlAccessibilityTest() throws BrutException {
compareXmlFiles("res/xml/accessibility_service_config.xml");
}
@Test
public void multipleDexTest() throws BrutException, IOException {
compareBinaryFolder("/smali_classes2", false);

View File

@ -0,0 +1,62 @@
/*
* Copyright (C) 2010 Ryszard Wiśniewski <brut.alll@gmail.com>
* Copyright (C) 2010 Connor Tumbleson <connor.tumbleson@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
*
* https://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 brut.androlib.decode;
import brut.androlib.ApkDecoder;
import brut.androlib.BaseTest;
import brut.androlib.Config;
import brut.androlib.TestUtils;
import brut.common.BrutException;
import brut.directory.ExtFile;
import brut.util.OS;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.io.File;
import java.io.IOException;
import static org.junit.Assert.assertTrue;
public class ResourceDirectoryTraversalTest extends BaseTest {
@BeforeClass
public static void beforeClass() throws Exception {
TestUtils.cleanFrameworkFile();
sTmpDir = new ExtFile(OS.createTempDirectory());
TestUtils.copyResourceDir(ResourceDirectoryTraversalTest.class, "decode/arbitrary-write/", sTmpDir);
}
@AfterClass
public static void afterClass() throws BrutException {
OS.rmdir(sTmpDir);
}
@Test
public void checkIfMaliciousRawFileIsDisassembledProperly() throws BrutException, IOException {
String apk = "GHSA-2hqv-2xv4-5h5w.apk";
Config config = Config.getDefaultConfig();
config.forceDelete = true;
ApkDecoder apkDecoder = new ApkDecoder(config, new File(sTmpDir + File.separator + apk));
File outDir = new File(sTmpDir + File.separator + apk + ".out");
apkDecoder.decode(outDir);
File pocTestFile = new File(outDir,"res/raw/poc");
assertTrue(pocTestFile.exists());
}
}

View File

@ -51,7 +51,7 @@ public class UnknownDirectoryTraversalTest extends BaseTest {
@Test
public void validFileTest() throws IOException, BrutException {
String validFilename = BrutIO.sanitizeUnknownFile(sTmpDir, "file");
String validFilename = BrutIO.sanitizeFilepath(sTmpDir, "file");
assertEquals(validFilename, "file");
File validFile = new File(sTmpDir, validFilename);
@ -60,18 +60,18 @@ public class UnknownDirectoryTraversalTest extends BaseTest {
@Test(expected = TraversalUnknownFileException.class)
public void invalidBackwardFileTest() throws IOException, BrutException {
BrutIO.sanitizeUnknownFile(sTmpDir, "../file");
BrutIO.sanitizeFilepath(sTmpDir, "../file");
}
@Test(expected = RootUnknownFileException.class)
public void invalidRootFileTest() throws IOException, BrutException {
String rootLocation = OSDetection.isWindows() ? "C:/" : File.separator;
BrutIO.sanitizeUnknownFile(sTmpDir, rootLocation + "file");
BrutIO.sanitizeFilepath(sTmpDir, rootLocation + "file");
}
@Test(expected = InvalidUnknownFileException.class)
public void noFilePassedTest() throws IOException, BrutException {
BrutIO.sanitizeUnknownFile(sTmpDir, "");
BrutIO.sanitizeFilepath(sTmpDir, "");
}
@Test(expected = TraversalUnknownFileException.class)
@ -83,12 +83,12 @@ public class UnknownDirectoryTraversalTest extends BaseTest {
invalidPath = "..\\..\\app.exe";
}
BrutIO.sanitizeUnknownFile(sTmpDir, invalidPath);
BrutIO.sanitizeFilepath(sTmpDir, invalidPath);
}
@Test
public void validDirectoryFileTest() throws IOException, BrutException {
String validFilename = BrutIO.sanitizeUnknownFile(sTmpDir, "dir" + File.separator + "file");
String validFilename = BrutIO.sanitizeFilepath(sTmpDir, "dir" + File.separator + "file");
assertEquals("dir" + File.separator + "file", validFilename);
}
}

View File

@ -2,7 +2,12 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:appCategory="game" android:compileSdkVersion="23" android:compileSdkVersionCodename="6.0-2438415" package="brut.apktool.testapp" platformBuildVersionCode="23" platformBuildVersionName="6.0-2438415">
<uses-feature android:glEsVersion="0x00020000" />
<uses-feature android:glEsVersion="0x00030002" />
<application android:label="Issue2799 &amp; B">
<application android:label="Issue2799 &amp; B" android:isAccessibilityTool="false">
<service android:name=".MyAccessibilityService">
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config" />
</service>
<meta-data name="test_int_as_string" value="\ 12345" />
<meta-data name="test_int" value="12345" />
</application>

View File

@ -0,0 +1,3 @@
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:isAccessibilityTool="true"
/>

View File

@ -1,6 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:appCategory="game" android:compileSdkVersion="23" android:compileSdkVersionCodename="6.0-2438415" package="brut.apktool.aapt1.testapp" platformBuildVersionCode="23" platformBuildVersionName="6.0-2438415">
<application android:label="Issue2799 &amp; B" android:enableOnBackInvokedCallback="true">
<application android:label="Issue2799 &amp; B" android:enableOnBackInvokedCallback="true" android:isAccessibilityTool="false">
<service android:name=".MyAccessibilityService">
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/accessibility_service_config" />
</service>
<fragment android:name=".views.Issue3427"/>
<meta-data name="test_int_as_string" value="\ 12345" />
<meta-data name="test_int" value="12345" />
</application>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="test_color1">#ff123456</color>
<color name="test_color2">@android:color/white</color>
<color name="test_color3">#00000000</color>
<color name="issue_3416">@null</color>
</resources>

View File

@ -0,0 +1,3 @@
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:isAccessibilityTool="true"
/>

View File

@ -89,7 +89,7 @@ public class DirUtil {
} else if (!in.containsDir(fileName) && !in.containsFile(fileName)) {
// Skip copies of directories/files not found.
} else {
String cleanedFilename = BrutIO.sanitizeUnknownFile(out, fileName);
String cleanedFilename = BrutIO.sanitizeFilepath(out, fileName);
if (! cleanedFilename.isEmpty()) {
File outFile = new File(out, cleanedFilename);
//noinspection ResultOfMethodCallIgnored

View File

@ -119,7 +119,7 @@ public class FileDirectory extends AbstractDirectory {
}
}
private File getDir() {
public File getDir() {
return mDir;
}
}

View File

@ -59,8 +59,8 @@ public class ZipUtils {
throws BrutException, IOException {
for (final File file : folder.listFiles()) {
if (file.isFile()) {
final String cleanedPath = BrutIO.sanitizeUnknownFile(folder, file.getPath().substring(prefixLength));
final ZipEntry zipEntry = new ZipEntry(BrutIO.normalizePath(cleanedPath));
final String cleanedPath = BrutIO.sanitizeFilepath(folder, file.getPath().substring(prefixLength));
final ZipEntry zipEntry = new ZipEntry(BrutIO.adaptSeparatorToUnix(cleanedPath));
// aapt binary by default takes in parameters via -0 arsc to list extensions that shouldn't be
// compressed. We will replicate that behavior

View File

@ -74,8 +74,8 @@ public class BrutIO {
return crc;
}
public static String sanitizeUnknownFile(final File directory, final String entry) throws IOException, BrutException {
if (entry.length() == 0) {
public static String sanitizeFilepath(final File directory, final String entry) throws IOException, BrutException {
if (entry.isEmpty()) {
throw new InvalidUnknownFileException("Invalid Unknown File");
}
@ -94,7 +94,11 @@ public class BrutIO {
return canonicalEntryPath.substring(canonicalDirPath.length());
}
public static String normalizePath(String path) {
public static boolean detectPossibleDirectoryTraversal(String entry) {
return entry.contains("../") || entry.contains("/..") || entry.contains("..\\") || entry.contains("\\..");
}
public static String adaptSeparatorToUnix(String path) {
char separator = File.separatorChar;
if (separator != '/') {

View File

@ -1,7 +1,7 @@
import java.io.ByteArrayOutputStream
val version = "2.9.1"
val suffix = "SNAPSHOT"
val version = "2.9.3"
val suffix = ""
// Strings embedded into the build.
var gitRevision by extra("")

View File

@ -1,62 +0,0 @@
# BUILDER
# =====================================================
FROM ibm-semeru-runtimes:open-17-jdk-jammy AS builder
COPY . /build
WORKDIR /build
RUN \
# Apktool
./gradlew build shadowJar proguard
RUN \
# Relocate for easier copying
JAR=$(find /build/brut.apktool/apktool-cli/build/libs/apktool-*.jar |grep -v cli-all) &&\
mv ${JAR} /build/apktool.jar
# BASE
# =====================================================
FROM ibm-semeru-runtimes:open-17-jre-jammy AS base
# Latest version as of 2023.10.01
# Ref: https://developer.android.com/studio/index.html#command-line-tools-only
ARG COMMAND_LINE_TOOLS_VERSION=10406996
ARG ANDROID_SDK_ROOT=/opt/android-sdk
COPY --from=builder /build/apktool.jar /usr/local/bin/apktool.jar
COPY --from=builder /build/scripts/linux/apktool /usr/local/bin/apktool
RUN \
# Update
apt-get update &&\
\
# Apktool final setup
chmod +x /usr/local/bin/apktool /usr/local/bin/apktool.jar &&\
\
# Android SDK
apt-get install -y --no-install-recommends \
ca-certificates \
curl \
zipalign \
git \
openssl \
wget \
unzip \
sdkmanager &&\
curl -o /tmp/tools.zip https://dl.google.com/android/repository/commandlinetools-linux-${COMMAND_LINE_TOOLS_VERSION}_latest.zip &&\
mkdir -p ${ANDROID_SDK_ROOT}/cmdline-tools &&\
unzip -q /tmp/tools.zip -d ${ANDROID_SDK_ROOT}/cmdline-tools &&\
mv ${ANDROID_SDK_ROOT}/cmdline-tools/cmdline-tools ${ANDROID_SDK_ROOT}/cmdline-tools/latest &&\
rm -v /tmp/tools.zip &&\
mkdir -p /root/.android/ && touch /root/.android/repositories.cfg &&\
yes | sdkmanager --licenses &&\
export BUILD_TOOLS_VERSION=$(sdkmanager --list |grep build-tools |grep -v rc |awk '{print $1}' |sed 's/build-tools;//g' |sort |tail -n1) &&\
sdkmanager --install "build-tools;${BUILD_TOOLS_VERSION}" &&\
ln -s ${ANDROID_SDK_ROOT}/build-tools/${BUILD_TOOLS_VERSION} /opt/bin &&\
\
# Cleanup
rm -rf /var/lib/apt/lists/*
ENV PATH ${PATH}:/opt/bin
CMD ["/usr/local/bin/apktool"]

View File

@ -1,27 +0,0 @@
# Apktool in Docker
We provide an easy way to leverage `apktool`, along with common Android tools such as `zipalign` and `apksigner`, all from within Docker.
## Building the Docker image
To use the image, pull the pre-built image or build one from the repo root with the included Dockerfile:
```bash
docker pull ghcr.io/ibotpeaches/apktool:latest
# OR
docker build -t apktool:local -f docker/Dockerfile .
```
## Using the Docker image
The best way to use the image is to create aliases to run the internal commands. Replace `ghcr.io/ibotpeaches/apktool:latest` with `apktool:local` if you have built the image locally.
```bash
alias apktool="docker run --rm -ti --name=apktool -v \"${PWD}:${PWD}\" -w \"${PWD}\" ghcr.io/ibotpeaches/apktool:latest apktool"
alias zipalign="docker run --rm -ti --name=zipalign -v \"${PWD}:${PWD}\" -w \"${PWD}\" ghcr.io/ibotpeaches/apktool:latest zipalign"
alias apksigner="docker run --rm -ti --name=apksigner -v \"${PWD}:${PWD}\" -w \"${PWD}\" ghcr.io/ibotpeaches/apktool:latest apksigner"
```
## Running the commands
You can then utilize these commands as you would if they were natively installed:
```bash
apktool d My.apk -o MyFolder
apktool b MyFolder -o MyNew.apk
zipalign -p -f 4 MyNew.apk MyNewAligned.apk
apksigner sign --ks My.keystore MyNewAligned.apk
```

View File

@ -1,12 +1,12 @@
[versions]
baksmali = "3.0.3"
commons_io = "2.15.0"
commons_io = "2.15.1"
commons_cli = "1.6.0"
commons_lang3 = "3.13.0"
commons_lang3 = "3.14.0"
commons_text = "1.11.0"
guava = "32.0.1-jre"
junit = "4.13.2"
proguard = "7.4.0"
proguard = "7.4.1"
shadow = "8.1.1"
smali = "3.0.3"
xmlpull = "1.1.4c"