Skip to content
Merged
Changes from 1 commit
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
153 changes: 78 additions & 75 deletions src/objc/ModalWebViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -36,87 +36,90 @@ - (void)viewDidLoad {
if (self.interceptRequests) {
NSString *injectedJS =
@"(function() {\n"
" const log = (requestType, data) => {\n"
" try {\n"
" window.webkit.messageHandlers.interceptedRequestHandler.postMessage({ requestType, data });\n"
" } catch (e) {}\n"
" const handler = window.webkit.messageHandlers.interceptedRequestHandler;\n"
" const log = (requestType, data) => { try { handler.postMessage({ requestType, data }); } catch(e) {} };\n"
" \n"
" const nativeToString = Function.prototype.toString;\n"
" const nativeCallToString = Function.prototype.call.bind(nativeToString);\n"
" const wrappedFns = new WeakMap();\n"
" \n"
" Function.prototype.toString = function() {\n"
" if (wrappedFns.has(this)) {\n"
" return wrappedFns.get(this);\n"
" }\n"
" return nativeCallToString(this);\n"
" };\n"
" wrappedFns.set(Function.prototype.toString, 'function toString() { [native code] }');\n"
Comment on lines +43 to +53
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hahaha, missed seeing this. Overriding the toString, good old times. Fun fact though function toString() { [native code] } is only for chromium based browsers, firefox and safari actually has function () {\n [native code]\n}. But I guess it's not an issue even though we are on WKWebView - so Safari - because they don't yet have that much data from users using WebView

Not really sure if it's worth for you changing it to the one with newlines, but again, you did say adding all of this code made stuff work for Delta when they use akamai, so I guess normal users would probably be detected more 😂 classic antibot behavior

" \n"
" const originalFetch = window.fetch;\n"
" window.fetch = async function(input, init) {\n"
" try {\n"
" const method = (init && init.method) || (typeof input === \"string\" ? \"GET\" : input.method || \"GET\");\n"
" const url = typeof input === \"string\" ? input : input.url;\n"
" let requestHeaders = init?.headers || {};\n"
" if (requestHeaders instanceof Headers) {\n"
" requestHeaders = Object.fromEntries(requestHeaders.entries());\n"
" }\n"
" log(\"fetch_request\", {\n"
" url,\n"
" method,\n"
" headers: requestHeaders,\n"
" body: init?.body,\n"
" });\n"
" const response = await originalFetch.apply(this, arguments);\n"
" const clonedResponse = response.clone();\n"
" let responseHeaders = clonedResponse.headers || {};\n"
" if (responseHeaders instanceof Headers) {\n"
" responseHeaders = Object.fromEntries(responseHeaders.entries());\n"
" }\n"
" log(\"fetch_response\", {\n"
" url,\n"
" method,\n"
" headers: responseHeaders,\n"
" body: await clonedResponse.text(),\n"
" status: clonedResponse.status,\n"
" const wrappedFetch = function fetch(input, init) {\n"
" const method = (init && init.method) || (typeof input === 'string' ? 'GET' : input.method || 'GET');\n"
" const url = typeof input === 'string' ? input : input.url;\n"
" let requestHeaders = init?.headers || {};\n"
" if (requestHeaders instanceof Headers) requestHeaders = Object.fromEntries(requestHeaders.entries());\n"
" log('fetch_request', { url, method, headers: requestHeaders, body: init?.body });\n"
" return originalFetch.apply(this, arguments).then(function(response) {\n"
" const cloned = response.clone();\n"
" let responseHeaders = cloned.headers || {};\n"
" if (responseHeaders instanceof Headers) responseHeaders = Object.fromEntries(responseHeaders.entries());\n"
" cloned.text().then(function(body) {\n"
" log('fetch_response', { url, method, headers: responseHeaders, body, status: cloned.status });\n"
" });\n"
" return response;\n"
" } catch (err) {\n"
" log(\"fetch_error\", err.toString());\n"
" throw err;\n"
" }\n"
" });\n"
" };\n"
" wrappedFns.set(wrappedFetch, 'function fetch() { [native code] }');\n"
" Object.defineProperty(window, 'fetch', { value: wrappedFetch, writable: true, configurable: true });\n"
" \n"
" const OriginalXHR = window.XMLHttpRequest;\n"
"\n"
" function PatchedXHR() {\n"
" const xhr = new OriginalXHR();\n"
" let _method = \"\";\n"
" let _url = \"\";\n"
" let headers = {};\n"
" xhr.open = new Proxy(xhr.open, {\n"
" apply(t, thisArg, args) {\n"
" _method = args[0];\n"
" _url = args[1];\n"
" return Reflect.apply(t, thisArg, args);\n"
" },\n"
" });\n"
" const setRequestHeader = xhr.setRequestHeader;\n"
" xhr.setRequestHeader = function(name, value) {\n"
" headers[name] = value;\n"
" return setRequestHeader.apply(xhr, arguments);\n"
" };\n"
" xhr.send = new Proxy(xhr.send, {\n"
" apply(t, thisArg, args) {\n"
" log(\"xhr_request\", {\n"
" method: _method,\n"
" url: _url,\n"
" headers,\n"
" body: args[0],\n"
" });\n"
" xhr.addEventListener(\"loadend\", function() {\n"
" log(\"xhr_response\", {\n"
" method: _method,\n"
" url: _url,\n"
" headers,\n"
" body: xhr.responseText || xhr.response,\n"
" status: xhr.status,\n"
" });\n"
" });\n"
" return Reflect.apply(t, thisArg, args);\n"
" },\n"
" });\n"
" return xhr;\n"
" }\n"
" window.XMLHttpRequest = PatchedXHR;\n"
" const xhrProto = OriginalXHR.prototype;\n"
" const originalOpen = xhrProto.open;\n"
" const originalSend = xhrProto.send;\n"
" const originalSetHeader = xhrProto.setRequestHeader;\n"
" const xhrData = new WeakMap();\n"
" \n"
" xhrProto.open = function(method, url) {\n"
" xhrData.set(this, { method, url, headers: {} });\n"
" return originalOpen.apply(this, arguments);\n"
" };\n"
" wrappedFns.set(xhrProto.open, 'function open() { [native code] }');\n"
" \n"
" xhrProto.setRequestHeader = function(name, value) {\n"
" const data = xhrData.get(this);\n"
" if (data) data.headers[name] = value;\n"
" return originalSetHeader.apply(this, arguments);\n"
" };\n"
" wrappedFns.set(xhrProto.setRequestHeader, 'function setRequestHeader() { [native code] }');\n"
" \n"
" xhrProto.send = function(body) {\n"
" const data = xhrData.get(this);\n"
" if (data) {\n"
" log('xhr_request', { method: data.method, url: data.url, headers: data.headers, body });\n"
" this.addEventListener('loadend', () => {\n"
" log('xhr_response', { method: data.method, url: data.url, headers: data.headers, body: this.responseText || this.response, status: this.status });\n"
" });\n"
" }\n"
" return originalSend.apply(this, arguments);\n"
" };\n"
" wrappedFns.set(xhrProto.send, 'function send() { [native code] }');\n"
" \n"
" Object.defineProperty(navigator, 'webdriver', { get: () => undefined, configurable: true });\n"
" \n"
" const automationProps = ['__webdriver_script_fn', '__driver_evaluate', '__webdriver_evaluate',\n"
" '__selenium_evaluate', '__fxdriver_evaluate', '__driver_unwrapped', '__webdriver_unwrapped',\n"
" '__selenium_unwrapped', '__fxdriver_unwrapped', '_Selenium_IDE_Recorder', '_selenium',\n"
" 'calledSelenium', '_WEBDRIVER_ELEM_CACHE', 'ChromeDriverw', 'driver-hierarchical',\n"
" '__nightmare', '__phantomas', '_phantom', 'phantom', 'callPhantom'];\n"
" automationProps.forEach(p => { try { Object.defineProperty(window, p, { get: () => undefined, configurable: true }); } catch(e) {} });\n"
Comment on lines +109 to +114
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually interesting, any of these are present in WKWebView? Didn't know

" \n"
" const OriginalError = Error;\n"
" Error = function(...args) {\n"
" const err = new OriginalError(...args);\n"
" if (err.stack) err.stack = err.stack.replace(/\\n.*interceptedRequestHandler.*/g, '');\n"
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I knew DD and PX are pretty annoying with CDP error stacks, good catch :P

" return err;\n"
" };\n"
" Error.prototype = OriginalError.prototype;\n"
" Object.setPrototypeOf(Error, OriginalError);\n"
"})();\n";


Expand Down
Loading