Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix emojis breaking obfuscation #110

Merged
merged 1 commit into from
May 18, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion SOURCEKITISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ These are problems that SourceKit has that are unrelated to a specific feature,
**Note: You can use the `--ignore-targets` argument to completely disable the obfuscation of specific targets.**

- (SR-9020)](https://bugs.swift.org/browse/SR-9020) Legacy KeyPaths that include types (like `#keyPath(Foo.bar)`) will not get indexed.
- Any file that has an emoji will break the obfuscation process. This may not be a SourceKit bug itself, but something that we have to treat on our side.
- `@objc optional` protocol methods don't have their references indexed.

# Additional important information
Expand Down
9 changes: 5 additions & 4 deletions Sources/SwiftShieldCore/Obfuscator/SourceKitObfuscator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -199,27 +199,28 @@ extension SourceKitObfuscator {
// Avoid duplicates.
currentReferenceIndex += 1
}
let currentCharacter = charArray[currentCharIndex]
if line == reference.line, column == reference.column {
previousReference = reference
let originalName = reference.name
let obfuscatedName = obfuscate(name: originalName)
let wasInternalKeyword = charArray[currentCharIndex] == "`"
let wasInternalKeyword = currentCharacter == "`"
for i in 1 ..< (originalName.count + (wasInternalKeyword ? 2 : 0)) {
charArray[currentCharIndex + i] = ""
}
charArray[currentCharIndex] = obfuscatedName
currentReferenceIndex += 1
currentCharIndex += originalName.count
column += originalName.count
column += originalName.utf8Count
if wasInternalKeyword {
charArray[currentCharIndex] = ""
}
} else if charArray[currentCharIndex] == "\n" {
} else if currentCharacter == "\n" {
line += 1
column = 1
currentCharIndex += 1
} else {
column += 1
column += currentCharacter.utf8Count
currentCharIndex += 1
}
}
Expand Down
7 changes: 7 additions & 0 deletions Sources/SwiftShieldCore/StringHelpers.swift
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,10 @@ extension NSTextCheckingResult {
return groupStartIndex ..< groupEndIndex
}
}

extension String {
/// Considers emoji scalars when counting.
var utf8Count: Int {
return utf8.count
}
}
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.0",
abstract: "SwiftShield 4.0.1",
subcommands: [Obfuscate.self, Deobfuscate.self]
)
}
Expand Down
60 changes: 60 additions & 0 deletions Tests/SwiftShieldTests/FeatureTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -188,4 +188,64 @@ final class FeatureTests: XCTestCase {
//}
""")
}

func test_files_withEmojis() throws {
let (obfs, store, delegate) = baseTestData()
let module = try testModule(withContents: """
enum JSON {
static func parse(_ a: String) -> String { return a }
}

extension String {
func unobfuscate() -> String { return self }
}

func l3️⃣og(_ a: String) -> String { return JSON.parse("") }

var paramsString = "foo"

struct Logger {
func log() {
log("Hello 👨‍👩‍👧‍👧 3 A̛͚̖ 3️⃣ response up message 📲: \\(JSON.parse(paramsString.unobfuscate()).description) 🇹🇩👫👨‍👩‍👧‍👧👨‍👨‍👦"); log("")
}

func log(_ a: String) {
_ = l3️⃣og("foo".unobfuscate())
}
}
""")
store.obfuscationDictionary["JSON"] = "OBS1"
store.obfuscationDictionary["parse"] = "OBS2"
store.obfuscationDictionary["unobfuscate"] = "OBS3"
store.obfuscationDictionary["log"] = "OBS4"
store.obfuscationDictionary["Logger"] = "OBS5"
store.obfuscationDictionary["l3️⃣og"] = "OBS6"

try obfs.registerModuleForObfuscation(module)
try obfs.obfuscate()

XCTAssertEqual(delegate.receivedContent[modifiableFilePath], """
enum OBS1 {
static func OBS2(_ a: String) -> String { return a }
}

extension String {
func OBS3() -> String { return self }
}

func OBS6(_ a: String) -> String { return OBS1.OBS2("") }

var paramsString = "foo"

struct OBS5 {
func OBS4() {
OBS4("Hello 👨‍👩‍👧‍👧 3 A̛͚̖ 3️⃣ response up message 📲: \\(OBS1.OBS2(paramsString.OBS3()).description) 🇹🇩👫👨‍👩‍👧‍👧👨‍👨‍👦"); OBS4("")
}

func OBS4(_ a: String) {
_ = OBS6("foo".OBS3())
}
}
""")
}
}