Skip to content

Commit

Permalink
[kam800#33] Blacklisting symbols using the source code
Browse files Browse the repository at this point in the history
  • Loading branch information
kam800 committed Jun 26, 2019
1 parent 0446be5 commit dea2888
Show file tree
Hide file tree
Showing 18 changed files with 203 additions and 120 deletions.
76 changes: 42 additions & 34 deletions MachObfuscator.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions MachObfuscator/Controller/Obfuscator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class Obfuscator {
}

func run(loader: ImageLoader & SymbolsSourceLoader & DependencyNodeLoader = SimpleImageLoader(),
headerLoader: HeaderSymbolsLoader = SimpleHeaderSymbolsLoader()) {
sourceSymbolsLoader: SourceSymbolsLoader = SimpleSourceSymbolsLoader()) {
LOGGER.info("Will obfuscate \(directoryURL)")

LOGGER.info("Looking for dependencies...")
Expand All @@ -26,7 +26,7 @@ class Obfuscator {
LOGGER.info("\(paths.nibs.count) obfuscable NIBs")

LOGGER.info("Collecting symbols...")
let symbols = ObfuscationSymbols.buildFor(obfuscationPaths: paths, loader: loader, headerLoader: headerLoader)
let symbols = ObfuscationSymbols.buildFor(obfuscationPaths: paths, loader: loader, sourceSymbolsLoader: sourceSymbolsLoader)
LOGGER.info("\(symbols.whitelist.selectors.count) obfuscable selectors")
LOGGER.info("\(symbols.whitelist.classes.count) obfuscable classes")
LOGGER.info("\(symbols.blacklist.selectors.count) unobfuscable selectors")
Expand Down
5 changes: 0 additions & 5 deletions MachObfuscator/HeadersParsing/HeaderSymbolsLoader.swift

This file was deleted.

49 changes: 0 additions & 49 deletions MachObfuscator/HeadersParsing/SimpleHeaderSymbolsLoader.swift

This file was deleted.

50 changes: 50 additions & 0 deletions MachObfuscator/HeadersParsing/SimpleSourceSymbolsLoader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import Foundation

class SimpleSourceSymbolsLoader: SourceSymbolsLoader {
func load(forFrameworkURL frameworkURL: URL) throws -> SourceSymbols {
return try load(forFrameworkURL: frameworkURL, fileManager: FileManager.default)
}

func load(forFrameworkURL frameworkURL: URL, fileManager: FileManager) throws -> SourceSymbols {
let headers = fileManager.listSourceFilesRecursively(atURL: frameworkURL)
return headers.map(SourceSymbols.load(url:)).flatten()
}
}

private extension FileManager {
func listSourceFilesRecursively(atURL url: URL) -> [URL] {
return listFilesRecursively(atURL: url)
.filter { $0.isSourceFile }
}
}

private extension URL {
private static let sourceFileExtensionSet: Set<String> = ["h", "m"]
var isSourceFile: Bool {
return URL.sourceFileExtensionSet.contains(pathExtension)
}
}

private extension SourceSymbols {
static func load(url: URL) -> SourceSymbols {
let sourceContents: String
do {
sourceContents = try String(contentsOf: url, encoding: .ascii)
} catch {
fatalError("Could not read \(url) because: \(error.localizedDescription)")
}
let selectors = Set(sourceContents.objCMethodNames)
.union(sourceContents.objCPropertyNames)
let classNames = Set(sourceContents.objCTypeNames)
return SourceSymbols(selectors: selectors, classNames: classNames)
}
}

extension Sequence where Element == SourceSymbols {
func flatten() -> SourceSymbols {
return reduce(into: SourceSymbols(selectors: [], classNames: [])) { result, nextSymbols in
result.classNames.formUnion(nextSymbols.classNames)
result.selectors.formUnion(nextSymbols.selectors)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
struct HeaderSymbols {
struct SourceSymbols {
var selectors: Set<String>
var classNames: Set<String>
}
5 changes: 5 additions & 0 deletions MachObfuscator/HeadersParsing/SourceSymbolsLoader.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Foundation

protocol SourceSymbolsLoader {
func load(forFrameworkURL frameworkURL: URL) throws -> SourceSymbols
}
4 changes: 3 additions & 1 deletion MachObfuscator/HeadersParsing/String+objCMethodNames.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ private let methodSuffixRegexp = try! NSRegularExpression(pattern: "\\s([A-Z_]+_
private let namedParameterRegexp = try! NSRegularExpression(pattern: "\\b(\\w+:)", options: [])
private let parameterlessMethodNameRegexp = try! NSRegularExpression(pattern: "\\b(\\w+)\\b", options: [])

private let methodNameDelimiters = CharacterSet([";", "{", "}"])

extension String {
var objCMethodNames: [String] {
let headerWithoutComments = withoutComments
let allLines = headerWithoutComments.components(separatedBy: ";")
let allLines = headerWithoutComments.components(separatedBy: methodNameDelimiters)
return allLines.compactMap { $0.objCMethodNameFromLine }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Foundation
extension ObfuscationSymbols {
static func buildFor(obfuscationPaths: ObfuscationPaths,
loader: SymbolsSourceLoader,
headerLoader: HeaderSymbolsLoader) -> ObfuscationSymbols {
sourceSymbolsLoader: SourceSymbolsLoader) -> ObfuscationSymbols {
let systemSources = try! obfuscationPaths.unobfuscableDependencies.flatMap { try loader.load(forURL: $0) }

let userSourcesPerPath = [URL: [SymbolsSource]](uniqueKeysWithValues: obfuscationPaths.obfuscableImages.map { ($0, try! loader.load(forURL: $0)) })
Expand All @@ -19,7 +19,7 @@ extension ObfuscationSymbols {

let systemHeaderSymbols = obfuscationPaths
.systemFrameworks
.map { try! headerLoader.load(forFrameworkURL: $0) }
.map { try! sourceSymbolsLoader.load(forFrameworkURL: $0) }
.flatten()

// TODO: Array(userCStrings) should be opt-in
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
@interface PublicClass : NSObject

@property (nonatomic, readonly) NSString *publicProperty;

- (void)publicMethod;

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#import "File1.h"

@interface PrivateClass : NSObject

@property (nonatomic, readonly) NSString *privateProperty;

- (void)privateMethod;

@end

@implementation PublicClass

@end
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ extension URL {
static var systemLikeFramework: URL {
return Bundle(for: MarkerClass.self).url(forResource: "SystemLikeFramework", withExtension: "framework")!
}

static var librarySourceCode: URL {
return Bundle(for: MarkerClass.self).url(forResource: "LibrarySourceCode", withExtension: "bundle")!
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import XCTest

class SimpleHeaderSymbolsLoader_loadFromFrameworkURL_allSystemFrameworks_Tests: XCTestCase {
class SimpleSourceSymbolsLoader_loadFromFrameworkURL_allSystemFrameworks_Tests: XCTestCase {

// Disabled because it is very slow.
func DISABLED_test_shouldParseSelectors() {
// Given
let sut = SimpleHeaderSymbolsLoader()
let sut = SimpleSourceSymbolsLoader()

// When
let header = try! sut.load(forFrameworkURL: Paths.iosFrameworksRoot.asURL)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import XCTest

class SimpleHeaderSymbolsLoader_loadFromFrameworkURL_craftedFramework_Tests: XCTestCase {
class SimpleSourceSymbolsLoader_loadFromFrameworkURL_craftedFramework_Tests: XCTestCase {

var header: HeaderSymbols!
var symbols: SourceSymbols!

override func setUp() {
super.setUp()

let sut = SimpleHeaderSymbolsLoader()
header = try! sut.load(forFrameworkURL: URL.craftedFramework)
let sut = SimpleSourceSymbolsLoader()
symbols = try! sut.load(forFrameworkURL: URL.craftedFramework)
}

override func tearDown() {
header = nil
symbols = nil

super.tearDown()
}
Expand Down Expand Up @@ -52,9 +52,9 @@ class SimpleHeaderSymbolsLoader_loadFromFrameworkURL_craftedFramework_Tests: XCT
expectedMethods.union(expectedPropertyNames)

expectedSelectors.forEach {
XCTAssert(header.selectors.contains($0), "Should contain: \($0)")
XCTAssert(symbols.selectors.contains($0), "Should contain: \($0)")
}
let unexpectedSelectors = header.selectors.subtracting(expectedSelectors)
let unexpectedSelectors = symbols.selectors.subtracting(expectedSelectors)
XCTAssertEqual(unexpectedSelectors, [], "Detected unexpected selectors")
}

Expand All @@ -67,6 +67,6 @@ class SimpleHeaderSymbolsLoader_loadFromFrameworkURL_craftedFramework_Tests: XCT
"SampleClass_ForwardDeclaration",
"SampleProtocol_ForwardDeclaration",
]
XCTAssertEqual(header.classNames.symmetricDifference(expectedClassNames), [])
XCTAssertEqual(symbols.classNames.symmetricDifference(expectedClassNames), [])
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import XCTest

class SimpleHeaderSymbolsLoader_loadFromFrameworkURL_systemLikeFramework_Tests: XCTestCase {
class SimpleSourceSymbolsLoader_loadFromFrameworkURL_systemLikeFramework_Tests: XCTestCase {

var header: HeaderSymbols!
var symbols: SourceSymbols!

override func setUp() {
super.setUp()

let sut = SimpleHeaderSymbolsLoader()
header = try! sut.load(forFrameworkURL: URL.systemLikeFramework)
let sut = SimpleSourceSymbolsLoader()
symbols = try! sut.load(forFrameworkURL: URL.systemLikeFramework)
}

override func tearDown() {
header = nil
symbols = nil

super.tearDown()
}
Expand All @@ -23,7 +23,7 @@ class SimpleHeaderSymbolsLoader_loadFromFrameworkURL_systemLikeFramework_Tests:
"UIUserNotificationSettings",
"UIApplicationDelegate"
]
XCTAssertEqual(header.classNames, expectedClassNames)
XCTAssertEqual(symbols.classNames, expectedClassNames)
}

func test_shouldParseAllSelectors() {
Expand All @@ -37,6 +37,6 @@ class SimpleHeaderSymbolsLoader_loadFromFrameworkURL_systemLikeFramework_Tests:
"application:didFinishLaunchingWithOptions:",
"application:handleActionWithIdentifier:forLocalNotification:completionHandler:"
]
XCTAssertEqual(header.selectors, expectedSelectors)
XCTAssertEqual(symbols.selectors, expectedSelectors)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import XCTest

class SimpleSourceSymbolsLoader_loadFromSourcesURL_Tests: XCTestCase {

var symbols: SourceSymbols!

override func setUp() {
super.setUp()

let sut = SimpleSourceSymbolsLoader()
symbols = try! sut.load(forFrameworkURL: URL.librarySourceCode)
}

override func tearDown() {
symbols = nil

super.tearDown()
}

func test_shouldParseSelectors() {
let expectedMethods: Set<String> = [
"publicMethod",
"privateMethod",
]

let expectedPropertyNames: Set<String> = [
"publicProperty",
"privateProperty",
]

let expectedSelectors =
expectedMethods.union(expectedPropertyNames)

expectedSelectors.forEach {
XCTAssert(symbols.selectors.contains($0), "Should contain: \($0)")
}
let unexpectedSelectors = symbols.selectors.subtracting(expectedSelectors)
XCTAssertEqual(unexpectedSelectors, [], "Detected unexpected selectors")
}

func test_shouldParceClassNames() {
let expectedClassNames: Set<String> = [
"PublicClass",
"PrivateClass",
]
XCTAssertEqual(symbols.classNames.symmetricDifference(expectedClassNames), [])
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ class ObfuscationSymbols_Building_buildForObfuscationPaths_Tests: XCTestCase {

var sampleObfuscationPaths: ObfuscationPaths! = ObfuscationPaths()
var testLoader: ObfuscationSymbolsTestSymbolsSourceLoader! = ObfuscationSymbolsTestSymbolsSourceLoader()
var testHeaderLoader: ObfuscationSymbolsTestHeaderSymbolsLoader! = ObfuscationSymbolsTestHeaderSymbolsLoader()
var testSymbolsLoader: ObfuscationSymbolsTestSourceSymbolsLoader! = ObfuscationSymbolsTestSourceSymbolsLoader()
var sut: ObfuscationSymbols!

override func setUp() {
Expand Down Expand Up @@ -49,11 +49,11 @@ class ObfuscationSymbols_Building_buildForObfuscationPaths_Tests: XCTestCase {
classNames: ["c5"],
cstrings: ["s6", "c6"])
]
testHeaderLoader["/tmp/sys1.framework"] = HeaderSymbols(
testSymbolsLoader["/tmp/sys1.framework"] = SourceSymbols(
selectors: [ "sys1s" ],
classNames: [ "sys1c" ]
)
testHeaderLoader["/tmp/sys2.framework"] = HeaderSymbols(
testSymbolsLoader["/tmp/sys2.framework"] = SourceSymbols(
selectors: [ "sys2s" ],
classNames: [ "sys2c" ]
)
Expand All @@ -64,7 +64,7 @@ class ObfuscationSymbols_Building_buildForObfuscationPaths_Tests: XCTestCase {
func buildSUT() -> ObfuscationSymbols {
return ObfuscationSymbols.buildFor(obfuscationPaths: sampleObfuscationPaths,
loader: testLoader,
headerLoader: testHeaderLoader)
sourceSymbolsLoader: testSymbolsLoader)
}

func test_whitelistSelectors_shouldContainObfuscableImagesAccessors_withoutBlacklistedAccessors() {
Expand Down
Loading

0 comments on commit dea2888

Please sign in to comment.