@leejungkiin/awkit 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/VERSION +1 -1
- package/bin/awk.js +1 -1
- package/core/GEMINI.md +4 -0
- package/package.json +2 -2
- package/skills/smali-to-kotlin/SKILL.md +521 -0
- package/skills/smali-to-kotlin/library-patterns.md +189 -0
- package/skills/smali-to-kotlin/smali-reading-guide.md +310 -0
- package/skills/smali-to-swift/SKILL.md +749 -0
- package/skills/smali-to-swift/framework-patterns.md +189 -0
- package/skills/smali-to-swift/objc-reading-guide.md +388 -0
- package/workflows/mobile/reverse-android.md +740 -0
- package/workflows/mobile/reverse-ios.md +674 -0
|
@@ -0,0 +1,749 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: smali-to-swift
|
|
3
|
+
description: >-
|
|
4
|
+
iOS Reverse Engineering specialist. Reads decrypted IPA output (class-dump headers,
|
|
5
|
+
Hopper/IDA disassembly, resources, Info.plist) and rebuilds the app from scratch using
|
|
6
|
+
modern Swift + SwiftUI + Clean Architecture (MVVM).
|
|
7
|
+
Includes framework detection to reuse existing dependencies.
|
|
8
|
+
author: Antigravity Team
|
|
9
|
+
version: 1.0.0
|
|
10
|
+
trigger: conditional
|
|
11
|
+
activation_keywords:
|
|
12
|
+
- "/reverse-ios"
|
|
13
|
+
- "ipa"
|
|
14
|
+
- "class-dump"
|
|
15
|
+
- "hopper"
|
|
16
|
+
- "reverse engineer ios"
|
|
17
|
+
- "dịch ngược ios"
|
|
18
|
+
- "tái tạo ipa"
|
|
19
|
+
- "rebuild ipa"
|
|
20
|
+
- "objective-c to swift"
|
|
21
|
+
- "objc to swift"
|
|
22
|
+
priority: high
|
|
23
|
+
platform: ios
|
|
24
|
+
sibling_skill: smali-to-kotlin (Android counterpart)
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
# 🍎 Smali-to-Swift Skill
|
|
28
|
+
|
|
29
|
+
> **Purpose:** Transform decrypted iOS IPA (class-dump headers, disassembly, resources, plist) into a modern Swift app with SwiftUI, Clean Architecture, and MVVM.
|
|
30
|
+
> **Philosophy:** "Read ObjC headers to understand WHAT and WHY → Write Swift for HOW."
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## ⚠️ SCOPE CLARITY
|
|
35
|
+
|
|
36
|
+
| This skill DOES | This skill DOES NOT |
|
|
37
|
+
|-----------------|---------------------|
|
|
38
|
+
| Read & analyze ObjC headers, ARM disassembly | Write Objective-C code |
|
|
39
|
+
| Rebuild logic in modern Swift + SwiftUI | Modify original IPA |
|
|
40
|
+
| Detect & reuse third-party frameworks | Crack/bypass DRM or jailbreak |
|
|
41
|
+
| Extract only needed resources (on-demand) | Mass-copy assets blindly |
|
|
42
|
+
| Set up Clean Architecture project structure | Handle Android reverse engineering |
|
|
43
|
+
| Scan IPA frameworks for dependency reuse | Submit to App Store |
|
|
44
|
+
|
|
45
|
+
→ For Android reverse engineering → sibling skill: `smali-to-kotlin`
|
|
46
|
+
→ After rebuild complete → use `/test` or `/deploy` workflows
|
|
47
|
+
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
## 🎯 ROLE DEFINITION
|
|
51
|
+
|
|
52
|
+
When this skill is active, the agent becomes:
|
|
53
|
+
|
|
54
|
+
> **Expert iOS Reverse Engineer & Swift Architect**
|
|
55
|
+
> - Master at reading ObjC/Swift class-dump headers and ARM disassembly
|
|
56
|
+
> - Fluent in Clean Architecture + MVVM + SwiftUI
|
|
57
|
+
> - Knows when to reuse vs rewrite third-party frameworks
|
|
58
|
+
> - Enforces resource-on-demand principle (zero bloat)
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
## 🧰 iOS RE TOOLCHAIN
|
|
63
|
+
|
|
64
|
+
### Decryption & Extraction
|
|
65
|
+
| Tool | Purpose | Output |
|
|
66
|
+
|------|---------|--------|
|
|
67
|
+
| **frida-ios-dump** / **bagbak** | Decrypt IPA from jailbroken device | Decrypted .app bundle |
|
|
68
|
+
| **class-dump** / **class-dump-swift** | Extract ObjC/Swift headers | `.h` header files |
|
|
69
|
+
| **Hopper Disassembler** | ARM disassembly + pseudo-code | Pseudo-C / ARM ASM |
|
|
70
|
+
| **IDA Pro** | Advanced disassembly | Pseudo-C / ARM ASM |
|
|
71
|
+
| **jtool2** | Mach-O analysis, entitlements | Entitlements plist, segments |
|
|
72
|
+
| **plutil / plistutil** | Read binary plists | Readable plist XML |
|
|
73
|
+
|
|
74
|
+
### What we get from an IPA:
|
|
75
|
+
```
|
|
76
|
+
Payload/App.app/
|
|
77
|
+
├── App # Mach-O binary (encrypted → need decrypt first)
|
|
78
|
+
├── Info.plist # App metadata (bundle ID, permissions, URL schemes)
|
|
79
|
+
├── Frameworks/ # Embedded frameworks (.framework / .dylib)
|
|
80
|
+
│ ├── SomeSDK.framework/
|
|
81
|
+
│ └── libswiftCore.dylib
|
|
82
|
+
├── Assets.car # Compiled asset catalog
|
|
83
|
+
├── Base.lproj/ # Storyboards / XIBs (compiled)
|
|
84
|
+
│ ├── Main.storyboardc/
|
|
85
|
+
│ └── LaunchScreen.storyboardc/
|
|
86
|
+
├── *.nib # Compiled XIB files
|
|
87
|
+
├── *.momd # Core Data models (compiled)
|
|
88
|
+
├── embedded.mobileprovision # Provisioning profile
|
|
89
|
+
├── _CodeSignature/ # Code signing
|
|
90
|
+
└── [other resources: json, png, html, js, fonts...]
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## 🏗️ MODERN TECH STACK (Mandatory)
|
|
96
|
+
|
|
97
|
+
### Core
|
|
98
|
+
| Layer | Technology | Replaces |
|
|
99
|
+
|-------|-----------|----------|
|
|
100
|
+
| **UI** | SwiftUI + iOS 17+ | UIKit Storyboards / XIBs |
|
|
101
|
+
| **State** | `@Observable` (Observation framework) | `@ObservableObject` / KVO |
|
|
102
|
+
| **Navigation** | NavigationStack + NavigationPath | UINavigationController / segues |
|
|
103
|
+
| **DI** | Swift DI (protocol + init injection) | Singletons / Service Locators |
|
|
104
|
+
|
|
105
|
+
### Data Layer
|
|
106
|
+
| Purpose | Technology | Replaces |
|
|
107
|
+
|---------|-----------|----------|
|
|
108
|
+
| **Network** | URLSession + async/await | AFNetworking / Alamofire (evaluate) |
|
|
109
|
+
| **JSON** | Codable (Swift built-in) | NSJSONSerialization / Mantle / ObjectMapper |
|
|
110
|
+
| **Local DB** | SwiftData (iOS 17+) or Core Data | Raw SQLite / FMDB / Realm |
|
|
111
|
+
| **Preferences** | UserDefaults / @AppStorage | NSUserDefaults direct |
|
|
112
|
+
| **Keychain** | KeychainAccess (or custom wrapper) | Raw Security.framework |
|
|
113
|
+
| **Image Loading** | AsyncImage / Kingfisher / Nuke | SDWebImage (evaluate) |
|
|
114
|
+
| **Async** | Swift Concurrency (async/await, actors) | GCD / NSOperation / PromiseKit |
|
|
115
|
+
|
|
116
|
+
### Observability
|
|
117
|
+
| Purpose | Technology |
|
|
118
|
+
|---------|-----------|
|
|
119
|
+
| **Crash** | Firebase Crashlytics |
|
|
120
|
+
| **Analytics** | Firebase Analytics |
|
|
121
|
+
| **Logging** | OSLog / swift-log |
|
|
122
|
+
|
|
123
|
+
### Replacements Table (Legacy → Modern)
|
|
124
|
+
|
|
125
|
+
```yaml
|
|
126
|
+
always_replace:
|
|
127
|
+
NSURLConnection: "URLSession async/await"
|
|
128
|
+
AFNetworking: "URLSession async/await"
|
|
129
|
+
NSJSONSerialization: "Codable / JSONDecoder"
|
|
130
|
+
Mantle/ObjectMapper: "Codable"
|
|
131
|
+
GCD_dispatch_async: "Task { } / async-await"
|
|
132
|
+
NSOperation: "TaskGroup / async let"
|
|
133
|
+
NSTimer: "Timer.publish (Combine) or Task.sleep"
|
|
134
|
+
UIAlertView: "SwiftUI .alert modifier"
|
|
135
|
+
UIActionSheet: "SwiftUI .confirmationDialog"
|
|
136
|
+
UITableView: "List / LazyVStack"
|
|
137
|
+
UICollectionView: "LazyVGrid / LazyHGrid"
|
|
138
|
+
UIPageViewController: "TabView with .page style"
|
|
139
|
+
Storyboard_segues: "NavigationStack + NavigationLink"
|
|
140
|
+
NSNotificationCenter_addObserver: "NotificationCenter.notifications (AsyncSequence)"
|
|
141
|
+
KVO: "@Observable macro"
|
|
142
|
+
Delegate_patterns: "AsyncStream or closures"
|
|
143
|
+
Target_Action: "SwiftUI action closures"
|
|
144
|
+
|
|
145
|
+
evaluate_before_replacing:
|
|
146
|
+
Alamofire: "Keep if deeply used for interceptors/retry, otherwise → URLSession"
|
|
147
|
+
SDWebImage: "Replace with AsyncImage + Kingfisher"
|
|
148
|
+
Realm: "Migrate to SwiftData (if iOS 17+)"
|
|
149
|
+
RxSwift: "Migrate to async/await + AsyncSequence (gradual)"
|
|
150
|
+
SnapKit: "Replace with SwiftUI layout"
|
|
151
|
+
Masonry: "Replace with SwiftUI layout"
|
|
152
|
+
MBProgressHUD: "SwiftUI .overlay + ProgressView"
|
|
153
|
+
SVProgressHUD: "SwiftUI .overlay + ProgressView"
|
|
154
|
+
IQKeyboardManager: "SwiftUI handles keyboard automatically"
|
|
155
|
+
|
|
156
|
+
keep_as_is:
|
|
157
|
+
- "Firebase SDKs (use latest via SPM)"
|
|
158
|
+
- "Google Sign-In"
|
|
159
|
+
- "Facebook SDK (latest)"
|
|
160
|
+
- "Apple frameworks (MapKit, CoreLocation, AVFoundation, etc.)"
|
|
161
|
+
- "Native C/C++ libraries (.dylib)"
|
|
162
|
+
- "CryptoKit (Apple native)"
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
## 📋 EXECUTION PIPELINE (6 Steps)
|
|
168
|
+
|
|
169
|
+
> **Rule:** Always complete one step fully before moving to the next.
|
|
170
|
+
> **Rule:** After each step, create a checkpoint summary for the user.
|
|
171
|
+
|
|
172
|
+
### Step 0: Framework Scanner (PRE-STEP — Always First) 🔍
|
|
173
|
+
|
|
174
|
+
**Purpose:** Scan the IPA structure to identify all third-party frameworks before any coding.
|
|
175
|
+
|
|
176
|
+
**Process:**
|
|
177
|
+
1. **Scan `Frameworks/` directory:**
|
|
178
|
+
```
|
|
179
|
+
Frameworks/Alamofire.framework → Alamofire (network)
|
|
180
|
+
Frameworks/SDWebImage.framework → SDWebImage (image loading)
|
|
181
|
+
Frameworks/Realm.framework → Realm (database)
|
|
182
|
+
Frameworks/FBSDKCoreKit.framework → Facebook SDK
|
|
183
|
+
Frameworks/GoogleSignIn.framework → Google Sign-In
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
2. **Scan class-dump headers for imports:**
|
|
187
|
+
```
|
|
188
|
+
#import <AFNetworking/...> → AFNetworking
|
|
189
|
+
#import <Masonry/...> → Masonry (auto-layout)
|
|
190
|
+
#import <MBProgressHUD/...> → MBProgressHUD
|
|
191
|
+
@import Firebase; → Firebase SDK
|
|
192
|
+
@import GoogleMobileAds; → AdMob
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
3. **Check Mach-O linked frameworks:**
|
|
196
|
+
```bash
|
|
197
|
+
otool -L Payload/App.app/App | grep -v /System | grep -v /usr/lib
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
4. **Check embedded dylibs:**
|
|
201
|
+
```bash
|
|
202
|
+
find Payload/App.app -name "*.dylib" -o -name "*.framework" | sort
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
5. **Check for CocoaPods / SPM markers:**
|
|
206
|
+
```
|
|
207
|
+
Pods/ directory presence → was using CocoaPods
|
|
208
|
+
.package.resolved → was using SPM
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
6. **Output: Framework Detection Report**
|
|
212
|
+
```markdown
|
|
213
|
+
## 📦 Framework Detection Report
|
|
214
|
+
|
|
215
|
+
### ✅ Can Reuse (add to Package.swift / Podfile)
|
|
216
|
+
| Framework | Detected | Latest Version | Action |
|
|
217
|
+
|-----------|----------|----------------|--------|
|
|
218
|
+
| Alamofire | Frameworks/Alamofire.framework | 5.9.0 | Evaluate |
|
|
219
|
+
| Kingfisher | (header import) | 7.12.0 | Add via SPM |
|
|
220
|
+
|
|
221
|
+
### 🔄 Must Replace (legacy)
|
|
222
|
+
| Old Framework | Detected | Modern Replacement |
|
|
223
|
+
|---------------|----------|-------------------|
|
|
224
|
+
| AFNetworking | Frameworks/AFNetworking.framework | URLSession async/await |
|
|
225
|
+
| Masonry | header imports | SwiftUI layout |
|
|
226
|
+
|
|
227
|
+
### 🍏 Apple Frameworks Used
|
|
228
|
+
| Framework | Purpose |
|
|
229
|
+
|-----------|---------|
|
|
230
|
+
| MapKit | Maps |
|
|
231
|
+
| CoreLocation | GPS |
|
|
232
|
+
| AVFoundation | Camera/Audio |
|
|
233
|
+
|
|
234
|
+
### 📱 Native (.dylib) — Investigate
|
|
235
|
+
| File | Notes |
|
|
236
|
+
|------|-------|
|
|
237
|
+
| libcrypto.dylib | Custom crypto? |
|
|
238
|
+
|
|
239
|
+
### ❓ Unknown (investigate)
|
|
240
|
+
| Framework/Import | Notes |
|
|
241
|
+
|------------------|-------|
|
|
242
|
+
| CustomSDK.framework | Proprietary? |
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
### Step 1: Info.plist & Entitlements Analysis + Project Bootstrap 📄
|
|
248
|
+
|
|
249
|
+
**Input:** User provides `Info.plist` + entitlements from IPA.
|
|
250
|
+
|
|
251
|
+
**Tasks:**
|
|
252
|
+
1. Extract Bundle ID and display name
|
|
253
|
+
2. List required permissions (Privacy keys):
|
|
254
|
+
```
|
|
255
|
+
NSCameraUsageDescription → Camera
|
|
256
|
+
NSPhotoLibraryUsageDescription → Photo Library
|
|
257
|
+
NSLocationWhenInUseUsageDescription → Location
|
|
258
|
+
NSMicrophoneUsageDescription → Microphone
|
|
259
|
+
```
|
|
260
|
+
3. Identify URL Schemes (deep links)
|
|
261
|
+
4. Check app capabilities from entitlements:
|
|
262
|
+
```
|
|
263
|
+
com.apple.developer.associated-domains → Universal Links
|
|
264
|
+
aps-environment → Push Notifications
|
|
265
|
+
com.apple.developer.in-app-payments → Apple Pay
|
|
266
|
+
```
|
|
267
|
+
5. Analyze class-dump headers for entry points:
|
|
268
|
+
- `AppDelegate` → lifecycle logic
|
|
269
|
+
- Root ViewController → initial screen
|
|
270
|
+
- Tab bar / Navigation structure
|
|
271
|
+
6. **Output:** Propose Clean Architecture project structure
|
|
272
|
+
|
|
273
|
+
**Project Structure Template:**
|
|
274
|
+
```
|
|
275
|
+
App/
|
|
276
|
+
├── App.swift # @main entry point
|
|
277
|
+
├── AppDelegate.swift # UIKit lifecycle (if needed for SDKs)
|
|
278
|
+
├── Info.plist
|
|
279
|
+
├── Assets.xcassets/
|
|
280
|
+
├── DI/ # Dependency Injection
|
|
281
|
+
│ └── AppContainer.swift
|
|
282
|
+
├── Data/ # Data Layer
|
|
283
|
+
│ ├── Network/
|
|
284
|
+
│ │ ├── APIClient.swift # URLSession wrapper
|
|
285
|
+
│ │ ├── Endpoints/ # API endpoint definitions
|
|
286
|
+
│ │ └── DTOs/ # Codable response models
|
|
287
|
+
│ ├── Local/
|
|
288
|
+
│ │ ├── SwiftDataModels/ # @Model classes
|
|
289
|
+
│ │ ├── KeychainService.swift
|
|
290
|
+
│ │ └── UserDefaultsKeys.swift
|
|
291
|
+
│ └── Repositories/ # Repository implementations
|
|
292
|
+
├── Domain/ # Domain Layer
|
|
293
|
+
│ ├── Models/ # Business models
|
|
294
|
+
│ ├── Repositories/ # Repository protocols
|
|
295
|
+
│ └── UseCases/
|
|
296
|
+
├── Presentation/ # Presentation Layer
|
|
297
|
+
│ ├── Navigation/
|
|
298
|
+
│ │ ├── AppNavigation.swift # NavigationStack + routes
|
|
299
|
+
│ │ └── Route.swift # Deep link routes
|
|
300
|
+
│ ├── Theme/
|
|
301
|
+
│ │ ├── AppTheme.swift # Colors, fonts, spacing
|
|
302
|
+
│ │ └── Components/ # Reusable SwiftUI components
|
|
303
|
+
│ └── Screens/
|
|
304
|
+
│ ├── Launch/
|
|
305
|
+
│ │ └── LaunchScreen.swift
|
|
306
|
+
│ ├── Auth/
|
|
307
|
+
│ │ ├── LoginScreen.swift
|
|
308
|
+
│ │ └── LoginViewModel.swift
|
|
309
|
+
│ ├── Home/
|
|
310
|
+
│ │ ├── HomeScreen.swift
|
|
311
|
+
│ │ └── HomeViewModel.swift
|
|
312
|
+
│ └── ...
|
|
313
|
+
├── Utilities/
|
|
314
|
+
│ ├── Extensions/
|
|
315
|
+
│ ├── Crypto/ # Encryption/hashing utils
|
|
316
|
+
│ └── Helpers/
|
|
317
|
+
└── Resources/
|
|
318
|
+
├── Localizable.xcstrings
|
|
319
|
+
└── Fonts/
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
---
|
|
323
|
+
|
|
324
|
+
### Step 2: Data Layer Reconstruction 💾
|
|
325
|
+
|
|
326
|
+
**Input:** User provides class-dump headers + Hopper pseudo-code for network/data classes.
|
|
327
|
+
|
|
328
|
+
**Tasks:**
|
|
329
|
+
1. **Models:** Convert ObjC interfaces → Swift structs
|
|
330
|
+
```objc
|
|
331
|
+
// ObjC header (class-dump)
|
|
332
|
+
@interface UserModel : NSObject
|
|
333
|
+
@property (nonatomic, copy) NSString *userId;
|
|
334
|
+
@property (nonatomic, copy) NSString *fullName;
|
|
335
|
+
@property (nonatomic, assign) NSInteger age;
|
|
336
|
+
@end
|
|
337
|
+
```
|
|
338
|
+
```swift
|
|
339
|
+
// Swift
|
|
340
|
+
struct User: Codable, Identifiable {
|
|
341
|
+
let id: String
|
|
342
|
+
let fullName: String
|
|
343
|
+
let age: Int
|
|
344
|
+
|
|
345
|
+
enum CodingKeys: String, CodingKey {
|
|
346
|
+
case id = "user_id"
|
|
347
|
+
case fullName = "full_name"
|
|
348
|
+
case age
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
2. **API Layer:**
|
|
354
|
+
- Extract base URL, endpoints, headers from disassembly
|
|
355
|
+
- Create async URLSession-based API client:
|
|
356
|
+
```swift
|
|
357
|
+
actor APIClient {
|
|
358
|
+
private let session: URLSession
|
|
359
|
+
private let baseURL: URL
|
|
360
|
+
|
|
361
|
+
func request<T: Decodable>(_ endpoint: Endpoint) async throws -> T {
|
|
362
|
+
let (data, response) = try await session.data(for: endpoint.urlRequest(baseURL: baseURL))
|
|
363
|
+
guard let httpResponse = response as? HTTPURLResponse,
|
|
364
|
+
(200...299).contains(httpResponse.statusCode) else {
|
|
365
|
+
throw APIError.invalidResponse
|
|
366
|
+
}
|
|
367
|
+
return try JSONDecoder().decode(T.self, from: data)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
3. **Local Storage:**
|
|
373
|
+
- CoreData models → SwiftData `@Model` classes
|
|
374
|
+
- NSUserDefaults keys → `@AppStorage` or typed UserDefaults wrapper
|
|
375
|
+
- Keychain items → KeychainAccess wrapper
|
|
376
|
+
|
|
377
|
+
4. **Repository:**
|
|
378
|
+
```swift
|
|
379
|
+
// Domain layer - protocol
|
|
380
|
+
protocol UserRepository: Sendable {
|
|
381
|
+
func getUser(id: String) async throws -> User
|
|
382
|
+
func login(email: String, password: String) async throws -> AuthToken
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// Data layer - implementation
|
|
386
|
+
final class UserRepositoryImpl: UserRepository {
|
|
387
|
+
private let apiClient: APIClient
|
|
388
|
+
private let modelContext: ModelContext
|
|
389
|
+
|
|
390
|
+
func getUser(id: String) async throws -> User {
|
|
391
|
+
// offline-first: check local → fetch remote → cache
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
---
|
|
397
|
+
|
|
398
|
+
### Step 3: Core Logic & Utils Reconstruction 🧮
|
|
399
|
+
|
|
400
|
+
**Input:** Disassembly/pseudo-code for encryption, hashing, custom utils.
|
|
401
|
+
|
|
402
|
+
**Tasks:**
|
|
403
|
+
1. Translate ObjC/C crypto logic → Swift
|
|
404
|
+
- Use `CryptoKit` for modern crypto (SHA256, AES-GCM, HMAC)
|
|
405
|
+
- Use `CommonCrypto` for legacy-compatible (MD5, AES-CBC)
|
|
406
|
+
- Preserve exact input/output signatures
|
|
407
|
+
|
|
408
|
+
2. Common ObjC → Swift crypto patterns:
|
|
409
|
+
```objc
|
|
410
|
+
// ObjC (class-dump + disassembly)
|
|
411
|
+
+ (NSString *)md5Hash:(NSString *)input;
|
|
412
|
+
+ (NSData *)aesEncrypt:(NSData *)data withKey:(NSString *)key;
|
|
413
|
+
```
|
|
414
|
+
```swift
|
|
415
|
+
// Swift
|
|
416
|
+
import CryptoKit
|
|
417
|
+
import CommonCrypto
|
|
418
|
+
|
|
419
|
+
enum CryptoUtils {
|
|
420
|
+
static func md5Hash(_ input: String) -> String {
|
|
421
|
+
let data = Data(input.utf8)
|
|
422
|
+
var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
|
|
423
|
+
data.withUnsafeBytes { CC_MD5($0.baseAddress, CC_LONG(data.count), &digest) }
|
|
424
|
+
return digest.map { String(format: "%02x", $0) }.joined()
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
static func aesEncrypt(data: Data, key: String) throws -> Data {
|
|
428
|
+
// Implement matching original algorithm exactly
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
3. **Verification:** XCTest unit tests with known input/output pairs
|
|
434
|
+
|
|
435
|
+
**Critical Rule:**
|
|
436
|
+
> ⚠️ Crypto/hash functions MUST produce identical output to the original app.
|
|
437
|
+
> Test with known pairs captured from the running original app.
|
|
438
|
+
|
|
439
|
+
---
|
|
440
|
+
|
|
441
|
+
### Step 4: UI & ViewModel Reconstruction (Per Screen) 🎨
|
|
442
|
+
|
|
443
|
+
**Input:** Storyboard analysis + class-dump headers for ViewControllers.
|
|
444
|
+
|
|
445
|
+
**Tasks:**
|
|
446
|
+
1. **Resource Extraction (On-Demand):**
|
|
447
|
+
- Extract only images, colors, strings for current screen
|
|
448
|
+
- Use Asset Catalog for organized resources
|
|
449
|
+
- Map ObjC string tables to `Localizable.xcstrings`
|
|
450
|
+
|
|
451
|
+
2. **UIKit → SwiftUI Migration:**
|
|
452
|
+
```
|
|
453
|
+
UIViewController → SwiftUI View struct
|
|
454
|
+
UINavigationController → NavigationStack
|
|
455
|
+
UITabBarController → TabView
|
|
456
|
+
UITableView → List / LazyVStack
|
|
457
|
+
UICollectionView → LazyVGrid / LazyHGrid
|
|
458
|
+
UIScrollView → ScrollView
|
|
459
|
+
UIImageView → AsyncImage / Image
|
|
460
|
+
UILabel → Text
|
|
461
|
+
UITextField → TextField
|
|
462
|
+
UIButton → Button
|
|
463
|
+
UIActivityIndicatorView → ProgressView
|
|
464
|
+
UIAlertController → .alert / .confirmationDialog modifier
|
|
465
|
+
UIPageViewController → TabView(.page)
|
|
466
|
+
UIStackView → VStack / HStack
|
|
467
|
+
MKMapView → Map (MapKit SwiftUI)
|
|
468
|
+
WKWebView → WebView (custom wrapper)
|
|
469
|
+
UIRefreshControl → .refreshable modifier
|
|
470
|
+
UISearchController → .searchable modifier
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
3. **ViewModel Creation:**
|
|
474
|
+
```swift
|
|
475
|
+
@Observable
|
|
476
|
+
final class LoginViewModel {
|
|
477
|
+
var email = ""
|
|
478
|
+
var password = ""
|
|
479
|
+
var isLoading = false
|
|
480
|
+
var error: String?
|
|
481
|
+
|
|
482
|
+
private let loginUseCase: LoginUseCase
|
|
483
|
+
|
|
484
|
+
init(loginUseCase: LoginUseCase) {
|
|
485
|
+
self.loginUseCase = loginUseCase
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
func login() async {
|
|
489
|
+
isLoading = true
|
|
490
|
+
defer { isLoading = false }
|
|
491
|
+
do {
|
|
492
|
+
try await loginUseCase.execute(email: email, password: password)
|
|
493
|
+
} catch {
|
|
494
|
+
self.error = error.localizedDescription
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
4. **Screen Composable:**
|
|
501
|
+
```swift
|
|
502
|
+
struct LoginScreen: View {
|
|
503
|
+
@State private var viewModel: LoginViewModel
|
|
504
|
+
|
|
505
|
+
init(loginUseCase: LoginUseCase) {
|
|
506
|
+
_viewModel = State(initialValue: LoginViewModel(loginUseCase: loginUseCase))
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
var body: some View {
|
|
510
|
+
Form {
|
|
511
|
+
TextField("Email", text: $viewModel.email)
|
|
512
|
+
.textContentType(.emailAddress)
|
|
513
|
+
.keyboardType(.emailAddress)
|
|
514
|
+
|
|
515
|
+
SecureField("Password", text: $viewModel.password)
|
|
516
|
+
.textContentType(.password)
|
|
517
|
+
|
|
518
|
+
Button("Login") {
|
|
519
|
+
Task { await viewModel.login() }
|
|
520
|
+
}
|
|
521
|
+
.disabled(viewModel.isLoading)
|
|
522
|
+
}
|
|
523
|
+
.overlay { if viewModel.isLoading { ProgressView() } }
|
|
524
|
+
.alert("Error", isPresented: .constant(viewModel.error != nil)) {
|
|
525
|
+
Button("OK") { viewModel.error = nil }
|
|
526
|
+
} message: {
|
|
527
|
+
Text(viewModel.error ?? "")
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
---
|
|
534
|
+
|
|
535
|
+
### Step 5: Third-party SDK & Native Library Integration 📦
|
|
536
|
+
|
|
537
|
+
**Input:** Framework Report from Step 0.
|
|
538
|
+
|
|
539
|
+
**Tasks:**
|
|
540
|
+
1. **Swift Package Manager setup:**
|
|
541
|
+
```swift
|
|
542
|
+
// Package.swift dependencies (or Xcode SPM UI)
|
|
543
|
+
dependencies: [
|
|
544
|
+
.package(url: "https://github.com/firebase/firebase-ios-sdk", from: "11.0.0"),
|
|
545
|
+
.package(url: "https://github.com/onevcat/Kingfisher", from: "7.12.0"),
|
|
546
|
+
.package(url: "https://github.com/kishikawakatsumi/KeychainAccess", from: "4.2.2"),
|
|
547
|
+
]
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
2. **Native C/C++ libraries:**
|
|
551
|
+
```swift
|
|
552
|
+
// Bridging header for C libraries
|
|
553
|
+
// App-Bridging-Header.h
|
|
554
|
+
#include "native_crypto.h"
|
|
555
|
+
|
|
556
|
+
// Swift usage
|
|
557
|
+
func callNativeFunction() -> String {
|
|
558
|
+
let result = native_function_name(param)
|
|
559
|
+
return String(cString: result)
|
|
560
|
+
}
|
|
561
|
+
```
|
|
562
|
+
|
|
563
|
+
3. **App lifecycle for SDK init:**
|
|
564
|
+
```swift
|
|
565
|
+
@main
|
|
566
|
+
struct MyApp: App {
|
|
567
|
+
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
|
|
568
|
+
|
|
569
|
+
var body: some Scene {
|
|
570
|
+
WindowGroup {
|
|
571
|
+
ContentView()
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
class AppDelegate: NSObject, UIApplicationDelegate {
|
|
577
|
+
func application(_ application: UIApplication,
|
|
578
|
+
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
|
579
|
+
FirebaseApp.configure()
|
|
580
|
+
// Other SDK initialization
|
|
581
|
+
return true
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
### Step 6: Parity Check & Quality Gate ✅
|
|
589
|
+
|
|
590
|
+
**Per-module checklist:**
|
|
591
|
+
1. **Branch Coverage:** Review all conditional paths from disassembly
|
|
592
|
+
2. **API Parity:** Same requests/responses as original
|
|
593
|
+
3. **Data Parity:** Crypto output matches, local storage compatible
|
|
594
|
+
4. **UI Parity:** Screen-by-screen comparison
|
|
595
|
+
5. **Performance:**
|
|
596
|
+
- Instruments profiling
|
|
597
|
+
- No unnecessary @State re-renders
|
|
598
|
+
- Proper actor isolation (no data races)
|
|
599
|
+
|
|
600
|
+
---
|
|
601
|
+
|
|
602
|
+
## 🔍 ObjC HEADER READING GUIDE
|
|
603
|
+
|
|
604
|
+
### Property Types → Swift
|
|
605
|
+
```objc
|
|
606
|
+
@property (nonatomic, copy) NSString *name; // → let name: String
|
|
607
|
+
@property (nonatomic, strong) NSArray *items; // → let items: [Any] (refine type)
|
|
608
|
+
@property (nonatomic, assign) BOOL isActive; // → let isActive: Bool
|
|
609
|
+
@property (nonatomic, assign) NSInteger count; // → let count: Int
|
|
610
|
+
@property (nonatomic, assign) CGFloat height; // → let height: CGFloat
|
|
611
|
+
@property (nonatomic, strong) NSDictionary *meta; // → let meta: [String: Any]
|
|
612
|
+
@property (nonatomic, strong) NSDate *createdAt; // → let createdAt: Date
|
|
613
|
+
@property (nonatomic, strong) NSURL *imageURL; // → let imageURL: URL?
|
|
614
|
+
@property (nonatomic, strong) NSData *data; // → let data: Data
|
|
615
|
+
@property (nullable, nonatomic, copy) NSString *bio; // → let bio: String?
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
### Method Signatures → Swift
|
|
619
|
+
```objc
|
|
620
|
+
- (void)fetchUserWithId:(NSString *)userId completion:(void (^)(UserModel *, NSError *))completion;
|
|
621
|
+
// → func fetchUser(id: String) async throws -> User
|
|
622
|
+
|
|
623
|
+
+ (instancetype)sharedInstance;
|
|
624
|
+
// → static let shared = ClassName()
|
|
625
|
+
|
|
626
|
+
- (BOOL)validateEmail:(NSString *)email;
|
|
627
|
+
// → func validateEmail(_ email: String) -> Bool
|
|
628
|
+
```
|
|
629
|
+
|
|
630
|
+
### Delegate Patterns → Swift
|
|
631
|
+
```objc
|
|
632
|
+
@protocol UserServiceDelegate <NSObject>
|
|
633
|
+
- (void)userServiceDidFetchUser:(UserModel *)user;
|
|
634
|
+
- (void)userServiceDidFailWithError:(NSError *)error;
|
|
635
|
+
@end
|
|
636
|
+
```
|
|
637
|
+
```swift
|
|
638
|
+
// Replace with async/await:
|
|
639
|
+
func fetchUser() async throws -> User
|
|
640
|
+
// Or AsyncStream for multiple values:
|
|
641
|
+
func userUpdates() -> AsyncStream<User>
|
|
642
|
+
```
|
|
643
|
+
|
|
644
|
+
### Blocks → Closures / async
|
|
645
|
+
```objc
|
|
646
|
+
typedef void (^CompletionHandler)(NSData * _Nullable data, NSError * _Nullable error);
|
|
647
|
+
- (void)requestWithCompletion:(CompletionHandler)completion;
|
|
648
|
+
```
|
|
649
|
+
```swift
|
|
650
|
+
// Modern Swift: drop the callback, use async
|
|
651
|
+
func request() async throws -> Data
|
|
652
|
+
```
|
|
653
|
+
|
|
654
|
+
---
|
|
655
|
+
|
|
656
|
+
## 🔄 WORKFLOW INTEGRATION
|
|
657
|
+
|
|
658
|
+
```yaml
|
|
659
|
+
triggers_from:
|
|
660
|
+
- "/reverse-ios" workflow command
|
|
661
|
+
- Keywords: "ipa", "class-dump", "objc to swift", "dịch ngược ios", "reverse ios"
|
|
662
|
+
|
|
663
|
+
delegates_to:
|
|
664
|
+
- "/test" — after parity check
|
|
665
|
+
- "/deploy" — when rebuild is complete
|
|
666
|
+
- beads-manager — auto-track progress per step
|
|
667
|
+
|
|
668
|
+
works_with:
|
|
669
|
+
- memory-sync — saves decisions, patterns, solutions
|
|
670
|
+
- orchestrator — routes to this skill based on intent
|
|
671
|
+
- ios-engineer — shares iOS knowledge base
|
|
672
|
+
|
|
673
|
+
independent_from:
|
|
674
|
+
- brainstorm-agent
|
|
675
|
+
- smali-to-kotlin (sibling, same pattern, different platform)
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
---
|
|
679
|
+
|
|
680
|
+
## 🚫 ANTI-PATTERNS
|
|
681
|
+
|
|
682
|
+
```yaml
|
|
683
|
+
never_do:
|
|
684
|
+
- Copy all resources blindly from IPA → only on-demand
|
|
685
|
+
- Use UIKit when SwiftUI equivalent exists → always prefer SwiftUI
|
|
686
|
+
- Use GCD dispatch_async for new code → use async/await
|
|
687
|
+
- Use NSJSONSerialization → use Codable
|
|
688
|
+
- Modify encryption output → must match original exactly
|
|
689
|
+
- Create massive ViewController God objects → split into SwiftUI Views + ViewModels
|
|
690
|
+
- Skip framework scanner step → always detect reusable dependencies first
|
|
691
|
+
- Use ObjC in new code → Swift only (except bridging headers for C libs)
|
|
692
|
+
- Force unwrap optionals → use guard let / if let / nil coalescing
|
|
693
|
+
|
|
694
|
+
always_do:
|
|
695
|
+
- Run Framework Scanner (Step 0) before any coding
|
|
696
|
+
- Present framework report to user for approval
|
|
697
|
+
- Use Swift Concurrency (async/await) for all async operations
|
|
698
|
+
- Use @Observable (iOS 17+) for ViewModels
|
|
699
|
+
- Use NavigationStack for navigation
|
|
700
|
+
- Unit test all encryption/hashing utils
|
|
701
|
+
- Follow Clean Architecture layer separation strictly
|
|
702
|
+
- Use SPM for dependency management (not CocoaPods)
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
---
|
|
706
|
+
|
|
707
|
+
## 📊 CHECKPOINT TEMPLATE
|
|
708
|
+
|
|
709
|
+
After each step, output:
|
|
710
|
+
|
|
711
|
+
```markdown
|
|
712
|
+
## ✅ Step [N] Complete: [Step Name]
|
|
713
|
+
|
|
714
|
+
### What was done:
|
|
715
|
+
- [Summary]
|
|
716
|
+
|
|
717
|
+
### Files created:
|
|
718
|
+
- [List]
|
|
719
|
+
|
|
720
|
+
### Resources extracted:
|
|
721
|
+
- [Only what was needed]
|
|
722
|
+
|
|
723
|
+
### Decisions made:
|
|
724
|
+
- [Key decisions]
|
|
725
|
+
|
|
726
|
+
### ⏭️ Next: Step [N+1] — [Step Name]
|
|
727
|
+
- [What user needs to provide]
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
---
|
|
731
|
+
|
|
732
|
+
## 🧩 PLATFORM RE TEMPLATE PATTERN
|
|
733
|
+
|
|
734
|
+
This skill follows the same **6-step pipeline** as `smali-to-kotlin`:
|
|
735
|
+
|
|
736
|
+
| Step | Android (smali-to-kotlin) | iOS (smali-to-swift) |
|
|
737
|
+
|------|--------------------------|---------------------|
|
|
738
|
+
| 0 | Library Scanner (Smali packages) | Framework Scanner (Frameworks/ + headers) |
|
|
739
|
+
| 1 | AndroidManifest.xml | Info.plist + Entitlements |
|
|
740
|
+
| 2 | Retrofit + Room | URLSession + SwiftData |
|
|
741
|
+
| 3 | Kotlin crypto utils | Swift CryptoKit/CommonCrypto |
|
|
742
|
+
| 4 | Jetpack Compose + StateFlow | SwiftUI + @Observable |
|
|
743
|
+
| 5 | Hilt + JNI | SPM + Bridging Header |
|
|
744
|
+
| 6 | Parity Check | Parity Check |
|
|
745
|
+
|
|
746
|
+
---
|
|
747
|
+
|
|
748
|
+
*smali-to-swift v1.0.0 — iOS Reverse Engineering Skill for AWF*
|
|
749
|
+
*Created by Antigravity Team*
|