diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..f07d4e9 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,147 @@ +const baseExtends = ['eslint:recommended', 'prettier', 'plugin:react/recommended', 'plugin:react-hooks/recommended']; + +module.exports = { + parser: '@typescript-eslint/parser', + parserOptions: { + ecmaVersion: 2020, + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + settings: { + react: { version: '16' }, + }, + plugins: ['prettier', 'jest', '@typescript-eslint', 'no-only-tests', 'react-hooks', 'react-hooks-static-deps'], + extends: baseExtends, + env: { + node: true, + es2020: true, + 'jest/globals': true, + }, + rules: { + strict: 0, + 'react/prop-types': 0, + 'prettier/prettier': [ + 'error', + { + printWidth: 120, + semi: true, + singleQuote: true, + tabWidth: 4, + trailingComma: 'all', + useTabs: false, + endOfLine: 'lf', + propTypes: false, + bracketSpacing: true, + }, + ], + 'react/no-unescaped-entities': 0, + 'react/display-name': 'off', + 'no-prototype-builtins': 'off', + 'no-empty': 'off', + 'react-hooks/exhaustive-deps': 'off', + 'react-hooks-static-deps/exhaustive-deps': [ + 'warn', + { + // These hooks are defined to be static for react-hooks/exhaustive-deps eslint rule. + // It means that they are stable and shouldn't be added to the dependency array. + // See: https://www.npmjs.com/package/eslint-plugin-react-hooks-static-deps + staticHooks: { + useStoreStatic: true, + useStoreActions: true, + useIsMounted: true, + useGlobalContext: { + getOpenedModals: true, + showModal: true, + hideModal: true, + hideAllModals: true, + startLoader: true, + stopLoader: true, + showErrorToast: true, + showSuccessToast: true, + }, + useGTMDispatch: true, + useGtm: true, + useEventTargets: true, + useInternalEventReporter: true, + useEventReporter: true, + useStateInRef: true, + useHistory: true, + useIntegrations: true, + useIoContext: true, + useServiceTemplateApi: true, + useBillCommands: true, + useVoidInvoice: true, + useApproveAmendments: true, + useApproveCharges: true, + useApproveInvoices: true, + useUpdateProgressStatus: true, + useCopyDirectLink: true, + useSendReminder: true, + useDebouncedLogSearchToBI: true, + useRelationshipDraftApi: true, + usePusher: true, + usePusherBusinessChannel: { getChannel: true }, + useForm: true, + useFormContext: true, + useQboIntegration: { + fetchAndGetQboIntegration: true, + }, + useStateFromUrl: { + changeUrlState: true, + }, + useHomePage: { + setHidden: true, + }, + useOpenCopyLinkModal: true, + useSignal: [false, true], + useRestfulWrapper: { + refresh: true, + }, + }, + }, + ], + }, + overrides: [ + { + files: ['**/*.ts', '**/*.tsx'], + extends: [...baseExtends, 'plugin:@typescript-eslint/recommended'], + rules: { + 'no-unused-vars': 'off', + 'react/prop-types': 'off', + 'react/display-name': 'off', + '@typescript-eslint/no-empty-function': 'off', + 'no-only-tests/no-only-tests': 'error', + '@typescript-eslint/no-non-null-assertion': 'off', + '@typescript-eslint/no-explicit-any': 'warn', + '@typescript-eslint/no-unused-vars': [ + 'error', + { + argsIgnorePattern: '^_', + varsIgnorePattern: '^_', + }, + ], + 'react/no-unescaped-entities': 'off', + 'react-hooks/exhaustive-deps': 'off', + }, + }, + ], + globals: { + window: true, + navigator: true, + document: true, + history: true, + FileReader: true, + performance: true, + location: true, + localStorage: true, + Cypress: true, + cy: true, + Sentry: true, + alert: true, + File: true, + Blob: true, + fetch: true, + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..51d4ad3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/_agent.js +/node_modules \ No newline at end of file diff --git a/get_class_instances.js b/get_class_instances.js deleted file mode 100644 index 8aed32f..0000000 --- a/get_class_instances.js +++ /dev/null @@ -1,36 +0,0 @@ -function class_exists(class_name) -{ - return !!Java.classFactory.loader.find(class_name); -} - -function get_class_object(class_name) -{ - const classes = Java.enumerateLoadedClassesSync(); - - for (const class_object of classes) - { - if (class_object.match(class_name)) - { - return Java.use(class_object); - } - } - - throw "Error can't find class " + class_name; -} - -function get_class_instances(class_name) -{ - let class_instances = []; - let enumeration_done = false; - - Java.choose( - class_name, - { - onMatch: (class_instance) => class_instances.push(class_instance), - onComplete: () => { enumeration_done = true; } - } - ); - - while (!enumeration_done); - return class_instances; -} diff --git a/get_object_address.js b/get_object_address.js deleted file mode 100644 index 26fd9b6..0000000 --- a/get_object_address.js +++ /dev/null @@ -1,4 +0,0 @@ -function get_object_address(obj) -{ - return obj.toString().split('@')[1]; -} diff --git a/get_process_modules.js b/get_process_modules.js deleted file mode 100644 index ad3ee0e..0000000 --- a/get_process_modules.js +++ /dev/null @@ -1,4 +0,0 @@ -function get_process_modules() -{ - return Process.enumerateModules(); -} diff --git a/interactive.js b/interactive.js deleted file mode 100644 index ea2444a..0000000 --- a/interactive.js +++ /dev/null @@ -1,17 +0,0 @@ -function show_toast(message) -{ - Java.perform(() => { - Java.scheduleOnMainThread(() => { - const ToastWidget = Java.use("android.widget.Toast"); - const context = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext(); - const JavaString = Java.use("java.lang.String"); - - ToastWidget.makeText(context, JavaString.$new(message), 1).show(); - }); - }); -} - -function toast_loop(message, interval_ms) -{ - setInterval(show_toast, interval_ms, message); -} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c952497 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,512 @@ +{ + "name": "frida-agent-example", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frida-agent-example", + "version": "1.0.0", + "devDependencies": { + "@types/frida-gum": "^18.5.1", + "@types/node": "^18.19.3", + "frida-compile": "^16.4.1" + } + }, + "node_modules/@frida/assert": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@frida/assert/-/assert-3.0.2.tgz", + "integrity": "sha512-JXJq5SbXGrM5EkjrZKfRmB29zOoEOix02NC6A5TSJ+C1GE/X051EinJJsuOO2pEOx7KZwpvAHvS0WXW0+levKg==", + "dev": true + }, + "node_modules/@frida/base64-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@frida/base64-js/-/base64-js-2.0.3.tgz", + "integrity": "sha512-2w0F+1TynOTCZ/v7du9LdHPWwq0lJhazjo2fF9upMyQmA1zHetT14fLuQ1v/6T0qPgyeEGkiSrybstU8EsgeUA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/@frida/buffer": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@frida/buffer/-/buffer-7.0.4.tgz", + "integrity": "sha512-RxQ1lZRRiCJj7nhcCiD8xeJx0NsLpGGnjqsmTg7jShGmbnVFMN5W7+J+3gqdPSQhc/IxNBIWc6zRXVp4+qnYHg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.5.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/@frida/crosspath": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@frida/crosspath/-/crosspath-3.0.0.tgz", + "integrity": "sha512-bNdO1spIPD2P40XtK89N49oZpJhstdlnkJZcD4yJ17jrdkm9Ctu0sd9MIEX6Z8Tm8ydhVJBAOMEKl9/R27onAQ==", + "dev": true, + "dependencies": { + "@types/node": "^17.0.36" + }, + "engines": { + "node": ">=14.9.0" + } + }, + "node_modules/@frida/crosspath/node_modules/@types/node": { + "version": "17.0.45", + "resolved": "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz", + "integrity": "sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw==", + "dev": true + }, + "node_modules/@frida/crypto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@frida/crypto/-/crypto-1.0.1.tgz", + "integrity": "sha512-WXTkVjESvX8TIVXIynJJv9BKSIdQP0Iis04StIXknkDi1ULdMlrypA9p5C4KW1H6GCkNsVnKbybpzsulYCTvag==", + "dev": true + }, + "node_modules/@frida/diagnostics_channel": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@frida/diagnostics_channel/-/diagnostics_channel-1.0.0.tgz", + "integrity": "sha512-mYX1jp/5Bpk24tHArJNx65iCk7qSuV8YJkdU0gFNVtJUXxfV8BG5WuPa4mL+ynxsbWWpsg/cwKZbLAepYKTdQQ==", + "dev": true + }, + "node_modules/@frida/events": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@frida/events/-/events-4.0.4.tgz", + "integrity": "sha512-qJVQ6VWHf9sjUKuiJzoCAC00frbpcwxeYfvQ+PP9LU/d70j+QvjWgYe98Qa3ekLaBU6r/AvWm8ThKCDUCLWrQQ==", + "dev": true, + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/@frida/http": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@frida/http/-/http-4.0.2.tgz", + "integrity": "sha512-cvkc7ex7GmVXVOWqtjXKBWUUbYEBgpNRKZbEEoMeI8KiIs8zejKwg+N7rx7296Ao+EP3+xcUr4wBVr3xLaUVfQ==", + "dev": true, + "dependencies": { + "http-parser-js": "^0.5.3" + } + }, + "node_modules/@frida/http-parser-js": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@frida/http-parser-js/-/http-parser-js-1.0.0.tgz", + "integrity": "sha512-2nMrNXt/OeTlWbqnE8AH4Sfz4I2+BGoN206dzKEyC/g2svtn83Xu+zuv/V3TkwrA27s26Mcy84ZwsXeNlqNxUQ==", + "dev": true + }, + "node_modules/@frida/https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@frida/https/-/https-1.0.0.tgz", + "integrity": "sha512-OiqQ6qsALcWOktRLq07oJ0i6sH8eX6MXb/MdZS1qVKDRf6wchH4Pjn6fiLB+pt/OlYbggk+DOfpHwSdjTwuHMQ==", + "dev": true + }, + "node_modules/@frida/ieee754": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@frida/ieee754/-/ieee754-2.0.2.tgz", + "integrity": "sha512-wlcUebnne4ENN7GDr5pTH598ZDLMVOsh0FjenxeVOe6u7ewZkz9gGRnLnZKJAm9kl5G6XhdxhI0cSXVQK/rQUw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/@frida/net": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@frida/net/-/net-4.0.2.tgz", + "integrity": "sha512-qQRe7hQ+ZfCcG/SE3P1TRqQ9bmuK/T7wPCYaT4z56rBPWAxsaQbQHpX4fR6OrFaSDr7X0xJLsTbdIp9hGhhLZg==", + "dev": true + }, + "node_modules/@frida/os": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@frida/os/-/os-1.0.2.tgz", + "integrity": "sha512-3ISAiGNiyIya3QN2EHBCz1wqP0enTdSxP99wUeroeh8+AQRmgoOr/5TRnrVry8pe378anay3fmV/tdUMMSkehQ==", + "dev": true + }, + "node_modules/@frida/path": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@frida/path/-/path-2.0.3.tgz", + "integrity": "sha512-2RQy36QatoC846fzBhBhV8sXMsSOBGoYvwTHeaE1zUdz7F4RNScP4QEekTTooBYWYX/XjiF36KQpYAzc9OYFtg==", + "dev": true + }, + "node_modules/@frida/process": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@frida/process/-/process-1.2.1.tgz", + "integrity": "sha512-nvCu22DstFW2ttGFtOKekHM7vnjbZm+XgtvavOt427GNT6uV7k0JYK9tnMbcLMRWv57DG6udAmuJlWs8Paq1ag==", + "dev": true + }, + "node_modules/@frida/punycode": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@frida/punycode/-/punycode-3.0.0.tgz", + "integrity": "sha512-XVSDY2KamDs1D5/fTVgHcOSNxdU4kTboxzqJMBbTjcQC7XScIT9c0EfbwKCq7Kci6gWQdsHSCr7lU+9Oc4KAdg==", + "dev": true + }, + "node_modules/@frida/querystring": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@frida/querystring/-/querystring-1.0.0.tgz", + "integrity": "sha512-15m1fOZPmoO/vWlgPJrG/J9/BJDz6a2/JpVGpS8ynNzo+fBhTznaStX5nHxUs24mVTqh/OqLo0EiYJM5WWHXxg==", + "dev": true + }, + "node_modules/@frida/readable-stream": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@frida/readable-stream/-/readable-stream-4.1.3.tgz", + "integrity": "sha512-ntGUFmi+CryRGRJIK13a/VST2Ad19uivbln8Xd92vKPAARq+6vMIASDyZIqyl5BLRccfiyCHdYgrgQ6RI5rUig==", + "dev": true + }, + "node_modules/@frida/reserved-words": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@frida/reserved-words/-/reserved-words-1.0.0.tgz", + "integrity": "sha512-2yG/XxJlsGlk/mm6eZTb4OAaQEhkTI2qaFfZFtAsrA/XuCpuMWkS4y/guyBlsRu4hAuhK2HPmNM8+OLLK1zM9Q==", + "dev": true + }, + "node_modules/@frida/stream": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@frida/stream/-/stream-1.0.2.tgz", + "integrity": "sha512-4OuaC1ztmEKgTq3WeBhsy8Oq+AwW9n9cYnvLklcC9jwD93AEwgbWpecLlxJCVuALvTMdhKPg0nQVfyGYP/i9Bw==", + "dev": true, + "dependencies": { + "@frida/readable-stream": "^4.1.3" + } + }, + "node_modules/@frida/string_decoder": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@frida/string_decoder/-/string_decoder-2.0.0.tgz", + "integrity": "sha512-in371tYZMHQiW9HF5MS3JDw6Ao6tyBoq34UWy2rzOswYyMG1rpizh85ofi/yVkxDiaqybEZefxzkVittpPGT6g==", + "dev": true + }, + "node_modules/@frida/terser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@frida/terser/-/terser-1.0.0.tgz", + "integrity": "sha512-59h9WuNzD1Rx/zwoWqQ/FW/4Y/Q3R91Eng2hEwdHapqiTDvtKbZ08F6CynCR7ZVinrh4tLYsF46AtVPTz1ys9g==", + "dev": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.2", + "acorn": "^8.5.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@frida/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/@frida/timers": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@frida/timers/-/timers-3.0.0.tgz", + "integrity": "sha512-3b+0igv10aT8TMxefrTAd06rActqbxJLY2Xkkq9vYcPBffB/yHszl0NYIp/5ko8WC3ecDYPU6bQiY6fjs72zTA==", + "dev": true + }, + "node_modules/@frida/tty": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@frida/tty/-/tty-1.0.0.tgz", + "integrity": "sha512-p/kjLnKYxEAB1MdYP8+5rKv9CsHzyA+0jg9BcGETzjQVKHHcroHDULRxDYUh+DC7qs6cpX8QdDQh9E+a6ydgsQ==", + "dev": true + }, + "node_modules/@frida/url": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@frida/url/-/url-1.0.2.tgz", + "integrity": "sha512-ZKunbKJHMr8w2Eb/5K1avy0MzK1B998S17wYXNv3RmzBGxMm8S5T0F3qEpRxkU7/72P8m4izyQU87fWl+FjQsQ==", + "dev": true, + "dependencies": { + "@frida/punycode": "^3.0.0", + "@frida/querystring": "^1.0.0" + } + }, + "node_modules/@frida/util": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@frida/util/-/util-1.0.3.tgz", + "integrity": "sha512-htcG3uDiRXv89ERVNNYhfase39kJ2X75ZARfrYcYEtJLFEsSk0nemM1YnEIR4CjrHvdvkWHrwgKkS+acOyoNEg==", + "dev": true + }, + "node_modules/@frida/vm": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@frida/vm/-/vm-2.0.0.tgz", + "integrity": "sha512-7fsjLWscZT5odNIBtg6qbLNI+vAk1xmii6H5W2kaYkMYt0vRohQEcDSUWacA+eaWlu5SvMjZI82Yibj/3G9pJw==", + "dev": true + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.5.tgz", + "integrity": "sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.0", + "@jridgewell/trace-mapping": "^0.3.9" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.20", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz", + "integrity": "sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@types/frida-gum": { + "version": "18.5.1", + "resolved": "https://registry.npmjs.org/@types/frida-gum/-/frida-gum-18.5.1.tgz", + "integrity": "sha512-99geyCbWB+YBCqxcO+ue7dJUQJti7kQ5CHGQtKoz0ENtRswKULGMFKW6QgL657sMiztqhcDHWJjYSPv5GKT1ig==", + "dev": true + }, + "node_modules/@types/node": { + "version": "18.19.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.3.tgz", + "integrity": "sha512-k5fggr14DwAytoA/t8rPrIz++lXK7/DqckthCmoZOKNsEbJkId4Z//BqgApXBUGrGddrigYa1oqheo/7YmW4rg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true + }, + "node_modules/commander": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", + "integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/frida-compile": { + "version": "16.4.1", + "resolved": "https://registry.npmjs.org/frida-compile/-/frida-compile-16.4.1.tgz", + "integrity": "sha512-xI9HNtUFpHxuKBGXaL6XJAYH1zWTvNJDrHRnz1hp0oS24iPHt6c01Jmm3qljZek3oQWn8HhvfNvrzfgbsKzoBQ==", + "dev": true, + "dependencies": { + "@frida/assert": "^3.0.1", + "@frida/base64-js": "^2.0.3", + "@frida/buffer": "^7.0.4", + "@frida/crosspath": "^3.0.0", + "@frida/crypto": "^1.0.1", + "@frida/diagnostics_channel": "^1.0.0", + "@frida/events": "^4.0.4", + "@frida/http": "^4.0.2", + "@frida/http-parser-js": "^1.0.0", + "@frida/https": "^1.0.0", + "@frida/ieee754": "^2.0.2", + "@frida/net": "^4.0.1", + "@frida/os": "^1.0.0", + "@frida/path": "^2.0.3", + "@frida/process": "^1.2.1", + "@frida/punycode": "^3.0.0", + "@frida/querystring": "^1.0.0", + "@frida/readable-stream": "^4.1.3", + "@frida/reserved-words": "^1.0.0", + "@frida/stream": "^1.0.2", + "@frida/string_decoder": "^2.0.0", + "@frida/terser": "^1.0.0", + "@frida/timers": "^3.0.0", + "@frida/tty": "^1.0.0", + "@frida/url": "^1.0.2", + "@frida/util": "^1.0.3", + "@frida/vm": "^2.0.0", + "commander": "^11.1.0", + "frida-fs": "^5.2.3", + "typed-emitter": "^2.1.0" + }, + "bin": { + "frida-compile": "dist/cli.js" + } + }, + "node_modules/frida-fs": { + "version": "5.2.5", + "resolved": "https://registry.npmjs.org/frida-fs/-/frida-fs-5.2.5.tgz", + "integrity": "sha512-Eyb4OqUlcv1/Eq7Q+B9IZmYZIgIM2YjqDojrjmAGzPSSXBuUKwSkuObQcQ8Dup9JTOMIUcSII9/I8DaTe6LFKw==", + "dev": true + }, + "node_modules/http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==", + "dev": true + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dev": true, + "optional": true, + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true, + "optional": true + }, + "node_modules/typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/typed-emitter/-/typed-emitter-2.1.0.tgz", + "integrity": "sha512-g/KzbYKbH5C2vPkaXGu8DJlHrGKHLsM25Zg9WuC9pMGfuvT+X25tZQWo5fK1BjBm8+UrVE9LDCvaY0CQk+fXDA==", + "dev": true, + "optionalDependencies": { + "rxjs": "*" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..224c169 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "frida-agent-example", + "version": "1.0.0", + "description": "Example Frida agent written in TypeScript", + "private": true, + "main": "index.ts", + "scripts": { + "prepare": "npm run build", + "build": "frida-compile index.ts -o _agent.js -c", + "watch": "frida-compile index.ts -o _agent.js -w" + }, + "devDependencies": { + "@types/frida-gum": "^18.5.1", + "@types/node": "^18.19.3", + "frida-compile": "^16.4.1" + } +} \ No newline at end of file diff --git a/patch_memory.js b/patch_memory.js deleted file mode 100644 index 0afeac0..0000000 --- a/patch_memory.js +++ /dev/null @@ -1,8 +0,0 @@ -function write_int(addresses, target_value) -{ - for (var i = 0; i < addresses.length; ++i) - { - const address = new NativePointer(addresses[i]); - address.writeInt(target_value); - } -} diff --git a/print_object_properties.js b/print_object_properties.js deleted file mode 100644 index 6b31903..0000000 --- a/print_object_properties.js +++ /dev/null @@ -1,14 +0,0 @@ -function print_object_properties(obj) -{ - for (let field_name in obj) - { - let field_str = `obj.${field_name}`; - - if (eval(field_str).hasAttribute('value')) - { - field_str += '.value'; - } - - console.log(`${field_str} = ${eval(field_str)}`); - } -} diff --git a/deep_clone.js b/src/deep_clone.ts similarity index 79% rename from deep_clone.js rename to src/deep_clone.ts index 23f9628..ba5338f 100644 --- a/deep_clone.js +++ b/src/deep_clone.ts @@ -1,10 +1,8 @@ -function clone_clonable(obj) -{ +function clone_clonable(obj: Java.Wrapper) { return obj.clone(); } -function clone_serializable(obj) -{ +function clone_serializable(obj: Java.Wrapper) { let objectInputStreamClass = Java.use("java.io.ObjectInputStream"); let objectOutputStreamClass = Java.use("java.io.ObjectOutputStream"); @@ -24,8 +22,7 @@ function clone_serializable(obj) return objectInputStream.readObject(); } -function serialize_parcelable(obj) -{ +function serialize_parcelable(obj: Java.Wrapper) { let parcel = Java.use('android.os.Parcel').obtain(); obj.writeToParcel(parcel, 0); @@ -35,8 +32,7 @@ function serialize_parcelable(obj) return data; } -function parse_parcelable(data, class_name) -{ +function parse_parcelable(data: Uint8Array, class_name: string) { let parcel = Java.use('android.os.Parcel').obtain(); let original_cls = Java.use(class_name); @@ -51,10 +47,17 @@ function parse_parcelable(data, class_name) return Java.cast(obj, original_cls); } -function clone_parcelable(obj) -{ +function clone_parcelable(obj: Java.Wrapper) { let class_name = obj.getClass().getName(); let obj_data = serialize_parcelable(obj); return parse_parcelable(obj_data, class_name); } + +export { + clone_clonable, + clone_serializable, + clone_parcelable, + serialize_parcelable, + parse_parcelable +}; diff --git a/disable_ads.js b/src/disable_ads.ts similarity index 64% rename from disable_ads.js rename to src/disable_ads.ts index bfb2a5d..6b4c435 100644 --- a/disable_ads.js +++ b/src/disable_ads.ts @@ -1,7 +1,5 @@ -function attempt_set_vungle_disabled() -{ - try - { +function attempt_set_vungle_disabled() { + try { let vungleClass = Java.use("com.vungle.warren.Vungle"); let adConfigClass = Java.use("com.vungle.warren.AdConfig"); @@ -11,58 +9,55 @@ function attempt_set_vungle_disabled() vungleClass.setMinSpaceForAd(999999999); vungleClass.setGlobalAdConfig(adConfig); } - catch(e) - { + catch (e) { console.log('attempt_set_vungle_disabled failed: ', e); } } -function attempt_disable_google_mobile_ads() -{ - try - { +function attempt_disable_google_mobile_ads() { + try { let MobileAds = Java.use('com.google.android.gms.ads.MobileAds'); - MobileAds.initialize.implementation = function() {}; + MobileAds.initialize.implementation = function () { }; } - catch(e) - { + catch (e) { console.log('attempt_set_vungle_disabled failed: ', e); } } -function attempt_disable_facebook_ads() -{ - try - { +function attempt_disable_facebook_ads() { + try { let FacebookAds = Java.use('com.facebook.ads.AudienceNetworkAds'); - FacebookAds.initialize.implementation = function() {}; + FacebookAds.initialize.implementation = function () { }; } - catch(e) - { + catch (e) { console.log('attempt_set_vungle_disabled failed: ', e); } } -function attempt_disable_unity_ads() -{ - try - { +function attempt_disable_unity_ads() { + try { let UnityAds = Java.use("com.unity3d.ads.UnityAds"); UnityAds.isInitialized.implementation = function () { return true; }; } - catch(e) - { + catch (e) { console.log('attempt_disable_unity_ads failed:', e); } } -function attempt_disable_ads() -{ +function attempt_disable_ads() { attempt_disable_unity_ads(); attempt_set_vungle_disabled(); attempt_disable_facebook_ads(); attempt_disable_google_mobile_ads(); } + +export { + attempt_disable_ads, + attempt_disable_unity_ads, + attempt_set_vungle_disabled, + attempt_disable_facebook_ads, + attempt_disable_google_mobile_ads, +} diff --git a/file_io.js b/src/file_io.ts similarity index 74% rename from file_io.js rename to src/file_io.ts index f3d3960..c6503d2 100644 --- a/file_io.js +++ b/src/file_io.ts @@ -1,13 +1,11 @@ -function file_write_string(file_path, content) -{ +function file_write_string(file_path: string, content: string | ArrayBuffer | number[]) { const file = new File(file_path, 'w'); file.write(content); file.close(); } -function file_read_bytes(file_path) -{ +function file_read_bytes(file_path: string) { const URI = Java.use("java.net.URI"); const Files = Java.use("java.nio.file.Files"); const Paths = Java.use("java.nio.file.Paths"); @@ -15,14 +13,12 @@ function file_read_bytes(file_path) return Files.readAllBytes(Paths.get(URI.create(`file://${file_path}`))); } -function file_read_string(file_path) -{ +function file_read_string(file_path: string) { const fileBytes = file_read_bytes(file_path); return String.fromCharCode(...JSON.parse(JSON.stringify(fileBytes))); } -function file_write_serializable(serializable, file_path) -{ +function file_write_serializable(serializable: Java.Wrapper, file_path: string) { const File = Java.use("java.io.File"); const FileOutputStream = Java.use("java.io.FileOutputStream"); const ObjectOutputStream = Java.use("java.io.ObjectOutputStream"); @@ -35,8 +31,7 @@ function file_write_serializable(serializable, file_path) objectOutputStream.close(); } -function file_read_serializable(file_path) -{ +function file_read_serializable(file_path: string) { const File = Java.use("java.io.File"); const FileInputStream = Java.use("java.io.FileInputStream"); const ObjectInputStream = Java.use("java.io.ObjectInputStream"); @@ -50,3 +45,11 @@ function file_read_serializable(file_path) return obj; } + +export { + file_write_string, + file_read_bytes, + file_read_string, + file_write_serializable, + file_read_serializable, +} diff --git a/src/get_class_instances.ts b/src/get_class_instances.ts new file mode 100644 index 0000000..87b0be8 --- /dev/null +++ b/src/get_class_instances.ts @@ -0,0 +1,39 @@ +function class_exists(class_name: string) { + return !!Java.classFactory.loader?.find(class_name); +} + +function get_class_object(class_name: string) { + const classes = Java.enumerateLoadedClassesSync(); + + for (const class_object of classes) { + if (class_object.match(class_name)) { + return Java.use(class_object); + } + } + + throw "Error can't find class " + class_name; +} + +function get_class_instances(class_name: string) { + let class_instances: Java.Wrapper[] = []; + let enumeration_done = false; + + Java.choose( + class_name, + { + onMatch: (class_instance) => { + class_instances.push(class_instance) + }, + onComplete: () => { enumeration_done = true; } + } + ); + + while (!enumeration_done); + return class_instances; +} + +export { + class_exists, + get_class_object, + get_class_instances +} diff --git a/src/get_object_address.ts b/src/get_object_address.ts new file mode 100644 index 0000000..fc66eb5 --- /dev/null +++ b/src/get_object_address.ts @@ -0,0 +1,7 @@ +function get_object_address(obj: Java.Wrapper) { + return obj.toString().split('@')[1]; +} + +export { + get_object_address +} diff --git a/src/get_process_modules.ts b/src/get_process_modules.ts new file mode 100644 index 0000000..e773bc5 --- /dev/null +++ b/src/get_process_modules.ts @@ -0,0 +1,7 @@ +function get_process_modules() { + return Process.enumerateModules(); +} + +export { + get_process_modules +} diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..4db2114 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,40 @@ +import { write_int } from "./patch_memory" +import { show_toast, toast_loop } from "./interactive" +import { get_object_address } from "./get_object_address" +import { get_process_modules } from "./get_process_modules" +import { print_object_properties } from "./print_object_properties" +import { class_exists, get_class_instances, get_class_object } from "./get_class_instances" +import { print_exception_stack_trace, print_thread_stack_trace, print_throwable_stack_trace } from "./print_stack_trace" +import { clone_clonable, clone_parcelable, clone_serializable, parse_parcelable, serialize_parcelable } from "./deep_clone" +import { file_read_bytes, file_read_serializable, file_read_string, file_write_serializable, file_write_string } from "./file_io" +import { attempt_disable_ads, attempt_disable_facebook_ads, attempt_disable_google_mobile_ads, attempt_disable_unity_ads, attempt_set_vungle_disabled } from "./disable_ads" + +export { + attempt_disable_ads, + attempt_disable_facebook_ads, + attempt_disable_google_mobile_ads, + attempt_disable_unity_ads, + attempt_set_vungle_disabled, + clone_clonable, + clone_parcelable, + clone_serializable, + class_exists, + file_read_bytes, + file_read_serializable, + file_read_string, + file_write_serializable, + file_write_string, + get_class_instances, + get_class_object, + get_object_address, + get_process_modules, + parse_parcelable, + print_exception_stack_trace, + print_object_properties, + print_thread_stack_trace, + print_throwable_stack_trace, + serialize_parcelable, + show_toast, + toast_loop, + write_int +} diff --git a/src/interactive.ts b/src/interactive.ts new file mode 100644 index 0000000..cd7a72e --- /dev/null +++ b/src/interactive.ts @@ -0,0 +1,20 @@ +function show_toast(message: string) { + Java.perform(() => { + Java.scheduleOnMainThread(() => { + const ToastWidget = Java.use("android.widget.Toast"); + const context = Java.use("android.app.ActivityThread").currentApplication().getApplicationContext(); + const JavaString = Java.use("java.lang.String"); + + ToastWidget.makeText(context, JavaString.$new(message), 1).show(); + }); + }); +} + +function toast_loop(message: string, interval_ms: number) { + setInterval(show_toast, interval_ms, message); +} + +export { + show_toast, + toast_loop +} diff --git a/src/patch_memory.ts b/src/patch_memory.ts new file mode 100644 index 0000000..72f66be --- /dev/null +++ b/src/patch_memory.ts @@ -0,0 +1,10 @@ +function write_int(addresses: string[], target_value: number | Int64) { + for (var i = 0; i < addresses.length; ++i) { + const address = new NativePointer(addresses[i]); + address.writeInt(target_value); + } +} + +export { + write_int +} diff --git a/src/print_object_properties.ts b/src/print_object_properties.ts new file mode 100644 index 0000000..9acec62 --- /dev/null +++ b/src/print_object_properties.ts @@ -0,0 +1,15 @@ +function print_object_properties(obj: Java.Wrapper) { + for (let field_name in obj) { + let field_str = `obj.${field_name}`; + + if (eval(field_str).hasAttribute('value')) { + field_str += '.value'; + } + + console.log(`${field_str} = ${eval(field_str)}`); + } +} + +export { + print_object_properties +} diff --git a/print_stack_trace.js b/src/print_stack_trace.ts similarity index 76% rename from print_stack_trace.js rename to src/print_stack_trace.ts index 55a1d04..8c439aa 100644 --- a/print_stack_trace.js +++ b/src/print_stack_trace.ts @@ -1,5 +1,4 @@ -function print_exception_stack_trace() -{ +function print_exception_stack_trace() { let log_utils_cls = Java.use("android.util.Log"); let exception_cls = Java.use("java.lang.Exception"); @@ -7,8 +6,7 @@ function print_exception_stack_trace() console.log(stack_trace_str); } -function print_throwable_stack_trace() -{ +function print_throwable_stack_trace() { let PrintWriter = Java.use('java.io.PrintWriter'); let StringWriter = Java.use('java.io.StringWriter'); let throwable = Java.use('java.lang.Throwable').$new(); @@ -20,14 +18,14 @@ function print_throwable_stack_trace() console.log(sw.toString()); } -function print_thread_stack_trace() -{ +function print_thread_stack_trace() { let Thread = Java.use('java.lang.Thread'); let currentThread = Thread.currentThread(); let stackTrace = currentThread.getStackTrace(); - stackTrace.forEach(function(stackTraceElement) { + // TODO: repalce this any + stackTrace.forEach(function (stackTraceElement: any) { let fileName = stackTraceElement.getFileName(); let className = stackTraceElement.getClassName(); let methodName = stackTraceElement.getMethodName(); @@ -36,3 +34,9 @@ function print_thread_stack_trace() console.log(`${className}.${methodName} at ${fileName}:${lineNumber}`); }); } + +export { + print_exception_stack_trace, + print_thread_stack_trace, + print_throwable_stack_trace +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..12231bb --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "target": "es2020", + "lib": ["es2020"], + "allowJs": true, + "noEmit": true, + "strict": true, + "esModuleInterop": true + } +} \ No newline at end of file