├── .gitignore ├── LICENSE ├── README.md ├── files ├── htdoc │ ├── favicon.ico │ ├── favicon.png │ └── index.html └── library │ ├── arm │ └── libtaikari.so │ ├── arm64 │ └── libtaikari.so │ └── jlhttp.dex ├── src ├── Android.mk ├── CMakeLists.txt ├── taikari.cpp └── taikari.h └── taikari.js /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 616 SB License 2 | 3 | Copyright (c) 2022 TheSnowfield 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | The person should speak loudly with "616 SB!", before using this Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 11 | 12 | 13 |
5 | 6 | 8 |

Taikari

9 |

Taikari is intent on destroying the Arcaea world.

10 |
14 | 15 | ![ver](https://img.shields.io/badge/taikari-v0.6.2-blue) ![arc](https://img.shields.io/badge/arcaea-4.1.4c-716dba) 16 | 17 | ## Hack Tool Sets 18 | | name | description | 19 | | :--- | :---------- | 20 | | captureSSL | Capture the SSL traffic print to screen | 21 | | dumpCertficate | Hex dump the latest P12 certificate | 22 | | hookOnlineManagerCtor | An almost perfect hook scheme of OnlineManager. | 23 | | challengeHookTest | Test the challenge hook | 24 | | challengeServer | Challenge server over the HTTP | 25 | | pretendArcVersion | Set the fake arcaea version | 26 | | pretendDeviceId | Set the fake device id | 27 | 28 | ## Deploy 29 | - Install frida-server following the official documentation. 30 | - Copy the `files` folder to `/system/usr/` then renaming to `taikari`. 31 | 32 | ## Usage 33 | > Recommend to use Frida 15 and Android 7+. 34 | ```bash 35 | $ frida -U -f "moe.low.arc" --no-pause -l taikari.js 36 | 37 | # Optional for Frida 16: frida -U -f "moe.low.arc" -l taikari.js 38 | # -U (use USB device) 39 | # -f (spawn the target app) 40 | # --no-pause (do not pause the thread while app start) 41 | # -l (load script) 42 | ``` 43 | 44 | ## Compatible Info 45 | | Arcaea (build) | arm64-v8a | armeabi-v7a | x86 | x86_64 | 46 | | :-------------- | :-----------: | :-----------: | :---: | :---: | 47 | | 3.11.2c_1019305 | ✔ | ✔ | ❌ | ❌ | 48 | | 3.12.0c_1020007 | ✔ | ✔ | ❌ | ❌ | 49 | | 3.12.1c_1020010 | ✔ | ✔ | ❌ | ❌ | 50 | | 3.12.2c_1020517 | ✔ | ✔ | ❌ | ❌ | 51 | | 3.12.6c_1032000 | 🟡 | ❌ (lazy) | ❌ | ❌ | 52 | | 4.0.0c_1050010 | 🟡 (wip) | ✔ | ❌ | ❌ | 53 | | 4.0.1c_1050014 | 🟡 (wip) | ✔ | ❌ | ❌ | 54 | | 4.0.255c_1060002 | 🟡 (wip) | ✔ | ❌ | ❌ | 55 | | 4.0.256c_1070001 | 🟡 (wip) | ✔ | ❌ | ❌ | 56 | | 4.1.4c_1092003 | ❌ (lazy) | ✔ | ❌ | ❌ | 57 | 58 | ## License 59 | Licensed under `616 SB License`. 60 | -------------------------------------------------------------------------------- /files/htdoc/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcaea-Infinity/Taikari/7a7d7763a1431e55428675f295081c35de701dcf/files/htdoc/favicon.ico -------------------------------------------------------------------------------- /files/htdoc/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcaea-Infinity/Taikari/7a7d7763a1431e55428675f295081c35de701dcf/files/htdoc/favicon.png -------------------------------------------------------------------------------- /files/htdoc/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Challenge Generator 5 | 6 | 7 | 8 | 9 | 10 |
11 |

Taikari X-Random-Challenge API

12 |

ATTENTION! This API is for everyone, Please DO NOT abuse this service.

13 |

GET /v1/generate

14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
argumentsdescriptionoptional
methodGET or POSTNO
pathrequest pathNO
bodypost bodyYES, while the method is GET
40 |

Explain

41 |

Generates an X-Random-Challenge string.

42 |

Return value

43 |
{
44 |     "status": 0,
45 |     "content": {
46 |         challenge: "xxxx"
47 |     }
48 | }
49 | 
50 |

Error codes

51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 |
statusdescription
0everything is OK
65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /files/library/arm/libtaikari.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcaea-Infinity/Taikari/7a7d7763a1431e55428675f295081c35de701dcf/files/library/arm/libtaikari.so -------------------------------------------------------------------------------- /files/library/arm64/libtaikari.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcaea-Infinity/Taikari/7a7d7763a1431e55428675f295081c35de701dcf/files/library/arm64/libtaikari.so -------------------------------------------------------------------------------- /files/library/jlhttp.dex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcaea-Infinity/Taikari/7a7d7763a1431e55428675f295081c35de701dcf/files/library/jlhttp.dex -------------------------------------------------------------------------------- /src/Android.mk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Arcaea-Infinity/Taikari/7a7d7763a1431e55428675f295081c35de701dcf/src/Android.mk -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | # For more information about using CMake with Android Studio, read the 3 | # documentation: https://d.android.com/studio/projects/add-native-code.html 4 | 5 | # Sets the minimum version of CMake required to build the native library. 6 | 7 | cmake_minimum_required(VERSION 3.10.2) 8 | 9 | # Declares and names the project. 10 | 11 | project("taikari") 12 | 13 | # Creates and names a library, sets it as either STATIC 14 | # or SHARED, and provides the relative paths to its source code. 15 | # You can define multiple libraries, and CMake builds them for you. 16 | # Gradle automatically packages shared libraries with your APK. 17 | 18 | add_library( # Sets the name of the library. 19 | taikari 20 | 21 | # Sets the library as a shared library. 22 | SHARED 23 | 24 | # Provides a relative path to your source file(s). 25 | taikari.cpp ) 26 | 27 | # Searches for a specified prebuilt library and stores the path as a 28 | # variable. Because CMake includes system libraries in the search path by 29 | # default, you only need to specify the name of the public NDK library 30 | # you want to add. CMake verifies that the library exists before 31 | # completing its build. 32 | 33 | find_library( # Sets the name of the path variable. 34 | log-lib 35 | 36 | # Specifies the name of the NDK library that 37 | # you want CMake to locate. 38 | log ) 39 | 40 | # Specifies libraries CMake should link to your target library. You 41 | # can link multiple libraries, such as libraries you define in this 42 | # build script, prebuilt third-party libraries, or system libraries. 43 | 44 | target_link_libraries( # Specifies the target library. 45 | taikari 46 | 47 | # Links the target library to the log library 48 | # included in the NDK. 49 | ${log-lib} ) 50 | -------------------------------------------------------------------------------- /src/taikari.cpp: -------------------------------------------------------------------------------- 1 | #include "taikari.h" 2 | 3 | int64_t sendHttpRequest(online_manager_t cls, send_request_t func, 4 | const char *url, request_type_t request_type, 5 | const char *header, const char *post_body) 6 | { 7 | // check pointers 8 | if (!cls) 9 | return -100; 10 | if (!func) 11 | return -101; 12 | if (!url) 13 | return -102; 14 | if (!header) 15 | return -103; 16 | if (!post_body) 17 | return -104; 18 | 19 | // create objects 20 | auto _url = std::string(url); 21 | auto _tag_name = std::string(""); 22 | auto _post_body = std::string(post_body); 23 | auto _extra_header = split(header, "\n"); 24 | auto _callback = std::function(responseCallback); 25 | 26 | // call send request 27 | return func(cls, _url, request_type, _tag_name, _callback, _extra_header, _post_body, 0); 28 | } 29 | 30 | size_t setFavoriteCharacter(online_manager_t cls, set_favorite_character_t func, 31 | size_t id) 32 | { 33 | // check pointers 34 | if (!cls) 35 | return -100; 36 | 37 | // create objects 38 | auto _callback = std::function(responseCallback); 39 | 40 | // call function 41 | return func(cls, id, _callback); 42 | } 43 | 44 | void responseCallback(void *client, void *response) 45 | { 46 | // do nothing 47 | } 48 | 49 | std::vector split(std::string s, std::string delimiter) 50 | { 51 | size_t pos_start = 0, pos_end, delim_len = delimiter.length(); 52 | std::string token; 53 | std::vector res; 54 | 55 | while ((pos_end = s.find(delimiter, pos_start)) != std::string::npos) 56 | { 57 | token = s.substr(pos_start, pos_end - pos_start); 58 | pos_start = pos_end + delim_len; 59 | res.push_back(token); 60 | } 61 | 62 | res.push_back(s.substr(pos_start)); 63 | return res; 64 | } 65 | -------------------------------------------------------------------------------- /src/taikari.h: -------------------------------------------------------------------------------- 1 | #ifndef _TAIKRI_H_ 2 | #define _TAIKRI_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | // request type 9 | typedef enum request_type 10 | { 11 | get = 0, 12 | post = 1 13 | } request_type_t; 14 | 15 | // online manager lpthis 16 | typedef void *online_manager_t; 17 | 18 | typedef int64_t (*send_request_t)(online_manager_t cls, 19 | std::string url, 20 | std::int32_t request_type, 21 | std::string tag_name, 22 | std::function const &callback, 23 | std::vector extra_header, 24 | std::string post_body, 25 | bool zero); 26 | 27 | typedef size_t (*set_favorite_character_t)(online_manager_t cls, 28 | std::size_t id, 29 | std::function const &callback); 30 | 31 | /** 32 | * @brief Call online manager 33 | * 34 | * @param cls 35 | * @param func 36 | * @param url 37 | * @param request_type 38 | * @param header 39 | * @param post_body 40 | * @return int64_t 41 | */ 42 | int64_t sendHttpRequest(online_manager_t cls, send_request_t func, 43 | const char *url, request_type_t request_type, 44 | const char *header, const char *post_body) asm("sendHttpRequest"); 45 | 46 | /** 47 | * @brief Call set favorite character 48 | * @param cls 49 | * @param func 50 | * @param id 51 | * @return 52 | */ 53 | size_t setFavoriteCharacter(online_manager_t cls, set_favorite_character_t func, 54 | size_t id) asm("setFavoriteCharacter"); 55 | 56 | /** 57 | * @brief Result callback 58 | * 59 | * @param client 60 | * @param response 61 | */ 62 | void responseCallback(void *client, void *response); 63 | 64 | /** 65 | * @brief split string 66 | * https://stackoverflow.com/questions/14265581/parse-split-a-string-in-c-using-string-delimiter-standard-c 67 | * 68 | * @param s 69 | * @param delimiter 70 | * @return std::vector 71 | */ 72 | std::vector split(std::string s, std::string delimiter); 73 | 74 | #endif /* _TAIKRI_H_ */ 75 | -------------------------------------------------------------------------------- /taikari.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Taikari frida tool 3 | * (C) TheSnowfield 4 | * 5 | * Usage: frida -U -f "moe.low.arc" --no-pause -l taikari.js 6 | */ 7 | 8 | const config = { 9 | 10 | // hacktools 11 | hackTools: [ 12 | { name: 'captureSSL', enabled: false, func: hackCaptureSSL }, 13 | { name: 'dumpCertficate', enabled: false, func: hackDumpCertificate }, 14 | { name: 'hookOnlineManagerCtor', enabled: true, func: hackOnlineManagerCtor }, 15 | { name: 'challengeHookTest', enabled: false, func: hackChallengeHookTest }, 16 | { name: 'challengeServer', enabled: true, func: hackChallengeServer }, 17 | { name: 'pretendArcVersion', enabled: false, func: hackPretendArcVersion }, 18 | { name: 'pretendDeviceId', enabled: false, func: hackPretendDeviceId }, 19 | ], 20 | 21 | // folders 22 | resFolder: { 23 | 'htdoc': '/system/usr/taikari/htdoc', 24 | 'library': '/system/usr/taikari/library' 25 | }, 26 | 27 | // libraries 28 | useNative: true, 29 | useJLHttp: true, 30 | 31 | // challenge server 32 | challengeHttpPort: 23333, 33 | 34 | // specific arcaea version 35 | arcVersion: 'init', 36 | 37 | // pretend 38 | pretendDeviceId: 'ffffffffffffffff', 39 | pretendArcVersion: '6.1.6c (Taikari)', 40 | 41 | // pre-defined symbols 42 | libSymbols: { 43 | '3.11.2c_1019305_arm64-v8a': [ 44 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0x111caf4 }, 45 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x122996c }, // curl_easy_perform also calling this 46 | { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0x11f273c }, 47 | { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: 0x1537c18 }, 48 | { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0x125226c } 49 | ], 50 | '3.11.2c_1019305_armeabi-v7a': [ 51 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0xc19fbc }, 52 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0xe5a664 }, 53 | { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0x9e2cbd }, 54 | { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: 0xc43af1 }, 55 | { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0xd3beb1 } 56 | ], 57 | '3.12.0c_1020007_armeabi-v7a': [ 58 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0x41c264 }, 59 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x3bca58 }, 60 | { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0x364971 }, 61 | { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: 0x3584c1 }, 62 | { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0x7b0009 } 63 | ], 64 | '3.12.0c_1020007_arm64-v8a': [ 65 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0xe112e4 }, 66 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x6ff1e0 }, 67 | { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0xbf1234 }, 68 | { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: 0x567064 }, 69 | { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0xd463fc } 70 | ], 71 | '3.12.1c_1020010_armeabi-v7a': [ 72 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0x6a946c }, 73 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x6e3fa8 }, 74 | { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0x38a0c1 }, 75 | { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: 0x6fcf05 }, 76 | { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0x5F4CA9 } 77 | ], 78 | '3.12.1c_1020010_arm64-v8a': [ 79 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0xbc7dc4 }, 80 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0xe711b0 }, 81 | { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0xc583dc }, 82 | { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: 0x8080cc }, 83 | { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0xde3cc0 } 84 | ], 85 | '3.12.2c_1020517_armeabi-v7a': [ 86 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0x7e0bb8 }, 87 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x5ee218 }, 88 | { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0x4153c5 }, 89 | { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: 0x60c9d5 }, 90 | { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0x5d9d9d } 91 | ], 92 | '3.12.2c_1020517_arm64-v8a': [ 93 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0x6e4564 }, 94 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x765350 }, 95 | { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0x69a380 }, 96 | { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: 0x8ba05c }, 97 | { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0xbd898c } 98 | ], 99 | '3.12.6c_1032000_arm64-v8a': [ // not working 100 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0xa43f64 }, 101 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0xbf22a4 }, 102 | { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0xd7a2b0 }, 103 | { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: 0xcb1088 }, 104 | { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0xacc900 } 105 | ], 106 | '4.0.0c_1050010_armeabi-v7a' : [ 107 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0x3f9008 }, 108 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x411a60 }, 109 | { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0x4301c5 }, 110 | { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: 0x31b7a1 }, 111 | { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0x786a8d } 112 | ], 113 | '4.0.1c_1050014_armeabi-v7a': [ 114 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0x80d24c }, 115 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x8263f4 }, 116 | { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0x3b7b6d }, 117 | { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: 0x6234b1 }, 118 | { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0x8853b9 } 119 | ], 120 | '4.0.0c_1050010_arm64-v8a' : [//wip 121 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0x788fbc }, 122 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0xcb2768 }, 123 | //{ name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: }, 124 | //{ name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: }, 125 | //{ name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: } 126 | ], 127 | '4.0.1c_1050014_arm64-v8a': [//wip 128 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0xa15c48 }, 129 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0xcda784 }, 130 | //{ name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: }, 131 | //{ name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: }, 132 | //{ name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: } 133 | ], 134 | '4.0.255c_1060002_armeabi-v7a' : [ // wip 135 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0x3700b4 }, 136 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x7c5e88 }, 137 | // { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0x4bd7e9 }, 138 | // { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: }, 139 | { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0x75ffdd } 140 | ], 141 | '4.0.255c_1060002_arm64-v8a': [//wip 142 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0xb4f734 }, 143 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x91166c },// 144 | //{ name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: }, 145 | //{ name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: }, 146 | //{ name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: } 147 | ], 148 | '4.0.256c_1070001_armeabi-v7a' : [ //untested 149 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0x3b9ee4 }, 150 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x623928 }, 151 | // { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: }, 152 | // { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: }, 153 | // { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0x86e601 } 154 | ], 155 | '4.0.256c_1070001_arm64-v8a' : [ //wip 156 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0xc81d44 }, 157 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0xb30a88 }, 158 | // { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: }, 159 | // { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: }, 160 | // { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: } 161 | ], 162 | '4.1.4c_1092003_armeabi-v7a' : [ 163 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc: 0x69af78 }, 164 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x62caa4 }, 165 | // { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: }, 166 | // { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: }, 167 | // { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: } 168 | ], // idk just test lmao 169 | '4.1.9c_1095000_armeabi-v7a' : [ 170 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc:0x962390 }, 171 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x6f2094 }, 172 | { name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: 0x79e198 }, 173 | // { name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: 0x64a170 }, 174 | // { name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: 0x79e198} 175 | ], 176 | '4.3.2c_1098002_arm64-v8a' : [ 177 | { name: 'libcocos2dcpp.so!curl_easy_setopt', proc:0x638ff8 }, 178 | { name: 'libcocos2dcpp.so!easy_perform', proc: 0x83ee6c }, 179 | //{ name: 'libcocos2dcpp.so!OnlineManager::OnlineManager', proc: }, 180 | //{ name: 'libcocos2dcpp.so!OnlineManager::sendHttpRequest', proc: }, 181 | //{ name: 'libcocos2dcpp.so!OnlineManager::setFavoriteCharacter', proc: } 182 | ], 183 | } 184 | }; 185 | // save original functions 186 | const __console_log = console.log; 187 | const __console_error = console.error; 188 | console.log = (...msg) => __console_log(new Date().toLocaleString(), '[*]', msg); 189 | console.error = (...msg) => __console_error(new Date().toLocaleString(), '[!]', msg); 190 | console.info = (...msg) => __console_log(new Date().toLocaleString(), '[i]', msg); 191 | console.raw = (...msg) => __console_log(msg); 192 | 193 | // start when cocos_android_app_init reached 194 | Interceptor.attach(Module.findExportByName('liblog.so', '__android_log_print'), { 195 | 196 | onEnter: (args) => { 197 | 198 | let _logstr = args[2].readUtf8String(); 199 | if (_logstr != 'cocos_android_app_init') return; 200 | console.raw(''); 201 | 202 | // get version 203 | config.arcVersion = getArcaeaVersion(); 204 | console.log(`current version is [${config.arcVersion}]`); 205 | 206 | // is supported 207 | if (!taiSupported()) { 208 | console.log('sorry, taikari currently not supported this device or arcaea.'); 209 | return; 210 | } 211 | 212 | // load native helper 213 | if (config.useNative) { 214 | Module.load(`${resFolder('library')}/${Process.arch}/libtaikari.so`); 215 | console.info('library \'libtaikari.so\' scuessfully loaded.'); 216 | } 217 | 218 | // load compiled dex library 219 | if (config.useJLHttp) { 220 | Java.openClassFile(`${resFolder('library')}/jlhttp.dex`).load(); 221 | console.info('dex file \'jlhttp.dex\' scuessfully loaded.'); 222 | } 223 | 224 | // apply hack tools where enabled 225 | config.hackTools.forEach(tool => { 226 | if (tool.enabled) { 227 | tool.func(); 228 | console.info(`tool \'${tool.name}\' enabled.`); 229 | } 230 | }); 231 | 232 | } 233 | }); 234 | 235 | ////// Hack Tools 236 | ///////////////////////////////////////////////////////// 237 | 238 | /** 239 | * dump certificate 240 | */ 241 | function hackDumpCertificate() { 242 | 243 | // hook curl_easy_setopt 244 | console.log('attaching [libcocos2dcpp.so!curl_easy_setopt]'); 245 | Interceptor.attach(libSymbol('libcocos2dcpp.so!curl_easy_setopt'), { 246 | 247 | onEnter: (args) => { 248 | 249 | // CURLOPT_SSLCERT_BLOB 250 | if (args[1] == 0x9d63) { 251 | let blob = args[2]; 252 | 253 | // Calc pointers 254 | let cert = blob.readPointer(); 255 | let length = blob.add(Process.pointerSize).readULong(); 256 | let bytes = cert.readByteArray(length); 257 | 258 | console.log('Certificate'); 259 | console.raw(hexdump(bytes)); 260 | } 261 | 262 | // CURLOPT_KEYPASSWD 263 | if (args[1] == 0x272a) { 264 | console.log('Certificate Pwd'); 265 | console.log(args[2].readUtf8String()); 266 | return; 267 | } 268 | } 269 | 270 | }); 271 | } 272 | 273 | /** 274 | * ssl traffic capturing 275 | */ 276 | function hackCaptureSSL() { 277 | 278 | let _sslWrite = libSymbol('libcocos2dcpp.so!SSL_write'); 279 | let _sslWriteOld = new NativeFunction(_sslWrite, 'int', ['pointer', 'pointer', 'int']); 280 | let counter = 1; 281 | 282 | // traffic out 283 | console.log('raplacing [libcocos2dcpp.so!SSL_write]'); 284 | Interceptor.replace(_sslWrite, new NativeCallback((ctx, buffer, length) => { 285 | 286 | counter++; 287 | 288 | // remove gzip compress feature 289 | let replace = buffer.readUtf8String(length).replace('Accept-Encoding: deflate, gzip\r\n', ''); 290 | let newBuffer = Memory.allocUtf8String(replace); 291 | 292 | // filter multiple calls 293 | if (counter % 2 == 0) { 294 | console.raw('\n====', new Date().toLocaleString(), '===='); 295 | console.raw(replace, '\n'); 296 | } 297 | 298 | // write data to 299 | return _sslWriteOld(ctx, newBuffer, replace.length); 300 | 301 | }, 'int', ['pointer', 'pointer', 'int'])); 302 | 303 | // traffic in 304 | console.log('attaching [libcocos2dcpp.so!SSL_read]'); 305 | Interceptor.attach(libSymbol('libcocos2dcpp.so!SSL_read'), { 306 | onEnter: (args) => { 307 | this.buffer = ptr(args[1]); 308 | }, 309 | 310 | onLeave: (ret) => { 311 | let data = this.buffer.readUtf8String(ret.toInt32()); 312 | console.raw(data, '\n================================='); 313 | } 314 | }); 315 | } 316 | 317 | /** 318 | * pretend arcara version 319 | */ 320 | function hackPretendArcVersion() { 321 | 322 | console.log('attaching [libcocos2dcpp.so!Java_low_moe_AppActivity_setAppVersion]'); 323 | Interceptor.attach(libSymbol('libcocos2dcpp.so!Java_low_moe_AppActivity_setAppVersion'), { 324 | 325 | onEnter: (args) => { 326 | // replacing the argument 327 | args[2] = jniNewStringUTF(args[0], 328 | Memory.allocUtf8String(config.pretendArcVersion)); 329 | } 330 | 331 | }); 332 | } 333 | 334 | /** 335 | * pretend arcaea device id 336 | */ 337 | function hackPretendDeviceId() { 338 | 339 | console.log('attaching [libcocos2dcpp.so!Java_low_moe_AppActivity_setDeviceId]'); 340 | Interceptor.attach(libSymbol('libcocos2dcpp.so!Java_low_moe_AppActivity_setDeviceId'), { 341 | 342 | onEnter: (args) => { 343 | // replacing the argument 344 | args[2] = jniNewStringUTF(args[0], 345 | Memory.allocUtf8String(config.pretendDeviceId)); 346 | } 347 | 348 | }); 349 | } 350 | 351 | /** 352 | * hook online manager constructor 353 | */ 354 | function hackOnlineManagerCtor() { 355 | 356 | console.log('attaching [libcocos2dcpp.so!OnlineManager::OnlineManager]'); 357 | Interceptor.attach(libSymbol('libcocos2dcpp.so!OnlineManager::OnlineManager'), { 358 | 359 | // save the pointer 360 | onEnter: (args) => { 361 | global.lpOnlineManager = args[0]; 362 | console.info(`lpOnlineManager = ${args[0]}`); 363 | } 364 | 365 | }); 366 | } 367 | 368 | /** 369 | * challenge hook test 370 | */ 371 | function hackChallengeHookTest() { 372 | 373 | // assert native helper loaded 374 | if (!config.useNative) { 375 | console.error('challenge hook test requires libtaikari.so!'); 376 | console.error('please enable the \'useNative\''); 377 | return; 378 | } 379 | 380 | // assert hookOnlineManagerCtor is enabled 381 | if (!checkIfEnabled('hookOnlineManagerCtor')) { 382 | console.error('please enable the \'hookOnlineManagerCtor\'!'); 383 | return; 384 | } 385 | 386 | // hook set favorite character 387 | console.log('attaching [libcocos2dcpp.so!OnlineManager::setFavoriteCharacter]'); 388 | Interceptor.attach(libSymbol('libcocos2dcpp.so!OnlineManager::setFavoriteCharacter'), { 389 | 390 | onEnter: (args) => { 391 | 392 | let _result = onlineManagerSendHttp(global.lpOnlineManager, 393 | 'https://arcapi-v2.lowiro.com/merikuri/17/lxnsnb'); 394 | 395 | console.log('test result:', _result); 396 | } 397 | 398 | }); 399 | } 400 | 401 | /** 402 | * challenge server 403 | */ 404 | function hackChallengeServer() { 405 | 406 | // assert native helper loaded 407 | if (!config.useNative) { 408 | console.error('challenge server requires libtaikari.so!'); 409 | console.error('please enable the \'useNative\''); 410 | return; 411 | } 412 | 413 | // assert http dex loaded 414 | if (!config.useJLHttp) { 415 | console.error('challenge server requires jlhttp.dex!'); 416 | console.error('please enable the \'useJLHttp\''); 417 | return; 418 | } 419 | 420 | // assert challengeHook is not enabled 421 | if (checkIfEnabled('challengeHook')) { 422 | console.error('please disable the \'challengeHook\'!'); 423 | return; 424 | } 425 | 426 | // assert dumpCertficate is not enabled 427 | if (checkIfEnabled('dumpCertficate')) { 428 | console.error('please disable the \'dumpCertficate\'!'); 429 | return; 430 | } 431 | 432 | // assert hookOnlineManagerCtor is enabled 433 | if (!checkIfEnabled('hookOnlineManagerCtor')) { 434 | console.error('please enable the \'hookOnlineManagerCtor\'!'); 435 | return; 436 | } 437 | 438 | let _taskTable = {}; 439 | let _taskIndex = 0; 440 | let _apiPrefix = ""; // https://arcapi-v2.lowiro.com/merikuri/17/ 441 | 442 | // replace easy_perform 443 | console.log('replacing [libcocos2dcpp.so!easy_perform]'); 444 | Interceptor.replace(libSymbol('libcocos2dcpp.so!easy_perform'), 445 | new NativeCallback((ctx, events) => { 446 | 447 | // block the request 448 | return 2; // CURLE_FAILED_INIT 449 | 450 | }, 'int', ['pointer', 'int'])); 451 | 452 | // attach libcocos2dcpp.so!curl_easy_setopt 453 | console.log('attach [libcocos2dcpp.so!curl_easy_setopt]'); 454 | Interceptor.attach(libSymbol('libcocos2dcpp.so!curl_easy_setopt'), { 455 | 456 | onEnter: (args) => { 457 | // CURLOPT_HTTPHEADER 458 | if (args[1] == 0x2727) { 459 | 460 | let _header = curlParseSlist(args[2]); 461 | 462 | let _taskIndex = _header.find((header) => { 463 | if (header.startsWith('Task')) 464 | return true; 465 | }); 466 | 467 | // task index 468 | if (!_taskIndex) return; 469 | _taskIndex = _taskIndex.substr(6).replace(/\s/, ''); 470 | 471 | let _challenge = _header.find((header) => { 472 | if (header.startsWith('X-Random-Challenge')) 473 | return true; 474 | }); 475 | 476 | // challenge string 477 | _challenge = _challenge.substr(20).replace(/\s/, ''); 478 | 479 | // return challenge 480 | _taskTable[_taskIndex].resolve(_challenge); 481 | return; 482 | } 483 | 484 | // CURLOPT_URL 485 | if (args[1] == 0x2712) { 486 | 487 | if (!_apiPrefix) { 488 | let _urlstr = args[2].readUtf8String(); 489 | 490 | // match the result 491 | let _match = _urlstr.match(/https:\/\/(\S.*?\/){3}/); 492 | if (_match.length != 2) { 493 | console.error(`error while detecting, preberly an invalid url. ${_urlstr}`); 494 | return; 495 | } 496 | 497 | // set the result 498 | _apiPrefix = _match[0]; 499 | console.log(`arcapi detected. ${_apiPrefix}`); 500 | } 501 | 502 | return; 503 | } 504 | 505 | } 506 | 507 | }); 508 | 509 | // the online manager has not been 510 | // constructed now, thus wait for 2s. 511 | setTimeout(() => { 512 | // arcapi prefix automatic detection 513 | console.info('auto detecting arcapi...'); 514 | onlineManagerSetFavChar(global.lpOnlineManager, 1); 515 | }, 2000); 516 | 517 | // start the server 518 | Java.perform(() => { 519 | 520 | function createHttpServer(port, routes) { 521 | let _httpServer = Java.use('net.freeutils.httpserver.HTTPServer'); 522 | let _contextHandler = Java.use('net.freeutils.httpserver.HTTPServer$ContextHandler'); 523 | 524 | // create instance 525 | let _server = _httpServer.$new(port); 526 | let _vhost = _server.getVirtualHost(null); 527 | 528 | // disable index generation 529 | _vhost.setAllowGeneratedIndex(false); 530 | 531 | // add routes 532 | routes.forEach(route => { 533 | 534 | // make class name 535 | let _clasName = route.path.split('/').join('.'); 536 | if (_clasName == '.') _clasName = '.index'; 537 | 538 | // implements the interface 539 | let _myHandler = Java.registerClass({ 540 | name: `moe.awa.taikari.handler${_clasName}`, 541 | implements: [_contextHandler], 542 | methods: { 543 | serve: (request, response) => { 544 | 545 | console.log(request.getURI()); 546 | 547 | try { 548 | // replace header 549 | response.getHeaders() 550 | .replace('Arcaea', `${config.arcVersion}`); 551 | 552 | // custom handler 553 | route.handler(request, response); 554 | } 555 | 556 | catch (e) { 557 | console.error(e.stack); 558 | response.send(500, 'Internal Server Error. _(:3) z)_'); 559 | } 560 | 561 | return 0; 562 | } 563 | } 564 | }); 565 | 566 | // add context handler 567 | _vhost.addContext(route.path, _myHandler.$new(), 568 | Java.array('java.lang.String', route.methods)); 569 | }); 570 | 571 | // destroy while reload the script 572 | Script.bindWeak(_server, _ => _server.stop()); 573 | 574 | return _server; 575 | } 576 | 577 | // create a http server 578 | console.log(`http server listening on :${config.challengeHttpPort}. (= w =)Zzz`); 579 | let http = createHttpServer(config.challengeHttpPort, [ 580 | { 581 | path: '/', 582 | methods: ['GET'], 583 | handler: (request, response) => { 584 | 585 | let _fileStream = Java.use("java.io.FileInputStream"); 586 | let _fileCls = Java.use('java.io.File'); 587 | 588 | let _index = `${resFolder('htdoc')}/index.html`; 589 | let _bodyLength = _fileCls.$new(_index).length(); 590 | let _fs = _fileStream.$new(_index); 591 | 592 | response.getHeaders().add('Content-Length', _bodyLength.toString()); 593 | response.sendHeaders(200); 594 | response.sendBody(_fs, _bodyLength, null); 595 | response.close(); 596 | 597 | _fs.close(); 598 | } 599 | 600 | }, 601 | { 602 | path: '/v1/generate', 603 | methods: ['GET'], 604 | handler: (request, response) => { 605 | 606 | // append header 607 | response.getHeaders().add('Content-Type', 'application/json; charset=utf-8'); 608 | 609 | // increase the index 610 | ++_taskIndex; 611 | if (_taskIndex > 10000) _taskIndex = 0; 612 | 613 | // create a new task 614 | let _resolve; 615 | let _promise = new Promise(r => _resolve = r); 616 | let _taskname = _taskIndex.toString(); 617 | 618 | _taskTable[_taskname] = { 619 | task: _promise, 620 | resolve: _resolve, 621 | response: Java.retain(response) // must retain this object 622 | }; 623 | 624 | // check the arguments 625 | let _params = parseJavaMap(request.getParams()); 626 | _params['method'] = _params['method'].toUpperCase(); 627 | 628 | if (!_params['method'] || !_params['path']) { 629 | response.send(200, JSON.stringify({ status: -1, message: 'lack arguments.' })); 630 | return; 631 | } 632 | 633 | // check the post body 634 | if (_params['method'] == 'POST' && !_params['body']) { 635 | response.send(200, JSON.stringify({ status: -2, message: 'lack argument \'body\'.' })); 636 | return; 637 | } 638 | 639 | // send http request 640 | onlineManagerSendHttp(global.lpOnlineManager, 641 | `${_apiPrefix}${decodeURIComponent(_params['path'])}`, 642 | `Task: ${_taskname}`, _params['method'], 643 | _params['method'] == 'POST' ? decodeURIComponent(_params['body']) : ''); 644 | 645 | // wait for promise 646 | _promise.then((data) => { 647 | 648 | try { 649 | Java.perform(_ => { 650 | let _response = _taskTable[_taskname].response; 651 | _response.send(200, JSON.stringify({ status: 0, content: { challenge: data } })); 652 | _response.close(); 653 | }) 654 | } catch (e) { 655 | console.log(e.stack); 656 | } 657 | }); 658 | 659 | } 660 | } 661 | ]); 662 | 663 | http.start(); 664 | }); 665 | 666 | } 667 | 668 | ////// Utils 669 | ///////////////////////////////////////////////////////// 670 | 671 | function taiSupported() { 672 | return !(config.libSymbols[getArcaeaVersion()]) == false; 673 | } 674 | 675 | /** 676 | * Send a http request 677 | * @param {*} lpthis instance of OnlineManager 678 | * @param {string} url url to access 679 | * @param {string} method http method GET or POST 680 | * @param {string} body POST body 681 | * @returns 682 | */ 683 | function onlineManagerSendHttp(lpthis, url, headers = '', method = 'GET', body = '') { 684 | 685 | // native help function 686 | let _helpfunc = libSymbol('libtaikari.so!sendHttpRequest'); 687 | _helpfunc = new NativeFunction(_helpfunc, 'int64', ['pointer', 'pointer', 'pointer', 'int', 'pointer', 'pointer']); 688 | 689 | // send function 690 | let _callfunc = libSymbol('libcocos2dcpp.so!OnlineManager::sendHttpRequest'); 691 | 692 | // prepare resources 693 | let _url = Memory.allocUtf8String(url); 694 | let _method = method.toUpperCase() == 'GET' ? 0x00 : 0x01; 695 | let _headers = Memory.allocUtf8String(headers); 696 | let _postbody = Memory.allocUtf8String(body); 697 | 698 | // call send http request 699 | return _helpfunc(lpthis, _callfunc, _url, _method, _headers, _postbody); 700 | } 701 | 702 | /** 703 | * Set favirate character 704 | */ 705 | function onlineManagerSetFavChar(lpthis, cid) { 706 | 707 | // native help function 708 | let _helpfunc = libSymbol('libtaikari.so!setFavoriteCharacter'); 709 | _helpfunc = new NativeFunction(_helpfunc, 'int64', ['pointer', 'pointer', 'int']); 710 | 711 | // calling function 712 | let _callfunc = libSymbol('libcocos2dcpp.so!OnlineManager::setFavoriteCharacter'); 713 | 714 | // call set favorite character 715 | return _helpfunc(lpthis, _callfunc, cid); 716 | } 717 | 718 | /** 719 | * Check if a hack tool enabled 720 | * @param {string} name name of a hack tool 721 | */ 722 | function checkIfEnabled(name) { 723 | let enabled = false; 724 | 725 | config.hackTools.forEach((tool) => { 726 | if (tool.name == name) enabled = tool.enabled; 727 | }); 728 | 729 | return enabled; 730 | } 731 | 732 | /** 733 | * Find symbol 734 | * @param {string} name function name 735 | * @returns NativePointer 736 | */ 737 | function libSymbol(name) { 738 | 739 | let _split = name.split('!'); 740 | if (_split.length != 2) return null; 741 | 742 | let _procName = _split[1]; 743 | let _moduleName = _split[0]; 744 | let _procAddress; 745 | 746 | // proc address from frida 747 | _procAddress = Module.findExportByName(_moduleName, _procName); 748 | if (_procAddress instanceof NativePointer) return _procAddress; 749 | 750 | // module base 751 | let _moduleBase = Module.getBaseAddress(_moduleName); 752 | if (!_moduleBase) return null; 753 | 754 | // proc address from config 755 | config.libSymbols[config.arcVersion].forEach((def) => { 756 | if (def.name == name) _procAddress = def.proc; 757 | }); 758 | 759 | if (_procAddress != 0) return _moduleBase.add(_procAddress); 760 | return null; 761 | } 762 | 763 | /** 764 | * Get resource folder 765 | * @param {string} name 766 | */ 767 | function resFolder(name) { 768 | return config.resFolder[name]; 769 | } 770 | 771 | function getArcaeaVersion() { 772 | 773 | let _arcver = ''; 774 | let _arcbuild = ''; 775 | let _architecture = ''; 776 | 777 | // arcaea version 778 | Java.perform(() => { 779 | let _buildConf = Java.use('moe.low.arcdev.BuildConfig'); 780 | _arcver = _buildConf.class.getDeclaredField('VERSION_NAME').get(null); 781 | _arcbuild = _buildConf.class.getDeclaredField('VERSION_CODE').get(null); 782 | }); 783 | 784 | // android architecture 785 | Java.perform(() => { 786 | let _osBuild = Java.use('android.os.Build'); 787 | _architecture = _osBuild.class.getDeclaredField('CPU_ABI').get(null); 788 | }); 789 | 790 | return `${_arcver}_${_arcbuild}_${_architecture}`; 791 | } 792 | 793 | /** 794 | * parse curl slist 795 | * @param {NativePointer} lpslist 796 | */ 797 | function curlParseSlist(lpslist) { 798 | 799 | // struct curl_slist { 800 | // char* data; 801 | // curl_slist* next; 802 | // } 803 | 804 | let _slist = lpslist; 805 | let _data = _slist.readPointer(); 806 | let _next = _slist.add(Process.pointerSize).readPointer(); 807 | let _result = []; 808 | 809 | // enumerate the linked table 810 | while (!_next.isNull()) { 811 | _result.push(_data.readUtf8String()); 812 | 813 | // next 814 | _slist = _next; 815 | _data = _slist.readPointer(); 816 | _next = _slist.add(Process.pointerSize).readPointer(); 817 | } 818 | 819 | // dont forget the last one 820 | return _result.concat(_data.readUtf8String()); 821 | } 822 | 823 | /** 824 | * parse java map 825 | * @param map 826 | */ 827 | function parseJavaMap(map) { 828 | var _keys = map.keySet(); 829 | var _itor = _keys.iterator(); 830 | var _array = {}; 831 | 832 | while (_itor.hasNext()) { 833 | var _key = _itor.next(); 834 | _array[_key.toString()] = map.get(_key).toString(); 835 | } 836 | return _array; 837 | } 838 | 839 | /** 840 | * New string utf 841 | * @param env JNI env 842 | * @param str string 843 | * @returns 844 | */ 845 | function jniNewStringUTF(env, str) { 846 | const _jniIndex = 167; 847 | const _funcAddress = env.readPointer().add(_jniIndex * Process.pointerSize).readPointer(); 848 | let newStringUTF = new NativeFunction(_funcAddress, 'pointer', ['pointer', 'pointer']); 849 | 850 | return newStringUTF(env, str); 851 | } 852 | --------------------------------------------------------------------------------