diff --git a/.gitignore b/.gitignore index 3d315ee19..ede55e996 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,21 @@ -obj -libs +obj/ +libs/ *.zip + +# Generated binaries +zip_static/arm/* +zip_static/arm64/* +zip_static/x86/* +zip_static/x64/* +uninstaller/arm/* +uninstaller/arm64/* +uninstaller/x86/* +uninstaller/x64/* +zipsigntools/zipadjust + +# Generated scripts +zip_static/common/magic_mask.sh +zip_static/META-INF/com/google/android/update-binary + +# Leave all busybox! +!busybox \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100755 index 000000000..82f357c72 --- /dev/null +++ b/build.sh @@ -0,0 +1,127 @@ +#!/bin/bash + +usage() { + echo "$0 --all " + echo -e "\tBuild binaries, zip, and sign Magisk" + echo -e "\tThis is equlivant to first --build, then --zip" + echo "$0 --clean" + echo -e "\tCleanup compiled / generated files" + echo "$0 --build" + echo -e "\tBuild the binaries with ndk" + echo "$0 --zip " + echo -e "\tZip and sign Magisk" + echo "$0 --uninstaller" + echo -e "\tZip and sign the uninstaller" + exit 1 +} + +cleanup() { + echo "************************" + echo "* Cleaning up" + echo "************************" + ndk-build clean + ls zip_static/arm/* | grep -v "busybox" | xargs rm -rfv + ls zip_static/arm64/* | grep -v "busybox" | xargs rm -rfv + ls zip_static/x86/* | grep -v "busybox" | xargs rm -rfv + ls zip_static/x64/* | grep -v "busybox" | xargs rm -rfv + rm -rfv zip_static/META-INF/com/google/android/update-binary + rm -rfv zip_static/common/magic_mask.sh + rm -rfv uninstaller/arm + rm -rfv uninstaller/arm64 + rm -rfv uninstaller/x86 + rm -rfv uninstaller/x64 +} + +mkcp() { + [ ! -d "$2" ] && mkdir -p "$2" + cp -afv $1 $2 +} + +build_bin() { + echo "************************" + echo "* Building binaries" + echo "************************" + ndk-build -j4 + echo "************************" + echo "* Copying binaries" + echo "************************" + mkcp "libs/armeabi/*" zip_static/arm + mkcp libs/armeabi/bootimgtools uninstaller/arm + mkcp "libs/arm64-v8a/*" zip_static/arm64 + mkcp libs/arm64-v8a/bootimgtools uninstaller/arm64 + mkcp "libs/x86/*" zip_static/x86 + mkcp libs/x86/bootimgtools uninstaller/x86 + mkcp "libs/x86_64/*" zip_static/x64 + mkcp libs/x86_64/bootimgtools uninstaller/x64 +} + +zip_package() { + echo "************************" + echo "* Adding version info" + echo "* \"Magisk v$1\"" + echo "************************" + sed "s/MAGISK_VERSION_STUB/Magisk v$1 Boot Image Patcher/g" scripts/flash_script.sh > zip_static/META-INF/com/google/android/update-binary + sed "s/MAGISK_VERSION_STUB/setprop magisk.version $1/g" scripts/magic_mask.sh > zip_static/common/magic_mask.sh + echo "************************" + echo "* Zipping the package" + echo "************************" + cd zip_static + find . -type f -exec chmod 644 {} \; + find . -type d -exec chmod 755 {} \; + rm -rf "../Magisk-v$1.zip" + zip "../Magisk-v$1.zip" -r . + cd ../ + sign_zip "Magisk-v$1.zip" +} + +sign_zip() { + if [ ! -f "zipsigntools/zipadjust" ]; then + echo "************************" + echo "* Compiling ZipAdjust" + echo "************************" + gcc -o zipsigntools/zipadjust zipsigntools/src/*.c -lz + chmod 755 zipsigntools/zipadjust + fi + echo "************************" + echo "* First sign $1" + echo "************************" + java -jar "zipsigntools/signapk.jar" "zipsigntools/test.certificate.x509.pem" "zipsigntools/test.key.pk8" "$1" "${1%.*}-firstsign.zip" + echo "************************" + echo "* Adjusting $1" + echo "************************" + zipsigntools/zipadjust "${1%.*}-firstsign.zip" "${1%.*}-adjusted.zip" + echo "************************" + echo "* Final sign $1" + echo "************************" + java -jar "zipsigntools/signapk.jar" "zipsigntools/test.certificate.x509.pem" "zipsigntools/test.key.pk8" "${1%.*}-adjusted.zip" "${1%.*}-signed.zip" + + mv "${1%.*}-signed.zip" "$1" + rm "${1%.*}-adjusted.zip" "${1%.*}-firstsign.zip" +} + +DIR="$(cd "$(dirname "$0")"; pwd)" +cd "$DIR" + +case $1 in + "--all" ) + [ -z "$2" ] && echo -e "! Missing version number\n" && usage + build_bin + zip_package $2 + ;; + "--clean" ) + cleanup + ;; + "--build" ) + build_bin + ;; + "--zip" ) + [ -z "$2" ] && echo -e "! Missing version number\n" && usage + zip_package $2 + ;; + "--uninstaller" ) + zip_uninstaller + ;; + * ) + usage + ;; +esac diff --git a/zip_static/META-INF/com/google/android/update-binary b/scripts/flash_script.sh similarity index 99% rename from zip_static/META-INF/com/google/android/update-binary rename to scripts/flash_script.sh index faea90361..ab2c494d7 100644 --- a/zip_static/META-INF/com/google/android/update-binary +++ b/scripts/flash_script.sh @@ -184,9 +184,9 @@ repack_boot() { # Detection ########################################################################################## -ui_print "****************************" -ui_print "Magisk v8 Boot Image Patcher" -ui_print "****************************" +ui_print "*****************************" +ui_print "MAGISK_VERSION_STUB" +ui_print "*****************************" if [ ! -d "$INSTALLER/common" ]; then ui_print "! Failed: Unable to extract zip file!" diff --git a/zip_static/common/magic_mask.sh b/scripts/magic_mask.sh similarity index 99% rename from zip_static/common/magic_mask.sh rename to scripts/magic_mask.sh index 506d92280..b1d08e874 100644 --- a/zip_static/common/magic_mask.sh +++ b/scripts/magic_mask.sh @@ -396,7 +396,7 @@ case $1 in service ) # Version info - setprop magisk.version 8 + MAGISK_VERSION_STUB log_print "Magisk late_start service mode running..." run_scripts service [ -f "$COREDIR/magiskhide/enable" ] && setprop magisk.hide 1 diff --git a/uninstaller/arm/bootimgtools b/uninstaller/arm/bootimgtools deleted file mode 100644 index 60b66bdcf..000000000 Binary files a/uninstaller/arm/bootimgtools and /dev/null differ diff --git a/uninstaller/x86/bootimgtools b/uninstaller/x86/bootimgtools deleted file mode 100644 index f121f22dc..000000000 Binary files a/uninstaller/x86/bootimgtools and /dev/null differ diff --git a/zip_static/arm/bootimgtools b/zip_static/arm/bootimgtools deleted file mode 100644 index 528617f1c..000000000 Binary files a/zip_static/arm/bootimgtools and /dev/null differ diff --git a/zip_static/arm/magiskhide b/zip_static/arm/magiskhide deleted file mode 100644 index 6c2cfaae0..000000000 Binary files a/zip_static/arm/magiskhide and /dev/null differ diff --git a/zip_static/arm/sepolicy-inject b/zip_static/arm/sepolicy-inject deleted file mode 100644 index b27c74c48..000000000 Binary files a/zip_static/arm/sepolicy-inject and /dev/null differ diff --git a/zip_static/arm64/bootimgtools b/zip_static/arm64/bootimgtools deleted file mode 100644 index ab4215f46..000000000 Binary files a/zip_static/arm64/bootimgtools and /dev/null differ diff --git a/zip_static/arm64/magiskhide b/zip_static/arm64/magiskhide deleted file mode 100644 index 39fc0aa05..000000000 Binary files a/zip_static/arm64/magiskhide and /dev/null differ diff --git a/zip_static/arm64/sepolicy-inject b/zip_static/arm64/sepolicy-inject deleted file mode 100644 index dc81ed92b..000000000 Binary files a/zip_static/arm64/sepolicy-inject and /dev/null differ diff --git a/zip_static/x64/bootimgtools b/zip_static/x64/bootimgtools deleted file mode 100644 index 2ec261831..000000000 Binary files a/zip_static/x64/bootimgtools and /dev/null differ diff --git a/zip_static/x64/magiskhide b/zip_static/x64/magiskhide deleted file mode 100644 index 4dee3a4c7..000000000 Binary files a/zip_static/x64/magiskhide and /dev/null differ diff --git a/zip_static/x64/sepolicy-inject b/zip_static/x64/sepolicy-inject deleted file mode 100644 index b27b45393..000000000 Binary files a/zip_static/x64/sepolicy-inject and /dev/null differ diff --git a/zip_static/x86/bootimgtools b/zip_static/x86/bootimgtools deleted file mode 100644 index c38d596e2..000000000 Binary files a/zip_static/x86/bootimgtools and /dev/null differ diff --git a/zip_static/x86/magiskhide b/zip_static/x86/magiskhide deleted file mode 100644 index 065e9ac4b..000000000 Binary files a/zip_static/x86/magiskhide and /dev/null differ diff --git a/zip_static/x86/sepolicy-inject b/zip_static/x86/sepolicy-inject deleted file mode 100644 index d7a4d7373..000000000 Binary files a/zip_static/x86/sepolicy-inject and /dev/null differ diff --git a/zipsigntools/minsignapk.jar b/zipsigntools/minsignapk.jar new file mode 100644 index 000000000..cd38d23fa Binary files /dev/null and b/zipsigntools/minsignapk.jar differ diff --git a/zipsigntools/signapk.jar b/zipsigntools/signapk.jar new file mode 100644 index 000000000..8435b757a Binary files /dev/null and b/zipsigntools/signapk.jar differ diff --git a/zipsigntools/src/MinSignAPK.java b/zipsigntools/src/MinSignAPK.java new file mode 100644 index 000000000..78508175a --- /dev/null +++ b/zipsigntools/src/MinSignAPK.java @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * 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. + */ +/* This is just a copy/paste/cut job from original SignAPK sources. This + * adaptation adds only the whole-file signature to a ZIP(jar,apk) file, and + * doesn't do any of the per-file signing, creating manifests, etc. This is + * useful when you've changed the structure itself of an existing (signed!) + * ZIP file, but the extracted contents are still identical. Using + * the normal SignAPK may re-arrange other things inside the ZIP, which may + * be unwanted behavior. This version only changes the ZIP's tail and keeps + * the rest the same - CF + */ + +package eu.chainfire.minsignapk; + +import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.GeneralSecurityException; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.Signature; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.security.spec.PKCS8EncodedKeySpec; + +import sun.security.pkcs.ContentInfo; +import sun.security.pkcs.PKCS7; +import sun.security.pkcs.SignerInfo; +import sun.security.x509.AlgorithmId; +import sun.security.x509.X500Name; + +public class MinSignAPK { + /** Write a .RSA file with a digital signature. */ + private static void writeSignatureBlock(Signature signature, X509Certificate publicKey, OutputStream out) + throws IOException, GeneralSecurityException { + SignerInfo signerInfo = new SignerInfo(new X500Name(publicKey.getIssuerX500Principal().getName()), + publicKey.getSerialNumber(), AlgorithmId.get("SHA1"), AlgorithmId.get("RSA"), signature.sign()); + + PKCS7 pkcs7 = new PKCS7(new AlgorithmId[] { AlgorithmId.get("SHA1") }, new ContentInfo(ContentInfo.DATA_OID, + null), new X509Certificate[] { publicKey }, new SignerInfo[] { signerInfo }); + + pkcs7.encodeSignedData(out); + } + + private static void signWholeOutputFile(byte[] zipData, OutputStream outputStream, X509Certificate publicKey, + PrivateKey privateKey) throws IOException, GeneralSecurityException { + + // For a zip with no archive comment, the + // end-of-central-directory record will be 22 bytes long, so + // we expect to find the EOCD marker 22 bytes from the end. + if (zipData[zipData.length - 22] != 0x50 || zipData[zipData.length - 21] != 0x4b + || zipData[zipData.length - 20] != 0x05 || zipData[zipData.length - 19] != 0x06) { + throw new IllegalArgumentException("zip data already has an archive comment"); + } + + Signature signature = Signature.getInstance("SHA1withRSA"); + signature.initSign(privateKey); + signature.update(zipData, 0, zipData.length - 2); + + ByteArrayOutputStream temp = new ByteArrayOutputStream(); + + // put a readable message and a null char at the start of the + // archive comment, so that tools that display the comment + // (hopefully) show something sensible. + // TODO: anything more useful we can put in this message? + byte[] message = "signed by SignApk".getBytes("UTF-8"); + temp.write(message); + temp.write(0); + writeSignatureBlock(signature, publicKey, temp); + int total_size = temp.size() + 6; + if (total_size > 0xffff) { + throw new IllegalArgumentException("signature is too big for ZIP file comment"); + } + // signature starts this many bytes from the end of the file + int signature_start = total_size - message.length - 1; + temp.write(signature_start & 0xff); + temp.write((signature_start >> 8) & 0xff); + // Why the 0xff bytes? In a zip file with no archive comment, + // bytes [-6:-2] of the file are the little-endian offset from + // the start of the file to the central directory. So for the + // two high bytes to be 0xff 0xff, the archive would have to + // be nearly 4GB in side. So it's unlikely that a real + // commentless archive would have 0xffs here, and lets us tell + // an old signed archive from a new one. + temp.write(0xff); + temp.write(0xff); + temp.write(total_size & 0xff); + temp.write((total_size >> 8) & 0xff); + temp.flush(); + + // Signature verification checks that the EOCD header is the + // last such sequence in the file (to avoid minzip finding a + // fake EOCD appended after the signature in its scan). The + // odds of producing this sequence by chance are very low, but + // let's catch it here if it does. + byte[] b = temp.toByteArray(); + for (int i = 0; i < b.length - 3; ++i) { + if (b[i] == 0x50 && b[i + 1] == 0x4b && b[i + 2] == 0x05 && b[i + 3] == 0x06) { + throw new IllegalArgumentException("found spurious EOCD header at " + i); + } + } + + outputStream.write(zipData, 0, zipData.length - 2); + outputStream.write(total_size & 0xff); + outputStream.write((total_size >> 8) & 0xff); + temp.writeTo(outputStream); + } + + private static PrivateKey readPrivateKey(File file) + throws IOException, GeneralSecurityException { + DataInputStream input = new DataInputStream(new FileInputStream(file)); + try { + byte[] bytes = new byte[(int) file.length()]; + input.read(bytes); + + // dont support encrypted keys atm + //KeySpec spec = decryptPrivateKey(bytes, file); + //if (spec == null) { + KeySpec spec = new PKCS8EncodedKeySpec(bytes); + //} + + try { + return KeyFactory.getInstance("RSA").generatePrivate(spec); + } catch (InvalidKeySpecException ex) { + return KeyFactory.getInstance("DSA").generatePrivate(spec); + } + } finally { + input.close(); + } + } + + private static X509Certificate readPublicKey(File file) + throws IOException, GeneralSecurityException { + FileInputStream input = new FileInputStream(file); + try { + CertificateFactory cf = CertificateFactory.getInstance("X.509"); + return (X509Certificate) cf.generateCertificate(input); + } finally { + input.close(); + } + } + + public static void main(String[] args) { + if (args.length < 4) { + System.out.println("MinSignAPK pemfile pk8file inzip outzip"); + System.out.println("- only adds whole-file signature to zip"); + return; + } + + String pemFile = args[0]; + String pk8File = args[1]; + String inFile = args[2]; + String outFile = args[3]; + + try { + X509Certificate publicKey = readPublicKey(new File(pemFile)); + PrivateKey privateKey = readPrivateKey(new File(pk8File)); + + InputStream fis = new FileInputStream(inFile); + byte[] buffer = new byte[(int)(new File(inFile)).length()]; + fis.read(buffer); + fis.close(); + + OutputStream fos = new FileOutputStream(outFile, false); + signWholeOutputFile(buffer, fos, publicKey, privateKey); + fos.close(); + } catch (Exception e) { + e.printStackTrace(); + } + } +} diff --git a/zipsigntools/src/zipadjust.c b/zipsigntools/src/zipadjust.c new file mode 100644 index 000000000..24486d3ad --- /dev/null +++ b/zipsigntools/src/zipadjust.c @@ -0,0 +1,319 @@ +/* + * Copyright (C) 2013 Jorrit "Chainfire" Jongma + * Copyright (C) 2013 The OmniROM Project + */ +/* + * This file is part of OpenDelta. + * + * OpenDelta is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenDelta is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenDelta. If not, see . + */ + +#include +#include +#include +#include +#include +#include +#include + +#pragma pack(1) +struct local_header_struct { + uint32_t signature; + uint16_t extract_version; + uint16_t flags; + uint16_t compression_method; + uint16_t last_modified_time; + uint16_t last_modified_date; + uint32_t crc32; + uint32_t size_compressed; + uint32_t size_uncompressed; + uint16_t length_filename; + uint16_t length_extra; + // filename + // extra +}; +typedef struct local_header_struct local_header_t; + +#pragma pack(1) +struct data_descriptor_struct { + uint32_t signature; + uint32_t crc32; + uint32_t size_compressed; + uint32_t size_uncompressed; +}; +typedef struct data_descriptor_struct data_descriptor_t; + +#pragma pack(1) +struct central_header_struct { + uint32_t signature; + uint16_t version_made; + uint16_t version_needed; + uint16_t flags; + uint16_t compression_method; + uint16_t last_modified_time; + uint16_t last_modified_date; + uint32_t crc32; + uint32_t size_compressed; + uint32_t size_uncompressed; + uint16_t length_filename; + uint16_t length_extra; + uint16_t length_comment; + uint16_t disk_start; + uint16_t attr_internal; + uint32_t attr_external; + uint32_t offset; + // filename + // extra + // comment +}; +typedef struct central_header_struct central_header_t; + +#pragma pack(1) +struct central_footer_struct { + uint32_t signature; + uint16_t disk_number; + uint16_t disk_number_central_directory; + uint16_t central_directory_entries_this_disk; + uint16_t central_directory_entries_total; + uint32_t central_directory_size; + uint32_t central_directory_offset; + uint16_t length_comment; + // comment +}; +typedef struct central_footer_struct central_footer_t; + +#define MAGIC_LOCAL_HEADER 0x04034b50 +#define MAGIC_DATA_DESCRIPTOR 0x08074b50 +#define MAGIC_CENTRAL_HEADER 0x02014b50 +#define MAGIC_CENTRAL_FOOTER 0x06054b50 + +static int xerror(char* message) { + fprintf(stderr, "%s\n", message); + return 0; +} + +static int xseekread(int fd, off_t offset, void* buf, size_t bytes) { + if (lseek(fd, offset, SEEK_SET) == (off_t)-1) return xerror("Seek failed"); + if (read(fd, buf, bytes) != bytes) return xerror("Read failed"); + return 1; +} + +static int xseekwrite(int fd, off_t offset, void* buf, size_t bytes) { + if (lseek(fd, offset, SEEK_SET) == (off_t)-1) return xerror("Seek failed"); + if (write(fd, buf, bytes) != bytes) return xerror("Write failed"); + return 1; +} + +static int xfilecopy(int fdIn, int fdOut, off_t offsetIn, off_t offsetOut, size_t bytes) { + if ((offsetIn != (off_t)-1) && (lseek(fdIn, offsetIn, SEEK_SET) == (off_t)-1)) return xerror("Seek failed"); + if ((offsetOut != (off_t)-1) && (lseek(fdOut, offsetOut, SEEK_SET) == (off_t)-1)) return xerror("Seek failed"); + + int CHUNK = 256 * 1024; + void* buf = malloc(CHUNK); + if (buf == NULL) return xerror("malloc failed"); + size_t left = bytes; + while (left > 0) { + size_t wanted = (left < CHUNK) ? left : CHUNK; + size_t r = read(fdIn, buf, wanted); + if (r <= 0) return xerror("Read failed"); + if (write(fdOut, buf, r) != r) return xerror("Write failed"); + left -= r; + } + free(buf); + + return 1; +} + +static int xdecompress(int fdIn, int fdOut, off_t offsetIn, off_t offsetOut, size_t bytes) { + if ((offsetIn != (off_t)-1) && (lseek(fdIn, offsetIn, SEEK_SET) == (off_t)-1)) return xerror("Seek failed"); + if ((offsetOut != (off_t)-1) && (lseek(fdOut, offsetOut, SEEK_SET) == (off_t)-1)) return xerror("Seek failed"); + + int CHUNK = 256 * 1024; + + int ret; + unsigned have; + z_stream strm; + unsigned char in[CHUNK]; + unsigned char out[CHUNK]; + + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit2(&strm, -15); + if (ret != Z_OK) return xerror("ret != Z_OK"); + + do { + strm.avail_in = read(fdIn, in, CHUNK); + if (strm.avail_in < 0) { + (void)inflateEnd(&strm); + return xerror("Read failed"); + } + if (strm.avail_in == 0) break; + strm.next_in = in; + + do { + strm.avail_out = CHUNK; + strm.next_out = out; + + ret = inflate(&strm, Z_NO_FLUSH); + if (ret == Z_STREAM_ERROR) return xerror("Stream error"); + switch (ret) { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void)inflateEnd(&strm); + return xerror("DICT/DATA/MEM error"); + } + + have = CHUNK - strm.avail_out; + if (write(fdOut, out, have) != have) { + (void)inflateEnd(&strm); + return xerror("Write failed"); + } + } while (strm.avail_out == 0); + } while (ret != Z_STREAM_END); + (void)inflateEnd(&strm); + + return ret == Z_STREAM_END ? 1 : 0; +} + +int zipadjust(char* filenameIn, char* filenameOut, int decompress) { + int ok = 0; + + int fin = open(filenameIn, O_RDONLY); + if (fin > 0) { + unsigned int size = lseek(fin, 0, SEEK_END); + lseek(fin, 0, SEEK_SET); + printf("%d bytes\n", size); + + char filename[1024]; + + central_footer_t central_footer; + uint32_t central_directory_in_position = 0; + uint32_t central_directory_in_size = 0; + uint32_t central_directory_out_size = 0; + + int i; + for (i = size - 4; i >= 0; i--) { + uint32_t magic = 0; + if (!xseekread(fin, i, &magic, sizeof(uint32_t))) return 0; + if (magic == MAGIC_CENTRAL_FOOTER) { + printf("central footer @ %08X\n", i); + if (!xseekread(fin, i, ¢ral_footer, sizeof(central_footer_t))) return 0; + + central_header_t central_header; + if (!xseekread(fin, central_footer.central_directory_offset, ¢ral_header, sizeof(central_header_t))) return 0; + if ( central_header.signature == MAGIC_CENTRAL_HEADER ) { + central_directory_in_position = central_footer.central_directory_offset; + central_directory_in_size = size - central_footer.central_directory_offset; + printf("central header @ %08X (%d)\n", central_footer.central_directory_offset, central_footer.central_directory_size); + break; + } + } + } + + if (central_directory_in_position == 0) return 0; + + unsigned char* central_directory_in = (unsigned char*)malloc(central_directory_in_size); + unsigned char* central_directory_out = (unsigned char*)malloc(central_directory_in_size); + if (!xseekread(fin, central_directory_in_position, central_directory_in, central_directory_in_size)) return 0; + memset(central_directory_out, 0, central_directory_in_size); + + unlink(filenameOut); + int fout = open(filenameOut, O_CREAT | O_WRONLY, 0644); + if (fout > 0) { + + uintptr_t central_directory_in_index = 0; + uintptr_t central_directory_out_index = 0; + + central_header_t* central_header = NULL; + local_header_t local_header; + + uint32_t out_index = 0; + + while (1) { + central_header = (central_header_t*)¢ral_directory_in[central_directory_in_index]; + if (central_header->signature != MAGIC_CENTRAL_HEADER) break; + + filename[central_header->length_filename] = (char)0; + memcpy(filename, ¢ral_directory_in[central_directory_in_index + sizeof(central_header_t)], central_header->length_filename); + printf("%s (%d --> %d) [%08X] (%d)\n", filename, central_header->size_uncompressed, central_header->size_compressed, central_header->crc32, central_header->length_extra + central_header->length_comment); + + local_header_t local_header; + if (!xseekread(fin, central_header->offset, &local_header, sizeof(local_header_t))) return 0; + + // save and update to next index before we clobber the data + uint16_t compression_method_old = central_header->compression_method; + uint32_t size_compressed_old = central_header->size_compressed; + uint32_t offset_old = central_header->offset; + uint32_t length_extra_old = central_header->length_extra; + central_directory_in_index += sizeof(central_header_t) + central_header->length_filename + central_header->length_extra + central_header->length_comment; + + // copying, rewriting, and correcting local and central headers so all the information matches, and no data descriptors are necessary + central_header->offset = out_index; + central_header->flags = central_header->flags & !8; + if (decompress && (compression_method_old == 8)) { + central_header->compression_method = 0; + central_header->size_compressed = central_header->size_uncompressed; + } + central_header->length_extra = 0; + central_header->length_comment = 0; + local_header.compression_method = central_header->compression_method; + local_header.flags = central_header->flags; + local_header.crc32 = central_header->crc32; + local_header.size_uncompressed = central_header->size_uncompressed; + local_header.size_compressed = central_header->size_compressed; + local_header.length_extra = 0; + + if (!xseekwrite(fout, out_index, &local_header, sizeof(local_header_t))) return 0; + out_index += sizeof(local_header_t); + if (!xseekwrite(fout, out_index, &filename[0], central_header->length_filename)) return 0; + out_index += central_header->length_filename; + + if (decompress && (compression_method_old == 8)) { + if (!xdecompress(fin, fout, offset_old + sizeof(local_header_t) + central_header->length_filename + length_extra_old, out_index, size_compressed_old)) return 0; + } else { + if (!xfilecopy(fin, fout, offset_old + sizeof(local_header_t) + central_header->length_filename + length_extra_old, out_index, size_compressed_old)) return 0; + } + out_index += local_header.size_compressed; + + memcpy(¢ral_directory_out[central_directory_out_index], central_header, sizeof(central_header_t) + central_header->length_filename); + central_directory_out_index += sizeof(central_header_t) + central_header->length_filename; + } + + central_directory_out_size = central_directory_out_index; + central_footer.central_directory_size = central_directory_out_size; + central_footer.central_directory_offset = out_index; + central_footer.length_comment = 0; + if (!xseekwrite(fout, out_index, central_directory_out, central_directory_out_size)) return 0; + out_index += central_directory_out_size; + if (!xseekwrite(fout, out_index, ¢ral_footer, sizeof(central_footer_t))) return 0; + + printf("central header @ %08X (%d)\n", central_footer.central_directory_offset, central_footer.central_directory_size); + printf("central footer @ %08X\n", out_index); + + close(fout); + ok = 1; + } + + free(central_directory_in); + free(central_directory_out); + close(fin); + } + + return ok; +} diff --git a/zipsigntools/src/zipadjust.h b/zipsigntools/src/zipadjust.h new file mode 100644 index 000000000..8ac221633 --- /dev/null +++ b/zipsigntools/src/zipadjust.h @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2013 Jorrit "Chainfire" Jongma + * Copyright (C) 2013 The OmniROM Project + */ +/* + * This file is part of OpenDelta. + * + * OpenDelta is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenDelta is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenDelta. If not, see . + */ + +#ifndef __ZIPADJUST_H +#define __ZIPADJUST_H + +int zipadjust(char* filenameIn, char* filenameOut, int decompress); + +#endif diff --git a/zipsigntools/src/zipadjust_run.c b/zipsigntools/src/zipadjust_run.c new file mode 100644 index 000000000..df7743333 --- /dev/null +++ b/zipsigntools/src/zipadjust_run.c @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2013 Jorrit "Chainfire" Jongma + * Copyright (C) 2013 The OmniROM Project + */ +/* + * This file is part of OpenDelta. + * + * OpenDelta is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * OpenDelta is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OpenDelta. If not, see . + */ + +#include +#include +#include "zipadjust.h" + +int main(int argc, char *argv[]) { + if (argc >= 3) { + if ((argc >= 4) && (strcmp(argv[1], "--decompress") == 0)) { + zipadjust(argv[2], argv[3], 1); + return 0; + } else { + zipadjust(argv[1], argv[2], 0); + return 0; + } + } + + printf("zipadjust - Copyright (c) 2013 Jorrit Jongma (Chainfire)\n"); + printf("\n"); + printf("Usage: zipadjust [--decompress] input.zip output.zip\n"); + printf("\n"); + printf("Rewrites a zipfile removing all extra fields and comments (this includes the signapk whole-file signature), and synchronizing local headers with the central directory so no data descriptors are needed anymore. Optionally, the output zip is converted to only use STORE.\n"); + printf("\n"); + printf("Written to work specifically with Android OTA zip files, and does not cope with all possible zip file features and formats.\n"); + return 0; +} diff --git a/zipsigntools/test.certificate.x509.pem b/zipsigntools/test.certificate.x509.pem new file mode 100644 index 000000000..e242d83e2 --- /dev/null +++ b/zipsigntools/test.certificate.x509.pem @@ -0,0 +1,27 @@ +-----BEGIN CERTIFICATE----- +MIIEqDCCA5CgAwIBAgIJAJNurL4H8gHfMA0GCSqGSIb3DQEBBQUAMIGUMQswCQYD +VQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4g +VmlldzEQMA4GA1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UE +AxMHQW5kcm9pZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTAe +Fw0wODAyMjkwMTMzNDZaFw0zNTA3MTcwMTMzNDZaMIGUMQswCQYDVQQGEwJVUzET +MBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4G +A1UEChMHQW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9p +ZDEiMCAGCSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbTCCASAwDQYJKoZI +hvcNAQEBBQADggENADCCAQgCggEBANaTGQTexgskse3HYuDZ2CU+Ps1s6x3i/waM +qOi8qM1r03hupwqnbOYOuw+ZNVn/2T53qUPn6D1LZLjk/qLT5lbx4meoG7+yMLV4 +wgRDvkxyGLhG9SEVhvA4oU6Jwr44f46+z4/Kw9oe4zDJ6pPQp8PcSvNQIg1QCAcy +4ICXF+5qBTNZ5qaU7Cyz8oSgpGbIepTYOzEJOmc3Li9kEsBubULxWBjf/gOBzAzU +RNps3cO4JFgZSAGzJWQTT7/emMkod0jb9WdqVA2BVMi7yge54kdVMxHEa5r3b97s +zI5p58ii0I54JiCUP5lyfTwE/nKZHZnfm644oLIXf6MdW2r+6R8CAQOjgfwwgfkw +HQYDVR0OBBYEFEhZAFY9JyxGrhGGBaR0GawJyowRMIHJBgNVHSMEgcEwgb6AFEhZ +AFY9JyxGrhGGBaR0GawJyowRoYGapIGXMIGUMQswCQYDVQQGEwJVUzETMBEGA1UE +CBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEQMA4GA1UEChMH +QW5kcm9pZDEQMA4GA1UECxMHQW5kcm9pZDEQMA4GA1UEAxMHQW5kcm9pZDEiMCAG +CSqGSIb3DQEJARYTYW5kcm9pZEBhbmRyb2lkLmNvbYIJAJNurL4H8gHfMAwGA1Ud +EwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAHqvlozrUMRBBVEY0NqrrwFbinZa +J6cVosK0TyIUFf/azgMJWr+kLfcHCHJsIGnlw27drgQAvilFLAhLwn62oX6snb4Y +LCBOsVMR9FXYJLZW2+TcIkCRLXWG/oiVHQGo/rWuWkJgU134NDEFJCJGjDbiLCpe ++ZTWHdcwauTJ9pUbo8EvHRkU3cYfGmLaLfgn9gP+pWA7LFQNvXwBnDa6sppCccEX +31I828XzgXpJ4O+mDL1/dBd+ek8ZPUP0IgdyZm5MTYPhvVqGCHzzTy3sIeJFymwr +sBbmg2OAUNLEMO6nwmocSdN2ClirfxqCzJOLSDE4QyS9BAH6EhY6UFcOaE0= +-----END CERTIFICATE----- diff --git a/zipsigntools/test.key.pk8 b/zipsigntools/test.key.pk8 new file mode 100644 index 000000000..586c1bd5c Binary files /dev/null and b/zipsigntools/test.key.pk8 differ