Rewrite zip signing

This commit is contained in:
topjohnwu 2017-06-16 03:07:46 +08:00
parent 42d14d5ca2
commit fd4aaab137
6 changed files with 605 additions and 1091 deletions

View File

@ -14,7 +14,6 @@ import com.topjohnwu.magisk.utils.ZipUtils;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.OutputStream; import java.io.OutputStream;
public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> { public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> {
@ -38,51 +37,33 @@ public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> {
@Override @Override
protected Boolean doInBackground(Void... params) { protected Boolean doInBackground(Void... params) {
FileInputStream in;
FileOutputStream out;
try { try {
// Create temp file // Create temp file
File temp1 = new File(magiskManager.getCacheDir(), "1.zip"); File temp1 = new File(magiskManager.getCacheDir(), "1.zip");
File temp2 = new File(magiskManager.getCacheDir(), "2.zip"); File temp2 = new File(magiskManager.getCacheDir(), "2.zip");
if (magiskManager.getCacheDir().mkdirs()) { magiskManager.getCacheDir().mkdirs();
temp1.createNewFile(); temp1.createNewFile();
temp2.createNewFile(); temp2.createNewFile();
}
out = new FileOutputStream(temp1);
// First remove top folder in Github source zip, Uri -> temp1 // First remove top folder in Github source zip, Uri -> temp1
ZipUtils.removeTopFolder(activity.getContentResolver().openInputStream(mUri), out); ZipUtils.removeTopFolder(activity.getContentResolver().openInputStream(mUri), temp1);
out.flush();
out.close();
out = new FileOutputStream(temp2);
// Then sign the zip for the first time, temp1 -> temp2 // Then sign the zip for the first time, temp1 -> temp2
ZipUtils.signZip(activity, temp1, out, false); ZipUtils.signZip(activity, temp1, temp2, false);
out.flush();
out.close();
// Adjust the zip to prevent unzip issues, temp2 -> temp2 // Adjust the zip to prevent unzip issues, temp2 -> temp1
ZipUtils.adjustZip(temp2); ZipUtils.zipAdjust(temp2.getPath(), temp1.getPath());
out = new FileOutputStream(temp1); // Finally, sign the whole zip file again, temp1 -> temp2
ZipUtils.signZip(activity, temp1, temp2, true);
// Finally, sign the whole zip file again, temp2 -> temp1 // Write it back to the downloaded zip, temp2 -> Uri
ZipUtils.signZip(activity, temp2, out, true); FileInputStream in = new FileInputStream(temp2);
out.flush();
out.close();
in = new FileInputStream(temp1);
// Write it back to the downloaded zip, temp1 -> Uri
try (OutputStream target = activity.getContentResolver().openOutputStream(mUri)) { try (OutputStream target = activity.getContentResolver().openOutputStream(mUri)) {
byte[] buffer = new byte[4096]; byte[] buffer = new byte[4096];
int length; int length;
if (target == null) throw new FileNotFoundException(); if (target == null) throw new FileNotFoundException();
while ((length = in.read(buffer)) > 0) while ((length = in.read(buffer)) > 0)
target.write(buffer, 0, length); target.write(buffer, 0, length);
} }

View File

@ -7,7 +7,6 @@ import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.support.v4.content.FileProvider; import android.support.v4.content.FileProvider;
import com.topjohnwu.magisk.MagiskManager;
import com.topjohnwu.magisk.utils.Utils; import com.topjohnwu.magisk.utils.Utils;
import java.io.File; import java.io.File;

File diff suppressed because it is too large Load Diff

View File

@ -3,58 +3,17 @@
// //
#include <jni.h> #include <jni.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include "zipadjust.h" #include "zipadjust.h"
JNIEXPORT jbyteArray JNICALL
Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust___3BI(JNIEnv *env, jclass type,
jbyteArray jbytes, jint size) {
fin = (*env)->GetPrimitiveArrayCritical(env, jbytes, NULL);
insize = (size_t) size;
zipadjust(0);
(*env)->ReleasePrimitiveArrayCritical(env, jbytes, fin, 0);
jbyteArray ret = (*env)->NewByteArray(env, outsize);
(*env)->SetByteArrayRegion(env, ret, 0, outsize, (const jbyte*) fout);
free(fout);
return ret;
}
JNIEXPORT void JNICALL JNIEXPORT void JNICALL
Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust__Ljava_lang_String_2(JNIEnv *env, jclass type, jstring name) { Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust(JNIEnv *env, jclass type, jstring filenameIn_,
const char *filename = (*env)->GetStringUTFChars(env, name, NULL); jstring filenameOut_) {
int fd = open(filename, O_RDONLY); const char *filenameIn = (*env)->GetStringUTFChars(env, filenameIn_, 0);
if (fd < 0) const char *filenameOut = (*env)->GetStringUTFChars(env, filenameOut_, 0);
return;
// Load the file to memory // TODO
insize = lseek(fd, 0, SEEK_END); zipadjust(filenameIn, filenameOut, 0);
lseek(fd, 0, SEEK_SET);
fin = malloc(insize);
read(fd, fin, insize);
zipadjust(0);
close(fd);
// Open file for output
fd = open(filename, O_WRONLY | O_TRUNC);
if (fd < 0)
return;
(*env)->ReleaseStringUTFChars(env, name, filename);
// Write back to file
lseek(fd, 0, SEEK_SET);
write(fd, fout, outsize);
close(fd);
free(fin);
free(fout);
(*env)->ReleaseStringUTFChars(env, filenameIn_, filenameIn);
(*env)->ReleaseStringUTFChars(env, filenameOut_, filenameOut);
} }

View File

@ -1,9 +1,14 @@
#include <stdlib.h> #include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <zlib.h> #include <zlib.h>
#include <unistd.h>
#include "zipadjust.h" #include "zipadjust.h"
size_t insize = 0, outsize = 0, alloc = 0; #ifndef O_BINARY
unsigned char *fin = NULL, *fout = NULL; #define O_BINARY 0
#define O_TEXT 0
#endif
#pragma pack(1) #pragma pack(1)
struct local_header_struct { struct local_header_struct {
@ -81,41 +86,43 @@ static int xerror(char* message) {
return 0; return 0;
} }
static int xseekread(off_t offset, void* buf, size_t bytes) { static int xseekread(int fd, off_t offset, void* buf, size_t bytes) {
memcpy(buf, fin + offset, 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; return 1;
} }
static int xseekwrite(off_t offset, const void* buf, size_t bytes) { static int xseekwrite(int fd, off_t offset, void* buf, size_t bytes) {
if (offset + bytes > outsize) outsize = offset + bytes; if (lseek(fd, offset, SEEK_SET) == (off_t)-1) return xerror("Seek failed");
if (outsize > alloc) { if (write(fd, buf, bytes) != bytes) return xerror("Write failed");
fout = realloc(fout, outsize);
alloc = outsize;
}
memcpy(fout + offset, buf, bytes);
return 1; return 1;
} }
static int xfilecopy(off_t offsetIn, off_t offsetOut, size_t bytes) { static int xfilecopy(int fdIn, int fdOut, off_t offsetIn, off_t offsetOut, size_t bytes) {
unsigned int CHUNK = 256 * 1024; if ((offsetIn != (off_t)-1) && (lseek(fdIn, offsetIn, SEEK_SET) == (off_t)-1)) return xerror("Seek failed");
unsigned char* buf = malloc(CHUNK); 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"); if (buf == NULL) return xerror("malloc failed");
size_t left = bytes; size_t left = bytes;
while (left > 0) { while (left > 0) {
size_t wanted = (left < CHUNK) ? left : CHUNK; size_t wanted = (left < CHUNK) ? left : CHUNK;
xseekread(offsetIn, buf, wanted); size_t r = read(fdIn, buf, wanted);
xseekwrite(offsetOut, buf, wanted); if (r <= 0) return xerror("Read failed");
offsetIn += wanted; if (write(fdOut, buf, r) != r) return xerror("Write failed");
offsetOut += wanted; left -= r;
left -= wanted;
} }
free(buf); free(buf);
return 1; return 1;
} }
static int xdecompress(off_t offsetIn, off_t offsetOut, size_t bytes) { static int xdecompress(int fdIn, int fdOut, off_t offsetIn, off_t offsetOut, size_t bytes) {
unsigned int CHUNK = 256 * 1024; 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; int ret;
unsigned have; unsigned have;
@ -132,12 +139,9 @@ static int xdecompress(off_t offsetIn, off_t offsetOut, size_t bytes) {
if (ret != Z_OK) return xerror("ret != Z_OK"); if (ret != Z_OK) return xerror("ret != Z_OK");
do { do {
strm.avail_in = insize - offsetIn; strm.avail_in = read(fdIn, in, CHUNK);
if (strm.avail_in == 0) break; if (strm.avail_in == 0) break;
strm.avail_in = (strm.avail_in > CHUNK) ? CHUNK : strm.avail_in;
xseekread(offsetIn, in, strm.avail_in);
strm.next_in = in; strm.next_in = in;
offsetIn += strm.avail_in;
do { do {
strm.avail_out = CHUNK; strm.avail_out = CHUNK;
@ -155,8 +159,10 @@ static int xdecompress(off_t offsetIn, off_t offsetOut, size_t bytes) {
} }
have = CHUNK - strm.avail_out; have = CHUNK - strm.avail_out;
xseekwrite(offsetOut, out, have); if (write(fdOut, out, have) != have) {
offsetOut += have; (void)inflateEnd(&strm);
return xerror("Write failed");
}
} while (strm.avail_out == 0); } while (strm.avail_out == 0);
} while (ret != Z_STREAM_END); } while (ret != Z_STREAM_END);
(void)inflateEnd(&strm); (void)inflateEnd(&strm);
@ -164,118 +170,128 @@ static int xdecompress(off_t offsetIn, off_t offsetOut, size_t bytes) {
return ret == Z_STREAM_END ? 1 : 0; return ret == Z_STREAM_END ? 1 : 0;
} }
int zipadjust(int decompress) { int zipadjust(const char* filenameIn, const char* filenameOut, int decompress) {
int ok = 0; int ok = 0;
char filename[1024]; int fin = open(filenameIn, O_RDONLY | O_BINARY);
if (fin > 0) {
unsigned int size = lseek(fin, 0, SEEK_END);
lseek(fin, 0, SEEK_SET);
LOGD("%d bytes\n", size);
central_footer_t central_footer; char filename[1024];
uint32_t central_directory_in_position = 0;
uint32_t central_directory_in_size = 0;
uint32_t central_directory_out_size = 0;
int i; central_footer_t central_footer;
for (i = insize - 4; i >= 0; i--) { uint32_t central_directory_in_position = 0;
uint32_t magic = 0; uint32_t central_directory_in_size = 0;
if (!xseekread(i, &magic, sizeof(uint32_t))) return 0; uint32_t central_directory_out_size = 0;
if (magic == MAGIC_CENTRAL_FOOTER) {
LOGD("central footer @ %08X\n", i);
if (!xseekread(i, &central_footer, sizeof(central_footer_t))) return 0;
central_header_t central_header; int i;
if (!xseekread(central_footer.central_directory_offset, &central_header, sizeof(central_header_t))) return 0; for (i = size - 4; i >= 0; i--) {
if ( central_header.signature == MAGIC_CENTRAL_HEADER ) { uint32_t magic = 0;
central_directory_in_position = central_footer.central_directory_offset; if (!xseekread(fin, i, &magic, sizeof(uint32_t))) return 0;
central_directory_in_size = insize - central_footer.central_directory_offset; if (magic == MAGIC_CENTRAL_FOOTER) {
LOGD("central header @ %08X (%d)\n", central_footer.central_directory_offset, central_footer.central_directory_size); LOGD("central footer @ %08X\n", i);
break; if (!xseekread(fin, i, &central_footer, sizeof(central_footer_t))) return 0;
central_header_t central_header;
if (!xseekread(fin, central_footer.central_directory_offset, &central_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;
LOGD("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 | O_BINARY, 0644);
if (fout > 0) {
uintptr_t central_directory_in_index = 0;
uintptr_t central_directory_out_index = 0;
central_header_t* central_header = NULL;
uint32_t out_index = 0;
while (1) {
central_header = (central_header_t*)&central_directory_in[central_directory_in_index];
if (central_header->signature != MAGIC_CENTRAL_HEADER) break;
filename[central_header->length_filename] = (char)0;
memcpy(filename, &central_directory_in[central_directory_in_index + sizeof(central_header_t)], central_header->length_filename);
LOGD("%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(&central_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, &central_footer, sizeof(central_footer_t))) return 0;
LOGD("central header @ %08X (%d)\n", central_footer.central_directory_offset, central_footer.central_directory_size);
LOGD("central footer @ %08X\n", out_index);
close(fout);
ok = 1;
}
free(central_directory_in);
free(central_directory_out);
close(fin);
} }
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(central_directory_in_position, central_directory_in, central_directory_in_size)) return 0;
memset(central_directory_out, 0, central_directory_in_size);
fout = (unsigned char*) malloc(insize);
alloc = insize;
uintptr_t central_directory_in_index = 0;
uintptr_t central_directory_out_index = 0;
central_header_t* central_header = NULL;
uint32_t out_index = 0;
while (1) {
central_header = (central_header_t*)&central_directory_in[central_directory_in_index];
if (central_header->signature != MAGIC_CENTRAL_HEADER) break;
filename[central_header->length_filename] = (char)0;
memcpy(filename, &central_directory_in[central_directory_in_index + sizeof(central_header_t)], central_header->length_filename);
LOGD("%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(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(out_index, &local_header, sizeof(local_header_t))) return 0;
out_index += sizeof(local_header_t);
if (!xseekwrite(out_index, &filename[0], central_header->length_filename)) return 0;
out_index += central_header->length_filename;
if (decompress && (compression_method_old == 8)) {
if (!xdecompress(offset_old + sizeof(local_header_t) + central_header->length_filename + length_extra_old, out_index, size_compressed_old)) return 0;
} else {
if (!xfilecopy(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(&central_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(out_index, central_directory_out, central_directory_out_size)) return 0;
out_index += central_directory_out_size;
if (!xseekwrite(out_index, &central_footer, sizeof(central_footer_t))) return 0;
LOGD("central header @ %08X (%d)\n", central_footer.central_directory_offset, central_footer.central_directory_size);
LOGD("central footer @ %08X\n", out_index);
ok = 1;
free(central_directory_in);
free(central_directory_out);
return ok; return ok;
} }

View File

@ -3,10 +3,7 @@
#include <android/log.h> #include <android/log.h>
int zipadjust(int decompress); int zipadjust(const char* filenameIn, const char* filenameOut, int decompress);
extern size_t insize, outsize, alloc;
extern unsigned char *fin, *fout;
#define LOG_TAG "zipadjust" #define LOG_TAG "zipadjust"