Location Spoofer (Sideload Only)

This commit is contained in:
Stephen 2025-08-03 02:16:10 -04:00
parent 9f02b608bf
commit 9ff7de2e5c
5 changed files with 447 additions and 0 deletions

View file

@ -7,3 +7,4 @@
#include "idevice/heartbeat.h"
#include "JSSupport/JSSupport.h"
#include "idevice/ideviceinfo.h"
#include "idevice/ls.h"

View file

@ -38,6 +38,12 @@ struct MainTabView: View {
.tabItem {
Label("Info", systemImage: "info.circle.fill")
}
#if DEBUG
LocationSimulatorView(deviceIp: "10.7.0.2")
.tabItem {
Label("Location", systemImage: "globe.americas")
}
#endif
SettingsView()
.tabItem {
Label("Settings", systemImage: "gearshape.fill")

View file

@ -0,0 +1,242 @@
//
// MapSelectionView.swift
// StikDebug
//
// Created by Stephen on 8/3/25.
//
import SwiftUI
import MapKit
struct MapSelectionView: UIViewRepresentable {
@Binding var coordinate: CLLocationCoordinate2D?
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, MKMapViewDelegate {
var parent: MapSelectionView
init(_ parent: MapSelectionView) {
self.parent = parent
}
@objc func handleLongPress(_ gesture: UILongPressGestureRecognizer) {
guard gesture.state == .began,
let map = gesture.view as? MKMapView
else { return }
let point = gesture.location(in: map)
parent.coordinate = map.convert(point, toCoordinateFrom: map)
}
}
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView(frame: .zero)
mapView.delegate = context.coordinator
let longPress = UILongPressGestureRecognizer(
target: context.coordinator,
action: #selector(Coordinator.handleLongPress(_:))
)
mapView.addGestureRecognizer(longPress)
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
uiView.removeAnnotations(uiView.annotations)
if let coord = coordinate {
let annotation = MKPointAnnotation()
annotation.coordinate = coord
uiView.addAnnotation(annotation)
let region = MKCoordinateRegion(
center: coord,
latitudinalMeters: 500,
longitudinalMeters: 500
)
uiView.setRegion(region, animated: true)
}
}
}
struct LocationSimulatorView: View {
@State private var coordinate: CLLocationCoordinate2D?
@State private var status = ""
@State private var showKeepOpenError = false
@State private var searchQuery = ""
@State private var searchResults: [MKMapItem] = []
let deviceIp: String
private var pairingFilePath: String {
FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)[0]
.appendingPathComponent("pairingFile.plist")
.path
}
var body: some View {
VStack(spacing: 16) {
Text("Location Simulator")
.font(.largeTitle.weight(.bold))
.padding(.top)
HStack {
Image(systemName: "magnifyingglass")
.foregroundColor(.secondary)
TextField("Search for a place", text: $searchQuery, onCommit: performSearch)
.font(.body)
.autocapitalization(.none)
.disableAutocorrection(true)
}
.padding(12)
.background(Color(UIColor.secondarySystemBackground))
.cornerRadius(12)
.shadow(color: .black.opacity(0.05), radius: 2, x: 0, y: 1)
.padding(.horizontal)
if !searchResults.isEmpty {
ScrollView {
VStack(spacing: 0) {
ForEach(searchResults, id: \.self) { item in
Button(action: { select(item: item) }) {
HStack {
VStack(alignment: .leading, spacing: 2) {
Text(item.name ?? "Unknown")
.font(.body)
Text(item.placemark.title ?? "")
.font(.caption)
.foregroundColor(.secondary)
}
Spacer()
}
.padding(.vertical, 8)
.padding(.horizontal)
}
Divider()
}
}
.background(RoundedRectangle(cornerRadius: 12)
.fill(Color(UIColor.systemBackground)))
.shadow(color: .black.opacity(0.05), radius: 2, x: 0, y: 1)
.padding(.horizontal)
}
.frame(maxHeight: 200)
}
MapSelectionView(coordinate: $coordinate)
.frame(height: 360)
.cornerRadius(12)
.shadow(color: .black.opacity(0.1), radius: 4, x: 0, y: 2)
.padding(.horizontal)
if let coord = coordinate {
VStack(alignment: .leading, spacing: 4) {
Text("Selected Coordinates")
.font(.headline)
Text(String(format: "%.6f, %.6f", coord.latitude, coord.longitude))
.font(.subheadline)
.foregroundColor(.secondary)
}
.padding()
.frame(maxWidth: .infinity)
.background(RoundedRectangle(cornerRadius: 12)
.fill(Color(UIColor.secondarySystemBackground)))
.shadow(color: .black.opacity(0.05), radius: 2, x: 0, y: 1)
.padding(.horizontal)
} else {
Text("Long-press on the map or search above to pick a location.")
.font(.subheadline)
.foregroundColor(.secondary)
.padding(.horizontal)
}
HStack(spacing: 12) {
Button(action: simulate) {
Label("Simulate", systemImage: "location.fill")
.frame(maxWidth: .infinity)
}
.buttonStyle(.borderedProminent)
.disabled(coordinate == nil)
Button(action: clear) {
Label("Clear", systemImage: "xmark.circle.fill")
.frame(maxWidth: .infinity)
}
.buttonStyle(.bordered)
.disabled(coordinate == nil)
}
.padding(.horizontal)
if !status.isEmpty {
Text(status)
.font(.callout)
.foregroundColor(status.contains("failed") ? .red : .green)
.padding(.horizontal)
.transition(.opacity)
}
Spacer()
}
.padding(.bottom)
.overlay(overlayErrorView)
}
private func performSearch() {
guard !searchQuery.isEmpty else { return }
let request = MKLocalSearch.Request()
request.naturalLanguageQuery = searchQuery
MKLocalSearch(request: request).start { response, _ in
searchResults = response?.mapItems ?? []
}
}
private func select(item: MKMapItem) {
coordinate = item.placemark.coordinate
status = ""
searchResults = []
searchQuery = item.name ?? ""
}
private func simulate() {
guard let coord = coordinate else { return }
let code = simulate_location(
deviceIp,
coord.latitude,
coord.longitude,
pairingFilePath
)
if code == 0 {
status = "Simulation running…"
showKeepOpenError = true
} else {
status = "Simulation failed (code \(code))."
}
}
private func clear() {
let code = clear_simulated_location()
status = code == 0 ? "Cleared simulation." : "Clear failed (code \(code))."
showKeepOpenError = false
}
@ViewBuilder
private var overlayErrorView: some View {
if showKeepOpenError {
CustomErrorView(
title: "Keep App Open",
message: "Your simulation will stop if the app goes to the background. Please keep the app open to continue.",
onDismiss: { showKeepOpenError = false },
primaryButtonText: "OK",
showSecondaryButton: false,
messageType: .info
)
.transition(.opacity.combined(with: .scale))
.zIndex(1)
}
}
}

143
StikJIT/idevice/ls.c Normal file
View file

@ -0,0 +1,143 @@
//
// ls.c
// StikDebug
//
// Created by Stephen on 8/3/25.
//
#include "ls.h"
#include "idevice.h"
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
static IdevicePairingFile *g_pairing = NULL;
static IdeviceProviderHandle *g_provider = NULL;
static CoreDeviceProxyHandle *g_core_device = NULL;
static AdapterHandle *g_adapter = NULL;
static RsdHandshakeHandle *g_handshake = NULL;
static RemoteServerHandle *g_remote_server = NULL;
static LocationSimulationHandle *g_location_sim = NULL;
static void cleanup_on_error(void) {
if (g_location_sim) { location_simulation_free(g_location_sim); g_location_sim = NULL; }
if (g_remote_server) { remote_server_free(g_remote_server); g_remote_server = NULL; }
if (g_handshake) { rsd_handshake_free(g_handshake); g_handshake = NULL; }
if (g_adapter) { adapter_free(g_adapter); g_adapter = NULL; }
if (g_core_device) { core_device_proxy_free(g_core_device); g_core_device = NULL; }
if (g_provider) { idevice_provider_free(g_provider); g_provider = NULL; }
if (g_pairing) { idevice_pairing_file_free(g_pairing); g_pairing = NULL; }
}
int simulate_location(const char *device_ip,
double latitude,
double longitude,
const char *pairing_file)
{
idevice_init_logger(Debug, Disabled, NULL);
IdeviceFfiError *err = NULL;
struct sockaddr_in addr = { .sin_family = AF_INET,
.sin_port = htons(LOCKDOWN_PORT) };
if (inet_pton(AF_INET, device_ip, &addr.sin_addr) != 1) {
return IPA_ERR_INVALID_IP;
}
if ((err = idevice_pairing_file_read(pairing_file, &g_pairing))) {
idevice_error_free(err);
return IPA_ERR_PAIRING_READ;
}
if ((err = idevice_tcp_provider_new((struct sockaddr *)&addr,
g_pairing,
"LocationSimCLI",
&g_provider)))
{
idevice_error_free(err);
cleanup_on_error();
return IPA_ERR_PROVIDER_CREATE;
}
if ((err = core_device_proxy_connect(g_provider, &g_core_device))) {
idevice_error_free(err);
cleanup_on_error();
return IPA_ERR_CORE_DEVICE;
}
idevice_provider_free(g_provider);
g_provider = NULL;
uint16_t rsd_port;
if ((err = core_device_proxy_get_server_rsd_port(g_core_device,
&rsd_port)))
{
idevice_error_free(err);
cleanup_on_error();
return IPA_ERR_RSD_PORT;
}
if ((err = core_device_proxy_create_tcp_adapter(g_core_device,
&g_adapter)))
{
idevice_error_free(err);
cleanup_on_error();
return IPA_ERR_ADAPTER_CREATE;
}
AdapterStreamHandle *stream = NULL;
if ((err = adapter_connect(g_adapter, rsd_port, &stream))) {
idevice_error_free(err);
cleanup_on_error();
return IPA_ERR_STREAM;
}
if ((err = rsd_handshake_new(stream, &g_handshake))) {
idevice_error_free(err);
adapter_close(stream);
cleanup_on_error();
return IPA_ERR_HANDSHAKE;
}
if ((err = remote_server_connect_rsd(g_adapter,
g_handshake,
&g_remote_server)))
{
idevice_error_free(err);
cleanup_on_error();
return IPA_ERR_REMOTE_SERVER;
}
if ((err = location_simulation_new(g_remote_server,
&g_location_sim))) {
idevice_error_free(err);
cleanup_on_error();
return IPA_ERR_LOCATION_SIM;
}
if ((err = location_simulation_set(g_location_sim,
latitude,
longitude))) {
idevice_error_free(err);
cleanup_on_error();
return IPA_ERR_LOCATION_SET;
}
return IPA_OK;
}
int clear_simulated_location(void)
{
IdeviceFfiError *err = NULL;
if (!g_location_sim) return IPA_ERR_LOCATION_CLEAR;
err = location_simulation_clear(g_location_sim);
location_simulation_free(g_location_sim);
g_location_sim = NULL;
if (g_remote_server) { remote_server_free(g_remote_server); g_remote_server = NULL; }
if (g_handshake) { rsd_handshake_free(g_handshake); g_handshake = NULL; }
if (g_adapter) { adapter_free(g_adapter); g_adapter = NULL; }
return err ? IPA_ERR_LOCATION_CLEAR : IPA_OK;
}

55
StikJIT/idevice/ls.h Normal file
View file

@ -0,0 +1,55 @@
//
// ls.h
// StikDebug
//
// Created by Stephen on 8/3/25.
//
#ifndef LS_H
#define LS_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
// Success / error codes
#define IPA_OK 0
#define IPA_ERR_INVALID_IP 1
#define IPA_ERR_PAIRING_READ 2
#define IPA_ERR_PROVIDER_CREATE 3
#define IPA_ERR_CORE_DEVICE 4
#define IPA_ERR_RSD_PORT 5
#define IPA_ERR_ADAPTER_CREATE 6
#define IPA_ERR_STREAM 7
#define IPA_ERR_HANDSHAKE 8
#define IPA_ERR_REMOTE_SERVER 9
#define IPA_ERR_LOCATION_SIM 10
#define IPA_ERR_LOCATION_SET 11
#define IPA_ERR_LOCATION_CLEAR 12
/**
* Simulate the device’s location.
* @param device_ip IP address of the device.
* @param latitude Latitude to simulate.
* @param longitude Longitude to simulate.
* @param pairing_file Path to the pairing file.
* @return IPA_OK (0) on success, or one of the IPA_ERR_* codes.
*/
int simulate_location(const char *device_ip,
double latitude,
double longitude,
const char *pairing_file);
/**
* Clear any simulated location.
* @return IPA_OK (0) on success, or IPA_ERR_LOCATION_CLEAR.
*/
int clear_simulated_location(void);
#ifdef __cplusplus
}
#endif
#endif /* LS_H */