Skip to content

Commit

Permalink
Make sideloading finally work!!! Last few fixes needed for a release!
Browse files Browse the repository at this point in the history
  • Loading branch information
Dadoum committed Oct 18, 2023
1 parent 10f3bc8 commit 769f2eb
Show file tree
Hide file tree
Showing 21 changed files with 1,817 additions and 386 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ jobs:
run: echo 'module version_string; enum versionStr = "Official build, branch ${{ github.ref_name }}, commit ${{ github.sha }}";' > source/version_string.d

- name: Build
run: dub build -b release-debug --compiler=ldc2 --arch x86_64-apple-darwin
run: dub build -b release-debug -c cli --compiler=ldc2 --arch x86_64-apple-darwin

- name: Rename
run: mv "${{github.workspace}}/bin/sideloader" "${{github.workspace}}/bin/sideloader-macOS-x86_64" # TODO make an app bundle
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ eventually install it with Sideloader on a real device (or maybe even an emulate
in the future!) and debug it (with `idevicedebug` or remote `lldb`). _(TODO: add an option
to add the entitlement for debugging)_

## Features

- Sideload
- Install SideStore (soon)
- iOS version range is unknown. 32-bit support is untested. Please report any issue here!!

## Notes on platform support

### Linux
Expand Down Expand Up @@ -94,6 +100,9 @@ cues on the authentication systems for both machines and accounts.
- All the people in the SideStore team: testing, help on the machine authentication.
- All the people in the AltStore team: help on the account auth, and 2FA (especially
kabiroberai's code).
- zhlynn: for its code in zsign.
- indygreg: for its code in rcodesign.
- teryx: [their article about code signature](https://medium.com/csit-tech-blog/demystifying-ios-code-signature-309d52c2ff1d).
- Apple Music for Android libraries: giving the opportunity to make all of this work
neatly!
- Apple's AuthKit and AuthKitWin: giving me the skeleton of the authentication requests
Expand All @@ -116,5 +125,5 @@ I took 2 years to find a way to overcome the problem that encountered Cydia Impa
without resorting to reimplementing the full Windows API. I dedicated a lot of work
on this software (alongside my studies).

That is why I am asking you, if you enjoyed my software and if you can afford it, to
That is why I am asking you - if you enjoyed my software and if you can afford it, to
give me a small tip via [GitHub Sponsors](https://github.com/sponsors/Dadoum).
245 changes: 75 additions & 170 deletions cli/frontend.d
Original file line number Diff line number Diff line change
@@ -1,198 +1,103 @@
module frontend;

import std.algorithm;
import std.array;
import std.datetime;
import std.format;
import std.path;
import std.stdio;
import std.sumtype;
import std.typecons;
import file = std.file;

import slf4d;
import slf4d.default_provider;
import slf4d.provider;

import plist;

import imobiledevice;

import server.appleaccount;
import server.developersession;

import sideload;
import sideload.bundle;
import sideload.application;
import sideload.certificateidentity;

import app.frontend;
import main;

version = X509;
shared class CLIFrontend: Frontend {
override string configurationPath() {
getLogger().error("Not implemented.");
return "";
return expandTilde("./sideloader-config");
}

override int run(string[] args) {
import std.algorithm;
import std.array;
import std.datetime;
import std.path;
import std.typecons;
import file = std.file;

import slf4d;

import plist;

import imobiledevice;

import server.developersession;

import sideload.bundle;
import sideload.application;
import sideload.certificateidentity;

import main;

auto log = getLogger();
// auto app = new Application("~/Téléchargements/SideStore.ipa".expandTilde());
auto app = new Application("~/Téléchargements/appux/packages/com.yourcompany.appux_0.0.1-1+debug.ipa".expandTilde());

// create a certificate for the developer
// auto certIdentity = new CertificateIdentity(configurationPath, null);

auto team = DeveloperTeam("iOS devel.", "TEAMID");

// check if we registered an app id for it (if not create it)
string mainAppBundleId = app.bundleIdentifier();
string mainAppIdStr = mainAppBundleId ~ "." ~ team.teamId;
string mainAppName = app.bundleName();

app.appId = mainAppIdStr;
foreach (plugin; app.plugIns) {
string pluginBundleIdentifier = plugin.bundleIdentifier();
assertBundle(
pluginBundleIdentifier.startsWith(mainAppBundleId) &&
pluginBundleIdentifier.length > mainAppBundleId.length,
"Plug-ins are not formed with the main app bundle identifier"
);
plugin.appId = mainAppIdStr ~ pluginBundleIdentifier[mainAppBundleId.length..$];
}
Bundle[] bundlesNeeded = [cast(Bundle) app] ~ app.plugIns;

// Search which App IDs have to be registered (we don't want to start registering App IDs if we don't
// have enough of them to register them all!! otherwise we will waste their precious App IDs)
auto appIdsToRegister = bundlesNeeded;
string appPath;

foreach (bundle; appIdsToRegister) {
log.infoF!"Creating App ID `%s`..."(bundle.appId);
if (args.length != 2) {
log.errorF!"Usage: %s <app path, .ipa or .app>"(args.length ? args[0] : "sideloader");
return 1;
}
appPath = args[1];

auto bundles = bundlesNeeded.map!((bundle) => tuple(bundle, AppId("", bundle.appId, "ApplicationName", null, DateTime()))).array();
auto mainBundle = bundles[0];
if (!(file.exists(configurationPath.buildPath("lib/libstoreservicescore.so")) && file.exists(configurationPath.buildPath("lib/libCoreADI.so")))) {
auto succeeded = downloadAndInstallDeps((progress) {
write(format!"%.2f %% completed\r"(progress * 100));
stdout.flush();

// sign the app with all the retrieved material!
foreach (bundlePair; bundles) {
import core.sys.darwin.mach.loader;
import sideload.macho;
return false;
});

auto bundle = bundlePair[0];
auto appId = bundlePair[1];

auto bundlePath = bundle.bundleDir;

// set the bundle identifier to the one with the team id to match the provisioning profile
bundle.appInfo["CFBundleIdentifier"] = appId.identifier.pl;

string executablePath = bundlePath.buildPath(bundle.appInfo["CFBundleExecutable"].str().native());
MachO[] machOs = MachO.parse(cast(ubyte[]) file.read(executablePath), Architecture.aarch64);
log.infoF!"Mach-Os: %s"(machOs);

import cms.cms_dec;
auto provisioningProfilePlist = Plist.fromMemory(dataFromCMS(
cast(ubyte[]) file.read("/home/dadoum/Téléchargements/com.SideStore.SideStore.MK7ZNLPN7B.AltWidget.mobileprovision")
));

auto entitlements = provisioningProfilePlist["Entitlements"].dict;

foreach (machO; machOs) {
auto execSegBase = machO.execSegBase;
auto execSegLimit = machO.execSegLimit;
auto execFlags = machO.execFlags(entitlements);

auto embeddedSignature = new EmbeddedSignature();
embeddedSignature ~= cast(Blob[]) [
new CodeDirectoryBlob(new SHA1(), team.teamId, execSegBase, execSegLimit, execFlags),
new RequirementsBlob(),
new EntitlementsBlob(entitlements.toXml()),
new DerEntitlementsBlob(entitlements),
new CodeDirectoryBlob(new SHA2(), team.teamId, execSegBase, execSegLimit, execFlags),
new SignatureBlob(),
];
machO.replaceCodeSignature(embeddedSignature);
}

file.write("/home/dadoum/Téléchargements/Salut", makeMachO(machOs));

/*
// fabricate entitlements file!!!
string executablePath = bundlePath.buildPath(bundle.appInfo["CFBundleExecutable"].str().native());
MachO[] machOs = MachO.parse(cast(ubyte[]) file.read(executablePath));
// here is the real signing logic:
// we will sign each of the mach-o contained
// and rebuild them.
foreach (machO; machOs) {
linkedit_data_command signatureCommand = void;
symtab_command symtabCommand = void;
foreach (command; machO.loadCommands) {
switch (command.cmd) {
case LC_CODE_SIGNATURE:
signatureCommand = command.read!linkedit_data_command(0);
break;
case LC_SYMTAB:
symtabCommand = command.read!symtab_command(0);
break;
default:
break;
}
}
string entitlementsStr = "";
if (signatureCommand.cmd) {
// get entitlements!!
SuperBlobHeader superBlob = machO.read!SuperBlobHeader(signatureCommand.dataoff);
auto blobArrayStart = signatureCommand.dataoff + SuperBlobHeader.sizeof;
auto blobArrayEnd = blobArrayStart + superBlob.count * BlobIndex.sizeof;
for (auto blobArrayIndex = blobArrayStart; blobArrayIndex < blobArrayEnd; blobArrayIndex += BlobIndex.sizeof) {
auto currentBlob = machO.read!BlobIndex(signatureCommand.dataoff + blobArrayIndex);
if (currentBlob.type == CSSLOT_ENTITLEMENTS) {
Blob entitlementsBlob = machO.read!Blob(currentBlob.offset);
entitlementsStr = cast(string) machO.data[signatureCommand.dataoff + currentBlob.offset + Blob.sizeof..signatureCommand.dataoff + currentBlob.offset + entitlementsBlob.length];
if (entitlementsStr.length) {
log.infoF!"Entitlements: %s"(entitlementsStr);
}
}
}
}
if (!succeeded) {
log.error("Download failed.");
return 1;
}
log.info("Download completed.");
}

/*
auto entitlements = Plist.fromMemory(cast(ubyte[]) entitlementsStr).dict();
entitlements["application-identifier"] = appId.identifier;
entitlements["com.apple.developer.team-identifier"] = team.teamId;
// create app groups for it if needed
if (auto bundleAppGroups = "com.apple.security.application-groups" in entitlements) {
if (!appId.features[AppIdFeatures.appGroup].boolean().native()) {
// We need to enable app groups then !
log.infoF!"Updating the app id %s to enable app groups."(appId.identifier);
appId.features = developer.updateAppId!iOS(team, appId, dict(AppIdFeatures.appGroup, true)).unwrap();
}
auto appGroups = developer.listApplicationGroups!iOS(team).unwrap();
foreach (bundleAppGroup; bundleAppGroups.array()) {
string bundleGroupId = bundleAppGroup.str().native();
auto matchingAppGroups = appGroups.find!((appGroup) => appGroup.identifier == bundleGroupId).array();
ApplicationGroup appGroup;
if (matchingAppGroups.empty) {
log.infoF!"Creating the app group %s."(bundleGroupId);
appGroup = developer.addApplicationGroup!iOS(team, bundleGroupId, mainAppName).unwrap();
} else {
appGroup = matchingAppGroups[0];
}
}
initializeADI();

write("Enter your Apple ID: ");
stdout.flush();
string appleId = readln()[0..$ - 1];
write("Enter your password (will appear in clear in your terminal): ");
stdout.flush();
string password = readln()[0..$ - 1];

DeveloperSession appleAccount = DeveloperSession.login(
device,
adi,
appleId,
password,
(sendCode, submitCode) {
sendCode();
write("A code has been sent to your devices, please write it here: ");
stdout.flush();
string code = readln();
submitCode(code);
}).match!(
(DeveloperSession session) => session,
(AppleLoginError error) {
auto errorStr = format!"%s (%d)"(error.description, error);
getLogger().errorF!"Apple auth error: %s"(errorStr);
return null;
}
// Write the updated Info.plist with the new bundle identifier.
file.write(bundlePath.buildPath("Info.plist"), bundle.appInfo.toXml());
file.write(bundlePath.buildPath("embedded.mobileprovision"), profile.encodedProfile);
// */
);

if (appleAccount) {
string udid = iDevice.deviceList()[0].udid;
log.infoF!"Initiating connection the device (UUID: %s)"(udid);
auto device = new iDevice(udid);
sideloadFull(device, appleAccount, new Application(appPath), (progress, action) {
log.infoF!"%s (%.2f%%)"(action, progress * 100);
});
}

return 0;
}
}
Expand Down
6 changes: 4 additions & 2 deletions dub.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@
],

"targetPath": "bin/",
"stringImportPaths": ["resources/"],

"buildRequirements": ["allowWarnings"],
"buildRequirements": ["allowWarnings", "requireBoundsCheck"],

"dependencies": {
"botan": {
Expand All @@ -18,9 +19,10 @@
"repository": "git+https://github.com/Dadoum/dynamicloader.git",
"version": "32355c1aae76e0a89c123bc082ec2df8cddc2b0f"
},
"intel-intrinsics": "~>1.11.15",
"plist-d": {
"repository": "git+https://github.com/Dadoum/libplist-d.git",
"version": "f04b7ebf2623ff5e3ad608910c3a3ac56639a21f"
"version": "ad7ce217b56af8e51647ab9dd86157b94ef58325"
},
"provision": {
"repository": "git+https://github.com/Dadoum/Provision.git",
Expand Down
3 changes: 2 additions & 1 deletion dub.selections.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,11 @@
"gtk_d": "1.0.3",
"icontheme": "1.2.3",
"inilike": "1.2.2",
"intel-intrinsics": "1.11.15",
"isfreedesktop": "0.1.1",
"memutils": "1.0.9",
"plist": "~master",
"plist-d": {"version":"f04b7ebf2623ff5e3ad608910c3a3ac56639a21f","repository":"git+https://github.com/Dadoum/libplist-d.git"},
"plist-d": {"version":"ad7ce217b56af8e51647ab9dd86157b94ef58325","repository":"git+https://github.com/Dadoum/libplist-d.git"},
"provision": {"version":"a007cb290da1a21e231a1a4a335a047a151ca24f","repository":"git+https://github.com/Dadoum/Provision.git"},
"requests": "2.0.9",
"silly": "1.2.0-dev.2",
Expand Down
6 changes: 3 additions & 3 deletions linux/gtk/ui/sideloadergtkapplication.d
Original file line number Diff line number Diff line change
Expand Up @@ -75,11 +75,11 @@ class SideloaderGtkApplication: Application {
aboutDialog.addCreditSection("SideStore contributors (no shared code)", ["Riley Testut", "Kabir Oberai", `Joelle "Lonkelle"`,
`Nick "nythepegasus"`, `James "JJTech"`, `Joss "bogotesr"`, `naturecodevoid`,
`many other, open a GH issue if needed`]);
aboutDialog.addCreditSection("zsign (no shared code)", [`zhlynn`]);
aboutDialog.addCreditSection("OG Cydia Impactor, ldid (no shared code)", [`Jay "saurik" Freeman`]);
aboutDialog.addCreditSection("Help on app signature", [`DebianArch`, `zhlynn (zsign)`, `Jay "saurik" Freeman (ldid)`]);
aboutDialog.addCreditSection("Apple Music for Android libraries", [`Apple`]);

aboutDialog.setComments("Don't hesitate to reach me out if I forgot someone in the credits!");
aboutDialog.setComments("Don't hesitate to reach me out if I forgot someone in the credits! \n"
~ "Note: most of them are not involved in the development of this software whatsoever. Do not report any issue to them!!");

aboutDialog.show();
});
Expand Down
Binary file added resources/AppleIncRootCertificate.cer
Binary file not shown.
Binary file added resources/AppleWWDRCAG3.cer
Binary file not shown.
10 changes: 10 additions & 0 deletions source/main.d
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,20 @@ int main(string[] args) {
Logger log = getLogger();

frontend = native_frontend.makeFrontend();
log.infoF!"Sideloader, compiled with %s on %s at %s"(__VENDOR__, __DATE__, __TIME__);
log.infoF!"Configuration path: %s"(frontend.configurationPath());
if (!file.exists(frontend.configurationPath)) {
file.mkdirRecurse(frontend.configurationPath);
}

return frontend.run(args);
}

static this() {
version (DigitalMars) {

} else {
static import sse2;
sse2.register();
}
}
Loading

0 comments on commit 769f2eb

Please sign in to comment.