mirror of
https://github.com/StikDebug/StikDebug.git
synced 2026-10-16 04:20:30 +00:00
Location Spoofer (Sideload Only)
This commit is contained in:
parent
9f02b608bf
commit
9ff7de2e5c
5 changed files with 447 additions and 0 deletions
|
|
@ -7,3 +7,4 @@
|
|||
#include "idevice/heartbeat.h"
|
||||
#include "JSSupport/JSSupport.h"
|
||||
#include "idevice/ideviceinfo.h"
|
||||
#include "idevice/ls.h"
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
242
StikJIT/Views/MapSelectionView.swift
Normal file
242
StikJIT/Views/MapSelectionView.swift
Normal 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
143
StikJIT/idevice/ls.c
Normal 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
55
StikJIT/idevice/ls.h
Normal 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 */
|
||||
Loading…
Add table
Add a link
Reference in a new issue