From 0a65264b96ce0c69536edb0b9fad000d34dfbeaf Mon Sep 17 00:00:00 2001
From: Stephen <158498287+0-Blu@users.noreply.github.com>
Date: Sun, 1 Jun 2025 23:14:44 -0400
Subject: [PATCH] PT 1 of Changes.
---
StikDebug.xcodeproj/project.pbxproj | 4 +-
.../xcschemes/DebugWidgetExtension.xcscheme | 113 ++++++++++++
.../xcshareddata/xcschemes/StikDebug.xcscheme | 78 ++++++++
.../xcschemes/TunnelProv.xcscheme | 97 ++++++++++
.../Extensions & Stuff/DebugViewModel.swift | 7 -
.../Extensions & Stuff/FancyBlurView.swift | 19 --
StikJIT/Extensions & Stuff/Font.swift | 14 --
.../Extensions & Stuff/RoundedTextField.swift | 34 ----
.../Extensions & Stuff/VisualEffectBlur.swift | 21 ---
StikJIT/StikJIT-Bridging-Header.h | 1 -
StikJIT/Utilities/AppStoreIconFetcher.swift | 32 ++--
.../Color.swift | 0
.../HapticFeedbackHelper.swift | 0
StikJIT/Views/AppIconView.swift | 60 ------
StikJIT/Views/InstalledAppsListView.swift | 73 ++++++--
StikJIT/Views/SettingsView.swift | 16 +-
StikJIT/idevice/applist.m | 117 +++++-------
StikJIT/idevice/em_proxy.h | 40 ----
TunnelProv/PacketTunnelProvider.swift | 172 ++++--------------
19 files changed, 459 insertions(+), 439 deletions(-)
create mode 100644 StikDebug.xcodeproj/xcshareddata/xcschemes/DebugWidgetExtension.xcscheme
create mode 100644 StikDebug.xcodeproj/xcshareddata/xcschemes/StikDebug.xcscheme
create mode 100644 StikDebug.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme
delete mode 100644 StikJIT/Extensions & Stuff/DebugViewModel.swift
delete mode 100644 StikJIT/Extensions & Stuff/FancyBlurView.swift
delete mode 100644 StikJIT/Extensions & Stuff/Font.swift
delete mode 100644 StikJIT/Extensions & Stuff/RoundedTextField.swift
delete mode 100644 StikJIT/Extensions & Stuff/VisualEffectBlur.swift
rename StikJIT/{Extensions & Stuff => Utilities}/Color.swift (100%)
rename StikJIT/{Extensions & Stuff => Utilities}/HapticFeedbackHelper.swift (100%)
delete mode 100644 StikJIT/Views/AppIconView.swift
delete mode 100644 StikJIT/idevice/em_proxy.h
diff --git a/StikDebug.xcodeproj/project.pbxproj b/StikDebug.xcodeproj/project.pbxproj
index 77442d0..f40f538 100644
--- a/StikDebug.xcodeproj/project.pbxproj
+++ b/StikDebug.xcodeproj/project.pbxproj
@@ -502,7 +502,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 1.0;
+ MARKETING_VERSION = 1.3.7;
PRODUCT_BUNDLE_IDENTIFIER = com.stik.sj.DebugWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
@@ -532,7 +532,7 @@
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
- MARKETING_VERSION = 1.0;
+ MARKETING_VERSION = 1.3.7;
PRODUCT_BUNDLE_IDENTIFIER = com.stik.sj.DebugWidget;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
diff --git a/StikDebug.xcodeproj/xcshareddata/xcschemes/DebugWidgetExtension.xcscheme b/StikDebug.xcodeproj/xcshareddata/xcschemes/DebugWidgetExtension.xcscheme
new file mode 100644
index 0000000..732e619
--- /dev/null
+++ b/StikDebug.xcodeproj/xcshareddata/xcschemes/DebugWidgetExtension.xcscheme
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/StikDebug.xcodeproj/xcshareddata/xcschemes/StikDebug.xcscheme b/StikDebug.xcodeproj/xcshareddata/xcschemes/StikDebug.xcscheme
new file mode 100644
index 0000000..8948422
--- /dev/null
+++ b/StikDebug.xcodeproj/xcshareddata/xcschemes/StikDebug.xcscheme
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/StikDebug.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme b/StikDebug.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme
new file mode 100644
index 0000000..43ab266
--- /dev/null
+++ b/StikDebug.xcodeproj/xcshareddata/xcschemes/TunnelProv.xcscheme
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/StikJIT/Extensions & Stuff/DebugViewModel.swift b/StikJIT/Extensions & Stuff/DebugViewModel.swift
deleted file mode 100644
index f278a20..0000000
--- a/StikJIT/Extensions & Stuff/DebugViewModel.swift
+++ /dev/null
@@ -1,7 +0,0 @@
-//
-// DebugViewModel.swift
-// StikJIT
-//
-// Created by Stephen on 3/27/25.
-//
-
diff --git a/StikJIT/Extensions & Stuff/FancyBlurView.swift b/StikJIT/Extensions & Stuff/FancyBlurView.swift
deleted file mode 100644
index 9f95c31..0000000
--- a/StikJIT/Extensions & Stuff/FancyBlurView.swift
+++ /dev/null
@@ -1,19 +0,0 @@
-//
-// FancyBlurView.swift
-// StikJIT
-//
-// Created by Stephen on 3/27/25.
-//
-
-import SwiftUI
-
-// Custom Blur Effect for macOS/iOS
-struct FancyBlurView: View {
- var body: some View {
- #if os(macOS)
- VisualEffectBlur(material: .sidebar, blendingMode: .behindWindow)
- #else
- VisualEffectBlur(style: .systemUltraThinMaterial)
- #endif
- }
-}
diff --git a/StikJIT/Extensions & Stuff/Font.swift b/StikJIT/Extensions & Stuff/Font.swift
deleted file mode 100644
index 2e74d79..0000000
--- a/StikJIT/Extensions & Stuff/Font.swift
+++ /dev/null
@@ -1,14 +0,0 @@
-//
-// Font.swift
-// StikJIT
-//
-// Created by Stephen on 3/27/25.
-//
-
-import SwiftUI
-
-extension Font {
- static let titleFont = Font.system(size: 24, weight: .bold)
- static let bodyFont = Font.system(size: 18, weight: .regular)
- static let captionFont = Font.system(size: 14, weight: .medium)
-}
diff --git a/StikJIT/Extensions & Stuff/RoundedTextField.swift b/StikJIT/Extensions & Stuff/RoundedTextField.swift
deleted file mode 100644
index 04970a9..0000000
--- a/StikJIT/Extensions & Stuff/RoundedTextField.swift
+++ /dev/null
@@ -1,34 +0,0 @@
-//
-// RoundedTextField.swift
-// StikJIT
-//
-// Created by Stephen on 3/27/25.
-//
-
-import SwiftUI
-
-struct RoundedTextField: View {
- var placeholder: String
- @Binding var text: String
-
- var body: some View {
- HStack {
- TextField(placeholder, text: $text)
- .font(.system(.body, design: .rounded))
- .autocapitalization(.none)
- .disableAutocorrection(true)
- .foregroundColor(.primary)
-
- if !text.isEmpty {
- Button(action: { text = "" }) {
- Image(systemName: "xmark.circle.fill")
- .foregroundColor(.gray)
- }
- .buttonStyle(.plain)
- }
- }
- .padding()
- .background(RoundedRectangle(cornerRadius: 12).fill(Color(.secondarySystemBackground)))
- .overlay(RoundedRectangle(cornerRadius: 12).stroke(Color(.tertiaryLabel), lineWidth: 0.5))
- }
-}
diff --git a/StikJIT/Extensions & Stuff/VisualEffectBlur.swift b/StikJIT/Extensions & Stuff/VisualEffectBlur.swift
deleted file mode 100644
index 6347fe5..0000000
--- a/StikJIT/Extensions & Stuff/VisualEffectBlur.swift
+++ /dev/null
@@ -1,21 +0,0 @@
-//
-// VisualEffectBlur.swift
-// StikJIT
-//
-// Created by Stephen on 3/27/25.
-//
-
-import SwiftUI
-
-// Universal Blur Effect (for iOS)
-struct VisualEffectBlur: UIViewRepresentable {
- let style: UIBlurEffect.Style
-
- func makeUIView(context: Context) -> UIVisualEffectView {
- let view = UIVisualEffectView(effect: UIBlurEffect(style: style))
- return view
- }
-
- func updateUIView(_ uiView: UIVisualEffectView, context: Context) {}
-}
-
diff --git a/StikJIT/StikJIT-Bridging-Header.h b/StikJIT/StikJIT-Bridging-Header.h
index 7ce5a25..963bed2 100644
--- a/StikJIT/StikJIT-Bridging-Header.h
+++ b/StikJIT/StikJIT-Bridging-Header.h
@@ -4,5 +4,4 @@
#include "idevice/JITEnableContext.h"
#include "idevice/idevice.h"
-#include "idevice/em_proxy.h"
#include "idevice/heartbeat.h"
diff --git a/StikJIT/Utilities/AppStoreIconFetcher.swift b/StikJIT/Utilities/AppStoreIconFetcher.swift
index a4e9829..e0cbf49 100644
--- a/StikJIT/Utilities/AppStoreIconFetcher.swift
+++ b/StikJIT/Utilities/AppStoreIconFetcher.swift
@@ -8,31 +8,23 @@
import UIKit
-// Not AppStore
-// Uses idevice
class AppStoreIconFetcher {
- static private var iconCache: [String: UIImage] = [:]
- static private let iconFetchDispatchQueue = DispatchQueue(label: "com.stik.StikJIT.iconFetchQueue", attributes: .concurrent)
-
+ private static var cache = [String: UIImage]()
+ private static let queue = DispatchQueue(label: "com.stik.StikJIT.iconFetchQueue", attributes: .concurrent)
+
static func getIcon(for bundleID: String, completion: @escaping (UIImage?) -> Void) {
- // Check cache first
- if let cachedIcon = iconCache[bundleID] {
- completion(cachedIcon)
+ if let icon = cache[bundleID] {
+ completion(icon)
return
}
-
- iconFetchDispatchQueue.async {
- do {
- let ans = try JITEnableContext.shared.getAppIcon(withBundleId: bundleID)
- DispatchQueue.main.async {
- iconCache[bundleID] = ans
- completion(ans)
- }
- } catch {
- print("Failed to get icon: \(error)")
- DispatchQueue.main.async {
- completion(nil)
+
+ queue.async {
+ let icon = try? JITEnableContext.shared.getAppIcon(withBundleId: bundleID)
+ DispatchQueue.main.async {
+ if let img = icon {
+ cache[bundleID] = img
}
+ completion(icon)
}
}
}
diff --git a/StikJIT/Extensions & Stuff/Color.swift b/StikJIT/Utilities/Color.swift
similarity index 100%
rename from StikJIT/Extensions & Stuff/Color.swift
rename to StikJIT/Utilities/Color.swift
diff --git a/StikJIT/Extensions & Stuff/HapticFeedbackHelper.swift b/StikJIT/Utilities/HapticFeedbackHelper.swift
similarity index 100%
rename from StikJIT/Extensions & Stuff/HapticFeedbackHelper.swift
rename to StikJIT/Utilities/HapticFeedbackHelper.swift
diff --git a/StikJIT/Views/AppIconView.swift b/StikJIT/Views/AppIconView.swift
deleted file mode 100644
index 8bbca3b..0000000
--- a/StikJIT/Views/AppIconView.swift
+++ /dev/null
@@ -1,60 +0,0 @@
-//
-// AppIconView.swift
-// StikJIT
-//
-// Created by neoarz on 4/12/25.
-//
-
-import SwiftUI
-
-struct AppIconView: View {
- @Environment(\.dismiss) private var dismiss
- @AppStorage("customAccentColor") private var customAccentColorHex: String = ""
-
- private var accentColor: Color {
- if customAccentColorHex.isEmpty {
- return .blue
- } else {
- return Color(hex: customAccentColorHex) ?? .blue
- }
- }
-
- var body: some View {
- NavigationView {
- ZStack {
- Color(UIColor.systemBackground)
- .ignoresSafeArea()
-
- VStack(spacing: 24) {
- Image(systemName: "hammer.fill")
- .font(.system(size: 60))
- .foregroundColor(accentColor)
- .padding(.bottom, 8)
-
- Text("Work in Progress")
- .font(.system(size: 24, weight: .bold))
- .multilineTextAlignment(.center)
-
- Text("App icon customization coming soon!")
- .font(.system(size: 17))
- .foregroundColor(.secondary)
- .multilineTextAlignment(.center)
- }
- .padding()
- }
- .navigationTitle("App Icon")
- .navigationBarTitleDisplayMode(.inline)
- .toolbar {
- ToolbarItem(placement: .navigationBarTrailing) {
- Button("Done") {
- dismiss()
- }
- }
- }
- }
- }
-}
-
-#Preview {
- AppIconView()
-}
diff --git a/StikJIT/Views/InstalledAppsListView.swift b/StikJIT/Views/InstalledAppsListView.swift
index cf5eb00..fd06de6 100644
--- a/StikJIT/Views/InstalledAppsListView.swift
+++ b/StikJIT/Views/InstalledAppsListView.swift
@@ -28,6 +28,10 @@ struct InstalledAppsListView: View {
@Environment(\.dismiss) private var dismiss
var onSelectApp: (String) -> Void
+ private var filteredRecents: [String] {
+ recentApps.filter { !favoriteApps.contains($0) }
+ }
+
var body: some View {
NavigationView {
Group {
@@ -47,7 +51,6 @@ struct InstalledAppsListView: View {
.background(Color(UIColor.systemGroupedBackground).edgesIgnoringSafeArea(.all))
}
- // MARK: - Empty State
private var emptyState: some View {
VStack(spacing: 16) {
Image(systemName: "magnifyingglass")
@@ -64,11 +67,10 @@ Please check if the app you want to connect to is signed with a **development**
.background(Color(UIColor.systemBackground))
}
- // MARK: - List of Apps
private var appsList: some View {
List {
if !favoriteApps.isEmpty {
- Section(header: Text("Favorites - (\(favoriteApps.count)/4)")) {
+ Section(header: Text("Favorites (\(favoriteApps.count)/4)")) {
ForEach(favoriteApps, id: \.self) { bundleID in
AppButton(
bundleID: bundleID,
@@ -83,9 +85,9 @@ Please check if the app you want to connect to is signed with a **development**
}
}
- if !recentApps.isEmpty {
+ if !filteredRecents.isEmpty {
Section(header: Text("Recents")) {
- ForEach(recentApps, id: \.self) { bundleID in
+ ForEach(filteredRecents, id: \.self) { bundleID in
AppButton(
bundleID: bundleID,
appName: viewModel.apps[bundleID] ?? bundleID,
@@ -100,7 +102,6 @@ Please check if the app you want to connect to is signed with a **development**
withAnimation {
recentApps.removeAll { $0 == bundleID }
sharedDefaults.set(recentApps, forKey: "recentApps")
- sharedDefaults.set(favoriteApps, forKey: "favoriteApps")
WidgetCenter.shared.reloadAllTimelines()
}
} label: {
@@ -111,7 +112,7 @@ Please check if the app you want to connect to is signed with a **development**
}
}
- Section(header: Text((favoriteApps.isEmpty && recentApps.isEmpty) ? "" : "All Applications")) {
+ Section(header: Text((favoriteApps.isEmpty && filteredRecents.isEmpty) ? "" : "All Applications")) {
ForEach(viewModel.apps.sorted(by: { $0.key < $1.key }), id: \.key) { bundleID, appName in
AppButton(
bundleID: bundleID,
@@ -168,14 +169,20 @@ struct AppButton: View {
Group {
if loadAppIconsOnJIT, let image = appIcons[bundleID] {
Image(uiImage: image)
- .resizable().aspectRatio(contentMode: .fit)
- .frame(width: 60, height: 60).cornerRadius(12)
+ .resizable()
+ .aspectRatio(contentMode: .fit)
+ .frame(width: 60, height: 60)
+ .cornerRadius(12)
.shadow(color: colorScheme == .dark ? .black.opacity(0.2) : .gray.opacity(0.2), radius: 3, x: 0, y: 1)
} else {
RoundedRectangle(cornerRadius: 12)
.fill(Color(UIColor.systemGray5))
.frame(width: 60, height: 60)
- .overlay(Image(systemName: "app").font(.system(size: 26)).foregroundColor(.gray))
+ .overlay(
+ Image(systemName: "app")
+ .font(.system(size: 26))
+ .foregroundColor(.gray)
+ )
.shadow(color: .black.opacity(0.1), radius: 2, x: 0, y: 1)
.onAppear { loadAppIcon(for: bundleID) }
}
@@ -184,8 +191,13 @@ struct AppButton: View {
private var appText: some View {
VStack(alignment: .leading, spacing: 4) {
- Text(appName).font(.system(size: 18, weight: .semibold)).foregroundColor(.primary)
- Text(bundleID).font(.system(size: 15)).foregroundColor(.gray).lineLimit(1)
+ Text(appName)
+ .font(.system(size: 18, weight: .semibold))
+ .foregroundColor(.primary)
+ Text(bundleID)
+ .font(.system(size: 15))
+ .foregroundColor(.gray)
+ .lineLimit(1)
}
}
@@ -204,6 +216,7 @@ struct AppButton: View {
favoriteApps.removeAll { $0 == bundleID }
} else if favoriteApps.count < 4 {
favoriteApps.insert(bundleID, at: 0)
+ recentApps.removeAll { $0 == bundleID }
}
sharedDefaults.set(recentApps, forKey: "recentApps")
sharedDefaults.set(favoriteApps, forKey: "favoriteApps")
@@ -212,17 +225,39 @@ struct AppButton: View {
private func loadAppIcon(for bundleID: String) {
guard loadAppIconsOnJIT else { return }
+
+ // 1) Check disk cache first
+ if let cachedImage = loadCachedIcon(bundleID: bundleID) {
+ DispatchQueue.main.async {
+ appIcons[bundleID] = cachedImage
+ }
+ return
+ }
+
+ // 2) Otherwise fetch from network and then cache
AppStoreIconFetcher.getIcon(for: bundleID) { image in
- if let image = image {
- DispatchQueue.main.async {
- withAnimation(.easeIn(duration: 0.2)) {
- appIcons[bundleID] = image
- saveIconToGroup(image, bundleID: bundleID)
- }
+ guard let image = image else { return }
+ DispatchQueue.main.async {
+ withAnimation(.easeIn(duration: 0.2)) {
+ appIcons[bundleID] = image
}
}
+ saveIconToGroup(image, bundleID: bundleID)
}
}
+
+ private func loadCachedIcon(bundleID: String) -> UIImage? {
+ guard
+ let containerURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.stik.sj")
+ else { return nil }
+
+ let iconsDir = containerURL.appendingPathComponent("icons", isDirectory: true)
+ let fileURL = iconsDir.appendingPathComponent("\(bundleID).png")
+ if FileManager.default.fileExists(atPath: fileURL.path) {
+ return UIImage(contentsOfFile: fileURL.path)
+ }
+ return nil
+ }
}
fileprivate func saveIconToGroup(_ image: UIImage, bundleID: String) {
@@ -230,6 +265,7 @@ fileprivate func saveIconToGroup(_ image: UIImage, bundleID: String) {
let data = image.pngData(),
let container = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "group.com.stik.sj")
else { return }
+
let iconsDir = container.appendingPathComponent("icons", isDirectory: true)
try? FileManager.default.createDirectory(at: iconsDir, withIntermediateDirectories: true)
let fileURL = iconsDir.appendingPathComponent("\(bundleID).png")
@@ -252,6 +288,7 @@ extension Array: @retroactive RawRepresentable where Element: Codable {
}
}
+// Preview helper
#Preview {
InstalledAppsListView { _ in }
}
diff --git a/StikJIT/Views/SettingsView.swift b/StikJIT/Views/SettingsView.swift
index 05a9ff1..518ee67 100644
--- a/StikJIT/Views/SettingsView.swift
+++ b/StikJIT/Views/SettingsView.swift
@@ -264,7 +264,21 @@ struct SettingsView: View {
self.mounted = isMounted()
}
}
-
+ SettingsCard {
+ VStack(alignment: .leading, spacing: 20) {
+ Text("Behavior")
+ .font(.headline)
+ .foregroundColor(.primary)
+ .padding(.bottom, 4)
+
+ Toggle("Automatically Quit After Enabling JIT", isOn: $doAutoQuitAfterEnablingJIT)
+ .foregroundColor(.primary)
+ .padding(.vertical, 6)
+ }
+ .padding(.vertical, 20)
+ .padding(.horizontal, 16)
+ }
+
// About section
SettingsCard {
VStack(alignment: .leading, spacing: 20) {
diff --git a/StikJIT/idevice/applist.m b/StikJIT/idevice/applist.m
index 5d6e355..06d49a2 100644
--- a/StikJIT/idevice/applist.m
+++ b/StikJIT/idevice/applist.m
@@ -5,109 +5,90 @@
// Created by Stephen on 3/27/25.
//
-#include "idevice.h"
+#import "idevice.h"
#include
-#include
#include
#include
-#include
-
-#include "applist.h"
+#import "applist.h"
NSDictionary* list_installed_apps(TcpProviderHandle* provider, NSString** error) {
- IdeviceErrorCode err = IdeviceSuccess;
-
InstallationProxyClientHandle *client = NULL;
- err = installation_proxy_connect_tcp(provider, &client);
- if (err != IdeviceSuccess) {
+ if (installation_proxy_connect_tcp(provider, &client) != IdeviceSuccess) {
*error = @"Failed to connect to installation proxy";
return nil;
}
void *apps = NULL;
- size_t apps_len = 0;
- err = installation_proxy_get_apps(client, "User", NULL, 0, &apps, &apps_len);
- if (err != IdeviceSuccess) {
+ size_t count = 0;
+ if (installation_proxy_get_apps(client, "User", NULL, 0, &apps, &count) != IdeviceSuccess) {
installation_proxy_client_free(client);
*error = @"Failed to get apps";
return nil;
}
- plist_t *app_list = (plist_t *)apps;
-
- NSMutableDictionary* ans = [[NSMutableDictionary alloc] init];
-
- 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) {
- uint8_t 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);
+ NSMutableDictionary *result = [NSMutableDictionary dictionaryWithCapacity:count];
- // Skip if bundle ID is empty
- if (bundle_id == NULL || strlen(bundle_id) == 0) {
- free(bundle_id);
- continue;
- }
+ for (size_t i = 0; i < count; i++) {
+ plist_t app = ((plist_t *)apps)[i];
+ plist_t ent = plist_dict_get_item(app, "Entitlements");
+ if (!ent) continue;
- // Retrieve the app name
- plist_t app_name_node = plist_dict_get_item(app, "CFBundleName");
- char *app_name = NULL;
- if (app_name_node) {
- plist_get_string_val(app_name_node, &app_name);
- } else {
- app_name = strdup("Unknown");
- }
+ plist_t tnode = plist_dict_get_item(ent, "get-task-allow");
+ if (!tnode) continue;
- ans[[NSString stringWithCString:bundle_id encoding:NSASCIIStringEncoding]] = [NSString stringWithCString:app_name encoding:NSASCIIStringEncoding];
+ uint8_t isAllowed = 0;
+ plist_get_bool_val(tnode, &isAllowed);
+ if (!isAllowed) continue;
- free(bundle_id);
- free(app_name);
- }
- }
- }
+ plist_t bidNode = plist_dict_get_item(app, "CFBundleIdentifier");
+ if (!bidNode) continue;
+
+ char *bidC = NULL;
+ plist_get_string_val(bidNode, &bidC);
+ if (!bidC || bidC[0] == '\0') {
+ free(bidC);
+ continue;
}
+ NSString *bundleID = [NSString stringWithUTF8String:bidC];
+ free(bidC);
+
+ NSString *appName = @"Unknown";
+ plist_t nameNode = plist_dict_get_item(app, "CFBundleName");
+ if (nameNode) {
+ char *nameC = NULL;
+ plist_get_string_val(nameNode, &nameC);
+ if (nameC && nameC[0] != '\0') {
+ appName = [NSString stringWithUTF8String:nameC];
+ }
+ free(nameC);
+ }
+
+ result[bundleID] = appName;
}
-
installation_proxy_client_free(client);
-
- return ans;
+ return result;
}
-
UIImage* getAppIcon(TcpProviderHandle* provider, NSString* bundleID, NSString** error) {
- IdeviceErrorCode err = IdeviceSuccess;
-
SpringBoardServicesClientHandle *client = NULL;
- springboard_services_connect_tcp(provider, &client);
- if (err != IdeviceSuccess) {
+ if (springboard_services_connect_tcp(provider, &client) != IdeviceSuccess) {
*error = @"Failed to connect to SpringBoard Services";
return nil;
}
-
+
void *pngData = NULL;
- size_t data_len = 0;
- err = springboard_services_get_icon(client, [bundleID UTF8String], &pngData, &data_len);
- if (err != IdeviceSuccess) {
+ size_t dataLen = 0;
+ if (springboard_services_get_icon(client, [bundleID UTF8String], &pngData, &dataLen) != IdeviceSuccess) {
springboard_services_free(client);
*error = @"Failed to get app icon";
return nil;
}
-
- NSData* pngNSData = [NSData dataWithBytes:pngData length:data_len];
+
+ NSData *data = [NSData dataWithBytes:pngData length:dataLen];
free(pngData);
- UIImage* ans = [UIImage imageWithData:pngNSData];
- return ans;
-
+ UIImage *icon = [UIImage imageWithData:data];
+
+ springboard_services_free(client);
+ return icon;
}
diff --git a/StikJIT/idevice/em_proxy.h b/StikJIT/idevice/em_proxy.h
deleted file mode 100644
index fd09f4f..0000000
--- a/StikJIT/idevice/em_proxy.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Jackson Coxson
-
-#include
-#include
-#include
-#include
-#include
-
-
-/**
- * Starts your emotional damage
- * # Arguments
- * * `bind_addr` - The UDP socket to listen to
- * # Returns
- * A handle to stop further emotional damage.
- * Null on failure
- * # Safety
- * Don't be stupid
- */
-int start_emotional_damage(const char *bind_addr);
-
-/**
- * Stops further emotional damage
- * # Arguments
- * * `handle` - The coping mechanism generated by start_emotional_damage
- * # Returns
- * The knowledge of knowing that you couldn't handle failure
- * # Safety
- * Don't be stupid
- */
-void stop_emotional_damage(void);
-
-/**
- * Blocks until Wireguard is ready
- * # Arguments
- * * `timeout` - The timeout in miliseconds to wait for Wireguard
- * # Returns
- * 0 on success, -1 on failure
- */
-int test_emotional_damage(int timeout);
diff --git a/TunnelProv/PacketTunnelProvider.swift b/TunnelProv/PacketTunnelProvider.swift
index 45feab3..d6e6c17 100644
--- a/TunnelProv/PacketTunnelProvider.swift
+++ b/TunnelProv/PacketTunnelProvider.swift
@@ -2,152 +2,56 @@
// PacketTunnelProvider.swift
// TunnelProv
//
-// Created by Stossy11 on 28/03/2025.
+// Created by Stephen on 06/01/2025.
+//
+// Inspired by Stossy & StosVPN
//
import NetworkExtension
+import Darwin
class PacketTunnelProvider: NEPacketTunnelProvider {
- var tunnelDeviceIp: String = "10.7.0.0"
- var tunnelFakeIp: String = "10.7.0.1"
- var tunnelSubnetMask: String = "255.255.255.0"
-
- override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) {
- if let deviceIp = options?["TunnelDeviceIP"] as? String {
- tunnelDeviceIp = deviceIp
- }
- if let fakeIp = options?["TunnelFakeIP"] as? String {
- tunnelFakeIp = fakeIp
- }
+ override func startTunnel(options: [String: NSObject]?, completionHandler: @escaping (Error?) -> Void) {
+ let deviceIP = options?["TunnelDeviceIP"] as? String ?? "10.7.0.0"
+ let fakeIP = options?["TunnelFakeIP"] as? String ?? "10.7.0.1"
- let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: tunnelDeviceIp)
- let ipv4 = NEIPv4Settings(addresses: [tunnelDeviceIp], subnetMasks: [tunnelSubnetMask])
- ipv4.includedRoutes = [NEIPv4Route(destinationAddress: tunnelDeviceIp, subnetMask: tunnelSubnetMask)]
- ipv4.excludedRoutes = [.default()]
+ let toNetwork: (String) -> UInt32 = { address in
+ var addr = in_addr()
+ inet_pton(AF_INET, address, &addr)
+ return addr.s_addr.bigEndian
+ }
+ let deviceNet = toNetwork(deviceIP), fakeNet = toNetwork(fakeIP)
+
+ let settings = NEPacketTunnelNetworkSettings(tunnelRemoteAddress: deviceIP)
+ let ipv4 = NEIPv4Settings(addresses: [deviceIP], subnetMasks: ["255.255.255.0"])
+ ipv4.includedRoutes = [NEIPv4Route(destinationAddress: deviceIP, subnetMask: "255.255.255.0")]
+ ipv4.excludedRoutes = [NEIPv4Route.default()]
settings.ipv4Settings = ipv4
+
setTunnelNetworkSettings(settings) { error in
- if error == nil {
- self.readPackets()
+ guard error == nil else { return completionHandler(error) }
+
+ func process() {
+ self.packetFlow.readPackets { packets, protocols in
+ var modified = packets
+ for i in modified.indices where protocols[i].int32Value == AF_INET && modified[i].count >= 20 {
+ modified[i].withUnsafeMutableBytes { buffer in
+ let ptr = buffer.baseAddress!.assumingMemoryBound(to: UInt32.self)
+ let src = ptr[3], dst = ptr[4]
+ ptr[3] = src == deviceNet ? fakeNet : dst
+ ptr[4] = dst == fakeNet ? deviceNet : src
+ }
+ }
+ self.packetFlow.writePackets(modified, withProtocols: protocols)
+ process()
+ }
}
- completionHandler(error)
+ process()
+ completionHandler(nil)
}
}
-
+
override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) {
- // Add code here to start the process of stopping the tunnel.
completionHandler()
}
-
- override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) {
- // Add code here to handle the message.
- if let handler = completionHandler {
- handler(messageData)
- }
- }
-
- override func sleep(completionHandler: @escaping () -> Void) {
- completionHandler()
- }
-
- override func wake() {
- // Add code here to wake up.
- }
-
- private func readPackets() {
- packetFlow.readPackets { packets, protocols in
- var output: [Data] = []
- for (i, packet) in packets.enumerated() {
- var modifiedPacket = packet
- if protocols[i].int32Value == AF_INET {
- modifiedPacket = self.packetReplaceIp(packet, self.tunnelDeviceIp, self.tunnelFakeIp, self.tunnelFakeIp, self.tunnelDeviceIp)
- }
-
- if modifiedPacket.count >= 20 {
- var mutableBytes = [UInt8](modifiedPacket)
-
- // Swap bytes
- (mutableBytes[12], mutableBytes[16]) = (mutableBytes[16], mutableBytes[12])
- (mutableBytes[13], mutableBytes[17]) = (mutableBytes[17], mutableBytes[13])
- (mutableBytes[14], mutableBytes[18]) = (mutableBytes[18], mutableBytes[14])
- (mutableBytes[15], mutableBytes[19]) = (mutableBytes[19], mutableBytes[15])
-
- modifiedPacket = Data(mutableBytes)
- }
-
- output.append(modifiedPacket)
- }
- self.packetFlow.writePackets(output, withProtocols: protocols)
- self.readPackets()
- }
- }
-
-
- private func packetReplaceIp(_ data: Data, _ sourceSearch: String, _ sourceReplace: String, _ destSearch: String, _ destReplace: String) -> Data {
- // Check if packet is too small for IPv4 header
- if data.count < 20 {
- return data
- }
-
- // Convert IP strings to Data with network byte order (big-endian)
- func ipToUInt32(_ ipString: String) -> UInt32 {
- let components = ipString.split(separator: ".")
- var result: UInt32 = 0
-
- if components.count == 4,
- let byte1 = UInt32(components[0]),
- let byte2 = UInt32(components[1]),
- let byte3 = UInt32(components[2]),
- let byte4 = UInt32(components[3]) {
- result = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4
- }
-
- return result
- }
-
- // Convert IP strings to UInt32
- let sourceSearchIP = ipToUInt32(sourceSearch)
- let sourceReplaceIP = ipToUInt32(sourceReplace)
- let destSearchIP = ipToUInt32(destSearch)
- let destReplaceIP = ipToUInt32(destReplace)
-
- // Extract source and destination IPs from packet
- var sourcePacketIP: UInt32 = 0
- var destPacketIP: UInt32 = 0
-
- (data as NSData).getBytes(&sourcePacketIP, range: NSRange(location: 12, length: 4))
- (data as NSData).getBytes(&destPacketIP, range: NSRange(location: 16, length: 4))
-
- if sourceSearchIP != sourcePacketIP && destSearchIP != destPacketIP {
- return data
- }
-
- let mutableData = NSMutableData(data: data)
-
- if sourceSearchIP == sourcePacketIP {
- var sourceIP = sourceReplaceIP
- mutableData.replaceBytes(in: NSRange(location: 12, length: 4), withBytes: &sourceIP)
- }
-
- if destSearchIP == destPacketIP {
- var destIP = destReplaceIP
- mutableData.replaceBytes(in: NSRange(location: 16, length: 4), withBytes: &destIP)
- }
-
- return mutableData as Data
- }
-
- // Helper function to convert IP string to Data
- private func ipToData(_ ip: String) -> Data {
- let components = ip.split(separator: ".")
- var data = Data(capacity: 4)
-
- for component in components {
- if let byte = UInt8(component) {
- data.append(byte)
- }
- }
-
- return data
- }
}
-