This commit is contained in:
Stephen 2025-03-27 12:31:54 -04:00
parent 11863ff763
commit 3fd7d86455
4 changed files with 221 additions and 5 deletions

View file

@ -5,3 +5,4 @@
#include "idevice.h"
#include "heartbeat.h"
#include "jit.h"
#include "applist.h"

View file

@ -8,9 +8,10 @@
import SwiftUI
struct HomeView: View {
@AppStorage("bundleID") private var bundleID: String = "com.stossy11.MeloNX"
@AppStorage("bundleID") private var bundleID: String = ""
@State private var isProcessing = false
@State private var isShowingInstalledApps = false
var body: some View {
ZStack {
Color(.systemGroupedBackground)
@ -29,13 +30,16 @@ struct HomeView: View {
.multilineTextAlignment(.center)
}
.padding(.top, 40)
RoundedTextField(placeholder: "Enter Bundle ID", text: $bundleID)
.padding(.horizontal, 20)
Button(action: {
HapticFeedbackHelper.trigger()
startJITInBackground()
startJITInBackground(with: bundleID)
}) {
Label(isProcessing ? "Enabling..." : "Enable JIT", systemImage: isProcessing ? "hourglass" : "bolt.fill")
Label(isProcessing ? "Enabling..." : "Enable JIT",
systemImage: isProcessing ? "hourglass" : "bolt.fill")
.font(.system(.title3, design: .rounded))
.fontWeight(.semibold)
.frame(maxWidth: .infinity)
@ -49,13 +53,36 @@ struct HomeView: View {
.disabled(isProcessing)
.padding(.horizontal, 20)
Button(action: {
isShowingInstalledApps = true
}) {
Label("View Installed Apps", systemImage: "list.bullet")
.font(.system(.title3, design: .rounded))
.fontWeight(.semibold)
.frame(maxWidth: .infinity)
.padding()
.background(Color.green)
.foregroundColor(.white)
.cornerRadius(16)
.shadow(color: Color.green.opacity(0.3), radius: 8, x: 0, y: 4)
}
.padding(.horizontal, 20)
Spacer()
}
.padding()
}
.sheet(isPresented: $isShowingInstalledApps) {
InstalledAppsListView { selectedBundle in
bundleID = selectedBundle
isShowingInstalledApps = false
HapticFeedbackHelper.trigger()
startJITInBackground(with: selectedBundle)
}
}
}
private func startJITInBackground() {
private func startJITInBackground(with bundleID: String) {
isProcessing = true
DispatchQueue.global(qos: .background).async {
guard let cBundleID = strdup(bundleID) else {
@ -78,6 +105,79 @@ struct HomeView: View {
}
}
class InstalledAppsViewModel: ObservableObject {
@Published var apps: [String] = []
init() {
loadApps()
}
func loadApps() {
guard let rawPointer = list_installed_apps() else {
self.apps = []
return
}
let output = String(cString: rawPointer)
free(rawPointer)
if output.hasPrefix("Error:") {
self.apps = []
} else {
self.apps = output.components(separatedBy: "\n").filter { !$0.isEmpty }
}
}
}
struct InstalledAppsListView: View {
@StateObject var viewModel = InstalledAppsViewModel()
@Environment(\.dismiss) var dismiss
@State private var searchText: String = ""
var onSelect: (String) -> Void
var filteredApps: [String] {
if searchText.isEmpty {
return viewModel.apps
} else {
return viewModel.apps.filter { $0.localizedCaseInsensitiveContains(searchText) }
}
}
var body: some View {
NavigationView {
ZStack {
Color(.systemGroupedBackground)
.edgesIgnoringSafeArea(.all)
List {
ForEach(filteredApps, id: \.self) { app in
Button(action: {
onSelect(app)
}) {
Text(app)
.font(.system(.body, design: .rounded))
.padding(.vertical, 8)
}
.listRowBackground(Color(.secondarySystemGroupedBackground))
.cornerRadius(8)
}
}
.listStyle(InsetGroupedListStyle())
.navigationTitle("Installed Apps")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Done") {
dismiss()
}
}
}
.searchable(text: $searchText, prompt: "Search Bundle IDs")
}
}
}
}
#Preview {
HomeView()
}

102
StikJIT/idevice/applist.c Normal file
View file

@ -0,0 +1,102 @@
//
// applist.c
// StikJIT
//
// Created by Stephen on 3/27/25.
//
#include "idevice.h"
#include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <CoreFoundation/CoreFoundation.h>
#include <limits.h>
char *list_installed_apps() {
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_port = htons(LOCKDOWN_PORT);
if (inet_pton(AF_INET, "10.7.0.1", &addr.sin_addr) <= 0) {
return strdup("Error: Invalid IP address");
}
CFURLRef pairingFileURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(),
CFSTR("pairing_file"),
CFSTR("plist"),
NULL);
if (pairingFileURL == NULL) {
return strdup("Error: Pairing file not found");
}
char pairingFilePath[PATH_MAX];
if (!CFURLGetFileSystemRepresentation(pairingFileURL, TRUE, (UInt8 *)pairingFilePath, PATH_MAX)) {
CFRelease(pairingFileURL);
return strdup("Error: Pairing file path conversion failed");
}
CFRelease(pairingFileURL);
IdevicePairingFile *pairing_file = NULL;
IdeviceErrorCode err = idevice_pairing_file_read(pairingFilePath, &pairing_file);
if (err != IdeviceSuccess) {
return strdup("Error: Failed to read pairing file");
}
TcpProviderHandle *provider = NULL;
err = idevice_tcp_provider_new((struct sockaddr *)&addr, pairing_file, "ExampleProvider", &provider);
if (err != IdeviceSuccess) {
idevice_pairing_file_free(pairing_file);
return strdup("Error: Failed to create TCP provider");
}
InstallationProxyClientHandle *client = NULL;
err = installation_proxy_connect_tcp(provider, &client);
if (err != IdeviceSuccess) {
tcp_provider_free(provider);
return strdup("Error: Failed to connect to installation proxy");
}
void *apps = NULL;
size_t apps_len = 0;
err = installation_proxy_get_apps(client, NULL, NULL, 0, &apps, &apps_len);
if (err != IdeviceSuccess) {
installation_proxy_client_free(client);
tcp_provider_free(provider);
return strdup("Error: Failed to get apps");
}
plist_t *app_list = (plist_t *)apps;
char *result = malloc(8192); // Allocate memory for output
result[0] = '\0';
for (size_t i = 0; i < apps_len; i++) {
plist_t app = app_list[i];
// Check if the app has an "Entitlements" dictionary.
plist_t entitlements = plist_dict_get_item(app, "Entitlements");
if (entitlements) {
// Look for the "get-task-allow" key.
plist_t taskAllowNode = plist_dict_get_item(entitlements, "get-task-allow");
if (taskAllowNode) {
int isAllowed = 0;
plist_get_bool_val(taskAllowNode, &isAllowed);
if (isAllowed) {
// Retrieve the bundle identifier if the entitlement is true.
plist_t bundle_id_node = plist_dict_get_item(app, "CFBundleIdentifier");
if (bundle_id_node) {
char *bundle_id = NULL;
plist_get_string_val(bundle_id_node, &bundle_id);
strcat(result, bundle_id);
strcat(result, "\n");
free(bundle_id);
}
}
}
}
}
installation_proxy_client_free(client);
tcp_provider_free(provider);
return result;
}

13
StikJIT/idevice/applist.h Normal file
View file

@ -0,0 +1,13 @@
//
// applist.h
// StikJIT
//
// Created by Stephen on 3/27/25.
//
#ifndef APPLIST_H
#define APPLIST_H
char *list_installed_apps(void);
#endif /* APPLIST_H */