Skip to content

Commit

Permalink
Merge pull request #111 from rockbruno/codingkeys
Browse files Browse the repository at this point in the history
Improve Enum obfuscation
  • Loading branch information
rockbruno authored May 22, 2020
2 parents d23f5e3 + a5fb82a commit 17e140f
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 39 deletions.
15 changes: 1 addition & 14 deletions SOURCEKITISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
- Classes
- Structs
- Methods
- Enums (as long as they don't have `CodingKeys` in the name)
- Enums (except ones that inherit from `CodingKey`)
- Enum cases

# What SwiftShield can't obfuscate

- Properties: Although we can obfuscate them, we avoid doing so because of `Codable` types. We can fix it by checking the inheritance tree of a property's outer type.
- `typealias` and `associatedtypes`: SourceKit doesn't always index them, so we avoid them to prevent broken projects. Note that these can't be reverse engineered as they are purely an editor thing, so avoiding them isn't a problem!
- Enums that have the `CodingKeys` suffix
- Module names: Not implemented yet, but possible.

# SourceKit Bugs
Expand Down Expand Up @@ -55,15 +54,3 @@ public extension Int {
```

If you're using `--ignore-public`, make sure your public extensions follow the pattern from the first snippet.

## Public enums

SourceKit doesn't detect public enum cases as such.

```
public enum Foo {
case bar
}
```

`.bar` will be obfuscated even though the enum is public.
44 changes: 32 additions & 12 deletions Sources/SwiftShieldCore/Obfuscator/SourceKitObfuscator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ extension SourceKitObfuscator {
ofFile _: File
) {
let entityKind: SKUID = dict[keys.kind]!
guard let declType = entityKind.declarationType() else {
guard entityKind.declarationType() != nil else {
return
}
guard let rawName: String = dict[keys.name],
Expand All @@ -68,18 +68,9 @@ extension SourceKitObfuscator {

let name = rawName.removingParameterInformation

// Exclude CodingKeys
// This should be done by checking the `CodingKey` protocol instead of the enum name.
if declType == .enum, name.hasSuffix("CodingKeys") {
dataStore.codableEnumUSRs.insert(usr)
}

if declType == .enumelement {
guard dataStore.codableEnumUSRs.first(where: { usr.hasPrefix($0) }) == nil else {
return
}
if dict.isCodingKeysEnumElement {
return
}
//

logger.log("* Found declaration of \(name) (USR: \(usr))")
dataStore.processedUsrs.insert(usr)
Expand Down Expand Up @@ -232,6 +223,9 @@ extension SourceKitObfuscator {

extension SKResponseDictionary {
var isPublic: Bool {
if let kindId: SKUID = self[sourcekitd.keys.kind], let type = kindId.declarationType(), type == .enumelement {
return parent.isPublic
}
guard let attributes: SKResponseArray = self[sourcekitd.keys.attributes] else {
return false
}
Expand All @@ -250,6 +244,32 @@ extension SKResponseDictionary {
return false
}

var isCodingKeysEnumElement: Bool {
guard let kindId: SKUID = self[sourcekitd.keys.kind],
let type = kindId.declarationType(),
type == .enumelement else
{
return false
}
guard let parentEntities: SKResponseArray = parent[sourcekitd.keys.entities] else {
return false
}
var result = false
parentEntities.forEach(parent: parent) { (i, dict) -> Bool in
guard let kindId: SKUID = dict[sourcekitd.keys.kind],
let type = kindId.referenceType(),
type == .protocol,
let usr: String = dict[self.sourcekitd.keys.usr],
usr == "s:s9CodingKeyP" else
{
return true
}
result = true
return false
}
return result
}

func isReferencingInternalFramework(dataStore: SourceKitObfuscatorDataStore) -> Bool {
guard let kindId: SKUID = self[sourcekitd.keys.kind] else {
return false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,5 @@ final class SourceKitObfuscatorDataStore {
var obfuscatedNames = Set<String>()
var usrRelationDictionary = [String: SKResponseDictionary]()
var indexedFiles = [IndexedFile]()
var codableEnumUSRs = Set<String>()
var plists = Set<File>()
}
16 changes: 11 additions & 5 deletions Sources/SwiftShieldCore/SourceKit/SourceKit.swift
Original file line number Diff line number Diff line change
Expand Up @@ -567,10 +567,12 @@ final class SKResponse {
final class SKResponseDictionary {
let dict: sourcekitd_variant_t
let resp: SKResponse
let parent: SKResponseDictionary!
var sourcekitd: SourceKit { resp.sourcekitd }

init(_ dict: sourcekitd_variant_t, response: SKResponse) {
init(_ dict: sourcekitd_variant_t, response: SKResponse, parent: SKResponseDictionary! = nil) {
self.dict = dict
self.parent = parent
resp = response
}

Expand Down Expand Up @@ -601,7 +603,7 @@ final class SKResponseDictionary {
guard let array: SKResponseArray = self[uid] else {
return
}
array.forEach { (_, dict) -> Bool in
array.forEach(parent: self) { (_, dict) -> Bool in
block(dict)
dict.recurseEntities(block: block)
return true
Expand All @@ -622,14 +624,18 @@ final class SKResponseArray {
var count: Int { sourcekitd.api.variant_array_get_count(array) }

subscript(i: Int) -> SKResponseDictionary {
SKResponseDictionary(sourcekitd.api.variant_array_get_value(array, i), response: resp)
return get(i, parent: nil)
}

private func get(_ i: Int, parent: SKResponseDictionary!) -> SKResponseDictionary {
SKResponseDictionary(sourcekitd.api.variant_array_get_value(array, i), response: resp, parent: parent)
}

/// If the `applier` returns `false`, iteration terminates.
@discardableResult
func forEach(_ applier: (Int, SKResponseDictionary) -> Bool) -> Bool {
func forEach(parent: SKResponseDictionary, _ applier: (Int, SKResponseDictionary) -> Bool) -> Bool {
for i in 0 ..< count {
if !applier(i, self[i]) {
if !applier(i, get(i, parent: parent)) {
return false
}
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/swiftshield/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import SwiftShieldCore

struct Swiftshield: ParsableCommand {
static var configuration = CommandConfiguration(
abstract: "SwiftShield 4.0.1",
abstract: "SwiftShield 4.0.2",
subcommands: [Obfuscate.self, Deobfuscate.self]
)
}
Expand Down
12 changes: 6 additions & 6 deletions Tests/SwiftShieldTests/FeatureTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,9 @@ final class FeatureTests: XCTestCase {
public func ignored4() {}
func notIgnored4() {}
//public enum Bla {
// case abc
//}
public enum Bla {
case abc
}
//Broken.
//public extension Int {
Expand Down Expand Up @@ -177,9 +177,9 @@ final class FeatureTests: XCTestCase {
public func ignored4() {}
func OBS4() {}
//public enum Bla {
// case abc
//}
public enum Bla {
case abc
}
//Broken.
//public extension Int {
Expand Down

0 comments on commit 17e140f

Please sign in to comment.