Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add listing and filtering support #10

Merged
merged 4 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .clang-format
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ PointerAlignment: Left
ExperimentalAutoDetectBinPacking: true
SpaceBeforeCpp11BracedList: true
IndentPPDirectives: BeforeHash
IncludeBlocks: Regroup
---
Language: ObjC
# Force pointers to the type for C++.
Expand Down
11 changes: 7 additions & 4 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ jobs:
run: make
- name: Test binary
run: |
./aastuff
./aastuff_standalone
./aastuff -h
./aastuff_standalone -h
- name: Upload artifacts
uses: actions/upload-artifact@v4
with:
Expand Down Expand Up @@ -60,6 +60,7 @@ jobs:
unzip -p tests/iPhone_15PM_18.0_22A5282m.ipsw 090-29713-049.dmg.aea > tests/iOS_18_beta_1_IPSW/encrypted.aea
rm tests/iPhone_15PM_18.0_22A5282m.ipsw
printf "$IOS_IPSW_TEST_KEY" > tests/iOS_18_beta_1_IPSW/expected.txt
touch tests/iOS_18_beta_1_IPSW/fast_unsupported
env:
MACOS_OTA_TEST_KEY: ${{ vars.MACOS_OTA_TEST_KEY }}
IOS_IPSW_TEST_KEY: ${{ vars.IOS_IPSW_TEST_KEY }}
Expand All @@ -86,11 +87,13 @@ jobs:
# This file uses a compressed inner layer
# curl -L "https://updates.cdn-apple.com/2024/Iris/mobileassets/003-49672/A1233F60-3D17-491B-803A-DB26E20695AE/com_apple_MobileAsset_UAF_Siri_Understanding/6FF3BAF0-FBEF-4C01-BB0E-30CD61DAFCC4.aar" -o tests/small/encrypted.aea
aria2c -x 16 -s 16 -j 16 --file-allocation=none "https://updates.cdn-apple.com/2024/Iris/mobileassets/003-49672/A1233F60-3D17-491B-803A-DB26E20695AE/com_apple_MobileAsset_UAF_Siri_Understanding/6FF3BAF0-FBEF-4C01-BB0E-30CD61DAFCC4.aar" -o tests/small/encrypted.aea
printf "$SMALL_TEST_KEY" > tests/small/key.txt
printf "$SMALL_TEST_KEY" > tests/small/expected.txt
printf "YEC,UID,GID,MOD,SH2" > tests/small/flags.txt
# This file uses a raw inner layer
# curl -L "https://updates.cdn-apple.com/2024SummerSeed/mobileassets/052-49061/CA7135A8-BAF6-4890-887C-35FB30C154D5/com_apple_MobileAsset_MacSoftwareUpdate/e2de87f20576b2bdc021d36f74a2f836cf42afe576178388dfd0cde875f4f979.aea" -o tests/large/encrypted.aea
aria2c -x 16 -s 16 -j 16 --file-allocation=none "https://updates.cdn-apple.com/2024SummerSeed/mobileassets/052-49061/CA7135A8-BAF6-4890-887C-35FB30C154D5/com_apple_MobileAsset_MacSoftwareUpdate/e2de87f20576b2bdc021d36f74a2f836cf42afe576178388dfd0cde875f4f979.aea" -o tests/large/encrypted.aea
printf "$LARGE_TEST_KEY" > tests/large/key.txt
printf "$LARGE_TEST_KEY" > tests/large/expected.txt
printf "LNK,FLG,UID,GID,MOD,MTM,CTM" > tests/large/flags.txt
env:
SMALL_TEST_KEY: ${{ vars.SMALL_TEST_KEY }}
LARGE_TEST_KEY: ${{ vars.LARGE_TEST_KEY }}
Expand Down
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
.PHONY: all clean test deploy

SRC_FILES = src/aastuff.m src/extract.m src/extract_standalone.m
SRC_FILES = src/aastuff.m src/args.m src/extract.m src/extract_standalone.m
HDR_FILES = include/AppleArchivePrivate.h include/extract.h include/extract_standalone.h

CFLAGS = -fmodules -fobjc-arc -Iinclude -Wall -Werror
CFLAGS = -fmodules -fobjc-arc -Iinclude -Wall -Werror -Wunreachable-code
LDLIBS = -framework Foundation -lAppleArchive
LDFLAGS = -Llib

Expand Down
22 changes: 17 additions & 5 deletions include/AppleArchivePrivate.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,23 @@ __BEGIN_DECLS
// TODO: Figure out how this is different from normal AppleArchive
typedef void* AAAssetExtractor;

AAAssetExtractor AAAssetExtractorCreate(const char* destDir, void** something, int something2);
// AAAssetExtractorSetParameterCallback
// AAAssetExtractorSetParameterPtr
AAAssetExtractor AAAssetExtractorCreate(const char* work_dir, uint64_t* maybe_offset, void* unknown);

// 102: AEA context
// 103: progress
// 104: entry message
// - 90: start?
// - 91: extract?
// - 92: complete
int AAAssetExtractorSetParameterCallback(AAAssetExtractor extractor, int param, void* callback);

// 101: first arg to callbacks
// 105: input directory
// 106: output directory
int AAAssetExtractorSetParameterPtr(AAAssetExtractor extractor, int param, void* ptr);

int AAAssetExtractorWrite(AAAssetExtractor extractor, void* buffer, size_t size);
void AAAssetExtractorDestroy(AAAssetExtractor extractor);
int AAAssetExtractorDestroy(AAAssetExtractor extractor, uint64_t* maybe_offset);

AAArchiveStream AAVerifyDirectoryArchiveOutputStreamOpen(const char* dir, AAFieldKeySet key_set, void* msg_data,
AAEntryMessageProc msg_proc, AAFlagSet flags, int n_threads);
Expand All @@ -23,7 +35,7 @@ typedef uint32_t AAYopType;
APPLE_ARCHIVE_ENUM(AAYopTypes, uint32_t) {
AA_YOP_TYPE_COPY = 'C', ///< copy
AA_YOP_TYPE_EXTRACT = 'E', ///< extract
AA_YOP_TYPE_SRC_CHECK = 'I', ///< extract
AA_YOP_TYPE_SRC_CHECK = 'I', ///< source check
AA_YOP_TYPE_MANIFEST = 'M', ///< manifest
AA_YOP_TYPE_DST_FIXUP = 'O', ///< destination fixup
AA_YOP_TYPE_PATCH = 'P', ///< patch
Expand Down
25 changes: 25 additions & 0 deletions include/args.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#ifndef ARGS_H
#define ARGS_H

#import <Foundation/Foundation.h>

@interface ExtractionConfiguration : NSObject <NSCopying>

@property(nonatomic, assign) bool encrypted;
@property(nonatomic, assign) bool list;
@property(nonatomic, strong) NSString* archivePath;
@property(nonatomic, strong) NSString* outputDirectory;
@property(nonatomic, strong) NSData* key;
@property(nonatomic, strong) NSString* filter;
@property(nonatomic, strong) NSRegularExpression* regex;

@property(nonatomic, strong) NSString* function;

- (instancetype)copyWithFunction:(NSString*)function;

@end

ExtractionConfiguration* parseArgs(int argc, char** argv, int* returnCode);
int validateArgs(ExtractionConfiguration* config);

#endif /* ARGS_H */
8 changes: 5 additions & 3 deletions include/extract.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#ifndef EXTRACT_H
#define EXTRACT_H

#include <AppleArchive/AppleArchive.h>
#include <Foundation/Foundation.h>
#import <AppleArchive/AppleArchive.h>
#import <Foundation/Foundation.h>

int extractAsset(AAByteStream stream, NSString* outputDirectory);
#import "args.h"

int extractAsset(AAByteStream stream, ExtractionConfiguration* config);

#endif /* EXTRACT_H */
8 changes: 5 additions & 3 deletions include/extract_standalone.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#ifndef EXTRACT_STANDALONE_H
#define EXTRACT_STANDALONE_H

#include <AppleArchive/AppleArchive.h>
#include <Foundation/Foundation.h>
#import <AppleArchive/AppleArchive.h>
#import <Foundation/Foundation.h>

int extractAssetStandalone(AAByteStream stream, NSString* outputDirectory);
#import "args.h"

int extractAssetStandalone(AAByteStream stream, ExtractionConfiguration* config);

#endif /* EXTRACT_STANDALONE_H */
8 changes: 8 additions & 0 deletions include/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,12 @@
#define DBGLOG(x, ...)
#endif

#if AASTUFF_STANDALONE
#define NAME @"aastuff_standalone"
#else
#define NAME @"aastuff"
#endif

#define VERSION @"0.0.1"

#endif /* UTILS_H */
94 changes: 12 additions & 82 deletions src/aastuff.m
Original file line number Diff line number Diff line change
@@ -1,110 +1,40 @@
#import <AppleArchive/AppleArchive.h>
#import <Foundation/Foundation.h>

#import "args.h"
#import "extract.h"
#import "extract_standalone.h"
#import "utils.h"

#define APPLE_ARCHIVE_MAGIC @"AA01"
#define APPLE_ENCRYPTED_ARCHIVE_MAGIC @"AEA1"

int main(int argc, char** argv) {
@autoreleasepool {
NSError* error = nil;

if (argc < 3) {
ERRLOG(@"Usage: %s <archive> <output directory> [key in base64]", argv[0]);
ERRLOG(@"Key is required for encrypted archives");
return argc == 1 ? 0 : 1;
}

NSString* archivePath = [NSString stringWithUTF8String:argv[1]];
NSString* outputDirectory = [NSString stringWithUTF8String:argv[2]];
NSString* keyBase64 = nil;
if (argc > 3) {
keyBase64 = [NSString stringWithUTF8String:argv[3]];
int ret = 0;
ExtractionConfiguration* config = parseArgs(argc, argv, &ret);
if (!config) {
return ret;
}

if (!archivePath || !outputDirectory) {
ERRLOG(@"Failed to parse arguments");
return 1;
}

NSFileManager* fileManager = [NSFileManager defaultManager];

if (![fileManager fileExistsAtPath:archivePath]) {
ERRLOG(@"Archive does not exist");
if (validateArgs(config)) {
return 1;
}

BOOL isDirectory = false;
if (![fileManager fileExistsAtPath:outputDirectory isDirectory:&isDirectory]) {
if (![fileManager createDirectoryAtPath:outputDirectory withIntermediateDirectories:NO attributes:nil error:&error]) {
ERRLOG(@"Failed to create directory: %@", error);
return 1;
}
} else {
if (!isDirectory) {
ERRLOG(@"Output path is not a directory");
return 1;
}
}

NSFileHandle* handle = [NSFileHandle fileHandleForReadingAtPath:archivePath];
if (!handle) {
ERRLOG(@"Failed to open archive file");
return 1;
}

NSData* magic = [handle readDataUpToLength:4 error:&error];
// If this fails, can't do anything about it, so just ignore the error
[handle closeAndReturnError:nil];

if (!magic || magic.length != 4) {
ERRLOG(@"Failed to read magic: %@", error);
return 1;
}

bool encrypted = false;
NSString* magicStr = [[NSString alloc] initWithData:magic encoding:NSUTF8StringEncoding];
if ([magicStr isEqualToString:APPLE_ENCRYPTED_ARCHIVE_MAGIC]) {
encrypted = true;
} else if ([magicStr isEqualToString:APPLE_ARCHIVE_MAGIC]) {
encrypted = false;
} else {
ERRLOG(@"Unknown magic: %@", magicStr);
return 1;
}

if (encrypted && !keyBase64) {
ERRLOG(@"Encrypted archive requires key");
return 1;
}

AAByteStream stream = AAFileStreamOpenWithPath(archivePath.UTF8String, O_RDONLY, 0644);
AAByteStream stream = AAFileStreamOpenWithPath(config.archivePath.UTF8String, O_RDONLY, 0644);
if (!stream) {
ERRLOG(@"Failed to open archive file stream");
return 1;
}

AAByteStream decryptionStream = NULL;
AEAContext decryptionContext = NULL;
if (encrypted) {
NSData* key = [[NSData alloc] initWithBase64EncodedString:keyBase64 options:0];
if (!key) {
ERRLOG(@"Failed to parse key");
AAByteStreamClose(stream);
return 1;
}

if (config.encrypted) {
decryptionContext = AEAContextCreateWithEncryptedStream(stream);
if (!decryptionContext) {
ERRLOG(@"Failed to create encrypted stream context");
AAByteStreamClose(stream);
return 1;
}

int ret = AEAContextSetFieldBlob(decryptionContext, AEA_CONTEXT_FIELD_SYMMETRIC_KEY, 0, key.bytes, key.length);
int ret = AEAContextSetFieldBlob(decryptionContext, AEA_CONTEXT_FIELD_SYMMETRIC_KEY, 0, config.key.bytes, config.key.length);
if (ret != 0) {
ERRLOG(@"Failed to set key");
AEAContextDestroy(decryptionContext);
Expand All @@ -114,17 +44,17 @@ int main(int argc, char** argv) {

decryptionStream = AEADecryptionInputStreamOpen(stream, decryptionContext, 0, 0);
if (!decryptionStream) {
ERRLOG(@"Failed to open decryption stream");
ERRLOG(@"Failed to open decryption stream (invalid key?)");
AEAContextDestroy(decryptionContext);
AAByteStreamClose(stream);
return 1;
}
}

#if AASTUFF_STANDALONE
if (extractAssetStandalone(encrypted ? decryptionStream : stream, outputDirectory)) {
if (extractAssetStandalone(config.encrypted ? decryptionStream : stream, config)) {
#else
if (extractAsset(encrypted ? decryptionStream : stream, outputDirectory)) {
if (extractAsset(config.encrypted ? decryptionStream : stream, config)) {
#endif
ERRLOG(@"Extracting asset failed");
AEAContextDestroy(decryptionContext);
Expand Down
Loading
Loading