├── .github └── workflows │ └── build.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── VC-LTL5.lua ├── src ├── PakFile.h ├── PakPatch.h ├── TabBookmark.h ├── appid.h ├── chrome++.cpp ├── chrome++.rc ├── fastsearch.h ├── green.h ├── hijack.h ├── portable.h ├── utils.h └── version.h └── xmake.lua /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | 3 | on: 4 | push: 5 | branches: [ main ] 6 | pull_request: 7 | branches: [ main ] 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | include: [ 14 | { name: windows_x86, arch: x86 }, 15 | { name: windows_x64, arch: x64 }, 16 | ] 17 | 18 | name: ${{ matrix.name }} 19 | 20 | runs-on: windows-latest 21 | 22 | steps: 23 | - name: checkout 24 | uses: actions/checkout@v2 25 | with: 26 | submodules: 'true' 27 | 28 | - name: setup VC-LTL 29 | working-directory: ${{env.GITHUB_WORKSPACE}} 30 | run: nuget install VC-LTL 31 | 32 | - name: setup xmake 33 | uses: xmake-io/github-action-setup-xmake@v1 34 | 35 | - name: configure 36 | run: xmake f -a ${{ matrix.arch }} 37 | 38 | - name: build 39 | run: xmake 40 | 41 | - name: upload 42 | uses: actions/upload-artifact@v3 43 | with: 44 | name: ${{ matrix.name }} 45 | path: build/release 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .xmake/ 2 | build/ 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "minhook"] 2 | path = minhook 3 | url = https://github.com/TsudaKageyu/minhook 4 | [submodule "mini_gzip"] 5 | path = mini_gzip 6 | url = https://github.com/shuax/mini_gzip 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 shuax 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 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 功能 2 | - 双击关闭标签页 3 | - 保留最后标签页(防止关闭最后一个标签页时关闭浏览器,点X不行) 4 | - 鼠标悬停标签栏滚动 5 | - 按住右键时滚轮滚动标签栏 6 | - 便携设计,程序放在App目录,数据放在Data目录(不兼容原版数据,可以重装系统换电脑不丢数据) 7 | - 移除更新错误警告(因为是绿色版没有自动更新功能) 8 | # 获取 9 | 采用GitHub Actions自动编译发布,下载地址:[https://nightly.link/shuax/chrome_plus/workflows/build/main](https://nightly.link/shuax/chrome_plus/workflows/build/main) 10 | 11 | [![build status](https://github.com/shuax/chrome_plus/actions/workflows/build.yml/badge.svg)](https://github.com/shuax/chrome_plus/actions/workflows/build.yml) 12 | # 安装 13 | dll放入解压版Chrome目录即可 14 | -------------------------------------------------------------------------------- /VC-LTL5.lua: -------------------------------------------------------------------------------- 1 | target("VC-LTL-5") 2 | set_kind("phony") 3 | before_build("windows", function (target) 4 | local function find_in_file() 5 | for _, dir in ipairs(os.dirs("$(projectdir)/*")) do 6 | name = dir:match(".*\\(.*)") 7 | if name:find("VC%-LTL") then 8 | return dir .. [[\build\native\]] 9 | end 10 | end 11 | end 12 | local function find_in_reg() 13 | return vformat("$(reg HKEY_CURRENT_USER\\Code\\VC-LTL;Root)") 14 | end 15 | local VC_LTL_Root = find_in_file() or find_in_reg() 16 | if #VC_LTL_Root==0 then 17 | return 18 | end 19 | local WindowsTargetPlatformMinVersion = "6.0.6000.0" 20 | cprint("${color.warning}VC-LTL Path : %s", VC_LTL_Root) 21 | cprint("${color.warning}WindowsTargetPlatformMinVersion : %s", WindowsTargetPlatformMinVersion) 22 | import("core.tool.toolchain") 23 | local msvc = toolchain.load("msvc") 24 | local runenvs = msvc:runenvs() 25 | 26 | local includepath = VC_LTL_Root .. [[TargetPlatform\header;]] .. VC_LTL_Root .. [[TargetPlatform\]] .. WindowsTargetPlatformMinVersion..[[\header;]] 27 | 28 | runenvs.INCLUDE = includepath .. runenvs.INCLUDE 29 | 30 | local arch = target:arch() 31 | local archpath = "Win32" 32 | if arch=="x86" then 33 | archpath = "Win32" 34 | elseif arch=="x64" then 35 | archpath = "x64" 36 | end 37 | cprint("${color.warning}Platform : %s", archpath) 38 | local libpath = VC_LTL_Root .. [[TargetPlatform\]] .. WindowsTargetPlatformMinVersion..[[\lib\]] .. archpath .. ";" 39 | 40 | runenvs.LIB = libpath .. runenvs.LIB 41 | 42 | -- print(runenvs.INCLUDE) 43 | -- print(runenvs.LIB) 44 | end) 45 | -------------------------------------------------------------------------------- /src/PakFile.h: -------------------------------------------------------------------------------- 1 | #pragma warning(disable: 4334) 2 | #pragma warning(disable: 4267) 3 | 4 | extern "C" 5 | { 6 | #include "..\mini_gzip\miniz.c" 7 | #include "..\mini_gzip\mini_gzip.h" 8 | #include "..\mini_gzip\mini_gzip.c" 9 | } 10 | 11 | #pragma pack(push) 12 | #pragma pack(1) 13 | 14 | #define PACK4_FILE_VERSION (4) 15 | #define PACK5_FILE_VERSION (5) 16 | 17 | struct PAK4_HEADER 18 | { 19 | uint32_t num_entries; 20 | uint8_t encodeing; 21 | }; 22 | 23 | struct PAK5_HEADER 24 | { 25 | uint32_t encodeing; 26 | uint16_t resource_count; 27 | uint16_t alias_count; 28 | }; 29 | 30 | struct PAK_ENTRY 31 | { 32 | uint16_t resource_id; 33 | uint32_t file_offset; 34 | }; 35 | 36 | struct PAK_ALIAS 37 | { 38 | uint16_t resource_id; 39 | uint16_t entry_index; 40 | }; 41 | #pragma pack(pop) 42 | 43 | bool CheckHeader(uint8_t *buffer, PAK_ENTRY *&pak_entry, PAK_ENTRY *&end_entry) 44 | { 45 | uint32_t version = *(uint32_t*)buffer; 46 | 47 | if (version != PACK4_FILE_VERSION && version != PACK5_FILE_VERSION) return false; 48 | 49 | if (version == PACK4_FILE_VERSION) 50 | { 51 | PAK4_HEADER *pak_header = (PAK4_HEADER*)(buffer + sizeof(uint32_t)); 52 | if (pak_header->encodeing != 1) return false; 53 | 54 | pak_entry = (PAK_ENTRY*)(buffer + sizeof(uint32_t) + sizeof(PAK4_HEADER)); 55 | end_entry = pak_entry + pak_header->num_entries; 56 | } 57 | 58 | if (version == PACK5_FILE_VERSION) 59 | { 60 | PAK5_HEADER *pak_header = (PAK5_HEADER*)(buffer + sizeof(uint32_t)); 61 | if (pak_header->encodeing != 1) return false; 62 | 63 | pak_entry = (PAK_ENTRY*)(buffer + sizeof(uint32_t) + sizeof(PAK5_HEADER)); 64 | end_entry = pak_entry + pak_header->resource_count; 65 | } 66 | 67 | // 为了保存最后一条的"下一条",这条特殊的条目的id一定为0 68 | if (!end_entry || end_entry->resource_id != 0) return false; 69 | 70 | return true; 71 | } 72 | 73 | template 74 | void PakFind(uint8_t *buffer, uint8_t* pos, Function f) 75 | { 76 | PAK_ENTRY *pak_entry = NULL; 77 | PAK_ENTRY *end_entry = NULL; 78 | 79 | // 检查文件头 80 | if (!CheckHeader(buffer, pak_entry, end_entry)) return; 81 | 82 | do 83 | { 84 | PAK_ENTRY *next_entry = pak_entry + 1; 85 | if (pos >= buffer + pak_entry->file_offset && pos <= buffer + next_entry->file_offset) 86 | { 87 | f(buffer + pak_entry->file_offset, next_entry->file_offset - pak_entry->file_offset); 88 | break; 89 | } 90 | 91 | pak_entry = next_entry; 92 | } while (pak_entry->resource_id != 0); 93 | } 94 | 95 | template 96 | void TraversalGZIPFile(uint8_t *buffer, Function f) 97 | { 98 | PAK_ENTRY *pak_entry = NULL; 99 | PAK_ENTRY *end_entry = NULL; 100 | 101 | // 检查文件头 102 | if (!CheckHeader(buffer, pak_entry, end_entry)) return; 103 | 104 | do 105 | { 106 | PAK_ENTRY *next_entry = pak_entry + 1; 107 | uint32_t old_size = next_entry->file_offset - pak_entry->file_offset; 108 | 109 | if (old_size < 10 * 1024) 110 | { 111 | // 小于10k文件跳过 112 | pak_entry = next_entry; 113 | continue; 114 | } 115 | 116 | BYTE gzip[] = { 0x1F, 0x8B, 0x08 }; 117 | size_t gzip_len = sizeof(gzip); 118 | if (memcmp(buffer + pak_entry->file_offset, gzip, gzip_len) != 0) 119 | { 120 | // 不是gzip文件跳过 121 | pak_entry = next_entry; 122 | continue; 123 | } 124 | 125 | uint32_t original_size = *(uint32_t*)(buffer + next_entry->file_offset - 4); 126 | uint8_t *unpack_buffer = (uint8_t *)malloc(original_size); 127 | if (!unpack_buffer) return; 128 | 129 | struct mini_gzip gz; 130 | mini_gz_start(&gz, buffer + pak_entry->file_offset, old_size); 131 | int unpack_len = mini_gz_unpack(&gz, unpack_buffer, original_size); 132 | 133 | if (original_size == unpack_len) 134 | { 135 | uint32_t new_len = old_size; 136 | bool changed = f(unpack_buffer, unpack_len, new_len); 137 | if (changed) 138 | { 139 | // 如果有改变 140 | size_t compress_size = 0; 141 | uint8_t *compress_buffer = (uint8_t *)gzip_compress(unpack_buffer, new_len, &compress_size); 142 | if (compress_buffer && compress_size < old_size) 143 | { 144 | /*FILE *fp = fopen("test.gz", "wb"); 145 | fwrite(compress_buffer, compress_size, 1, fp); 146 | fclose(fp);*/ 147 | 148 | //gzip头 149 | memcpy(buffer + pak_entry->file_offset, compress_buffer, 10); 150 | 151 | //extra 152 | buffer[pak_entry->file_offset + 3] = 0x04; 153 | uint16_t extra_length = old_size - compress_size - 2; 154 | memcpy(buffer + pak_entry->file_offset + 10, &extra_length, sizeof(extra_length)); 155 | memset(buffer + pak_entry->file_offset + 12, '\0', extra_length); 156 | 157 | //compress 158 | memcpy(buffer + pak_entry->file_offset + 12 + extra_length, compress_buffer + 10, compress_size - 10); 159 | 160 | /*fp = fopen("test2.gz", "wb"); 161 | fwrite(buffer + pak_entry->file_offset, old_size, 1, fp); 162 | fclose(fp);*/ 163 | } 164 | else 165 | { 166 | DebugLog(L"gzip compress error %d %d", compress_size, old_size); 167 | } 168 | 169 | if (compress_buffer) free(compress_buffer); 170 | } 171 | } 172 | 173 | free(unpack_buffer); 174 | pak_entry = next_entry; 175 | } while (pak_entry->resource_id != 0); 176 | } 177 | -------------------------------------------------------------------------------- /src/PakPatch.h: -------------------------------------------------------------------------------- 1 | #include "PakFile.h" 2 | 3 | DWORD resources_pak_size = 0; 4 | 5 | HANDLE resources_pak_map = NULL; 6 | 7 | typedef HANDLE(WINAPI *pMapViewOfFile)( 8 | _In_ HANDLE hFileMappingObject, 9 | _In_ DWORD dwDesiredAccess, 10 | _In_ DWORD dwFileOffsetHigh, 11 | _In_ DWORD dwFileOffsetLow, 12 | _In_ SIZE_T dwNumberOfBytesToMap 13 | ); 14 | 15 | pMapViewOfFile RawMapViewOfFile = NULL; 16 | 17 | HANDLE WINAPI MyMapViewOfFile( 18 | _In_ HANDLE hFileMappingObject, 19 | _In_ DWORD dwDesiredAccess, 20 | _In_ DWORD dwFileOffsetHigh, 21 | _In_ DWORD dwFileOffsetLow, 22 | _In_ SIZE_T dwNumberOfBytesToMap 23 | ) 24 | { 25 | if (hFileMappingObject == resources_pak_map) 26 | { 27 | // 修改属性为可修改 28 | LPVOID buffer = RawMapViewOfFile(hFileMappingObject, FILE_MAP_COPY, dwFileOffsetHigh, 29 | dwFileOffsetLow, dwNumberOfBytesToMap); 30 | 31 | // 不再需要hook 32 | resources_pak_map = NULL; 33 | MH_DisableHook(MapViewOfFile); 34 | 35 | if (buffer) 36 | { 37 | // 遍历gzip文件 38 | TraversalGZIPFile((BYTE*)buffer, [=](uint8_t *begin, uint32_t size, uint32_t &new_len) { 39 | bool changed = false; 40 | 41 | BYTE search_start[] = R"()"; 42 | uint8_t* pos = memmem(begin, size, search_start, sizeof(search_start) - 1); 43 | if (pos) 44 | { 45 | 46 | // 压缩HTML以备写入补丁信息 47 | std::string html((char*)begin, size); 48 | compression_html(html); 49 | 50 | // RemoveUpdateError 51 | //if (IsNeedPortable()) 52 | { 53 | ReplaceStringInPlace(html, R"(hidden="[[!showUpdateStatus_]]")", R"(hidden="true")"); 54 | ReplaceStringInPlace(html, R"(hidden="[[!shouldShowIcons_(showUpdateStatus_)]]")", R"(hidden="true")"); 55 | } 56 | 57 | const char prouct_title[] = u8R"({aboutBrowserVersion}
Chrome++ )" RELEASE_VER_STR u8R"( inside
)"; 58 | ReplaceStringInPlace(html, R"({aboutBrowserVersion})", prouct_title); 59 | 60 | if (html.length() <= size) 61 | { 62 | // 写入修改 63 | memcpy(begin, html.c_str(), html.length()); 64 | 65 | // 修改长度 66 | new_len = html.length(); 67 | changed = true; 68 | } 69 | } 70 | 71 | return changed; 72 | }); 73 | 74 | } 75 | 76 | return buffer; 77 | } 78 | 79 | return RawMapViewOfFile(hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh, 80 | dwFileOffsetLow, dwNumberOfBytesToMap); 81 | } 82 | 83 | HANDLE resources_pak_file = NULL; 84 | 85 | typedef HANDLE(WINAPI *pCreateFileMapping)( 86 | _In_ HANDLE hFile, 87 | _In_opt_ LPSECURITY_ATTRIBUTES lpAttributes, 88 | _In_ DWORD flProtect, 89 | _In_ DWORD dwMaximumSizeHigh, 90 | _In_ DWORD dwMaximumSizeLow, 91 | _In_opt_ LPCTSTR lpName 92 | ); 93 | 94 | pCreateFileMapping RawCreateFileMapping = NULL; 95 | 96 | HANDLE WINAPI MyCreateFileMapping( 97 | _In_ HANDLE hFile, 98 | _In_opt_ LPSECURITY_ATTRIBUTES lpAttributes, 99 | _In_ DWORD flProtect, 100 | _In_ DWORD dwMaximumSizeHigh, 101 | _In_ DWORD dwMaximumSizeLow, 102 | _In_opt_ LPCTSTR lpName 103 | ) 104 | { 105 | if (hFile == resources_pak_file) 106 | { 107 | // 修改属性为可修改 108 | resources_pak_map = RawCreateFileMapping(hFile, lpAttributes, PAGE_WRITECOPY, 109 | dwMaximumSizeHigh, dwMaximumSizeLow, lpName); 110 | 111 | // 不再需要hook 112 | resources_pak_file = NULL; 113 | MH_DisableHook(CreateFileMappingW); 114 | 115 | if (MH_CreateHook(MapViewOfFile, MyMapViewOfFile, (LPVOID*)&RawMapViewOfFile) == MH_OK) 116 | { 117 | MH_EnableHook(MapViewOfFile); 118 | } 119 | 120 | return resources_pak_map; 121 | } 122 | return RawCreateFileMapping(hFile, lpAttributes, flProtect, dwMaximumSizeHigh, 123 | dwMaximumSizeLow, lpName); 124 | } 125 | 126 | typedef HANDLE(WINAPI *pCreateFile)( 127 | _In_ LPCTSTR lpFileName, 128 | _In_ DWORD dwDesiredAccess, 129 | _In_ DWORD dwShareMode, 130 | _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, 131 | _In_ DWORD dwCreationDisposition, 132 | _In_ DWORD dwFlagsAndAttributes, 133 | _In_opt_ HANDLE hTemplateFile 134 | ); 135 | 136 | pCreateFile RawCreateFile = NULL; 137 | 138 | HANDLE WINAPI MyCreateFile( 139 | _In_ LPCTSTR lpFileName, 140 | _In_ DWORD dwDesiredAccess, 141 | _In_ DWORD dwShareMode, 142 | _In_opt_ LPSECURITY_ATTRIBUTES lpSecurityAttributes, 143 | _In_ DWORD dwCreationDisposition, 144 | _In_ DWORD dwFlagsAndAttributes, 145 | _In_opt_ HANDLE hTemplateFile 146 | ) 147 | { 148 | HANDLE file = RawCreateFile(lpFileName, dwDesiredAccess, dwShareMode, 149 | lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, 150 | hTemplateFile); 151 | 152 | if (isEndWith(lpFileName, L"resources.pak")) 153 | { 154 | resources_pak_file = file; 155 | resources_pak_size = GetFileSize(resources_pak_file, NULL); 156 | 157 | if (MH_CreateHook(CreateFileMappingW, MyCreateFileMapping, (LPVOID*)&RawCreateFileMapping) == MH_OK) 158 | { 159 | MH_EnableHook(CreateFileMappingW); 160 | } 161 | 162 | // 不再需要hook 163 | MH_DisableHook(CreateFileW); 164 | } 165 | 166 | return file; 167 | } 168 | 169 | 170 | void PakPatch() 171 | { 172 | MH_STATUS status = MH_CreateHook(CreateFileW, MyCreateFile, (LPVOID*)&RawCreateFile); 173 | if (status == MH_OK) 174 | { 175 | MH_EnableHook(CreateFileW); 176 | } 177 | else 178 | { 179 | DebugLog(L"MH_CreateHook CreateFileW failed:%d", status); 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/TabBookmark.h: -------------------------------------------------------------------------------- 1 | #include 2 | #pragma comment(lib,"oleacc.lib") 3 | 4 | #include 5 | #include 6 | 7 | HHOOK mouse_hook = NULL; 8 | 9 | using NodePtr = Microsoft::WRL::ComPtr; 10 | 11 | #define KEY_PRESSED 0x8000 12 | bool IsPressed(int key) 13 | { 14 | return key && (::GetKeyState(key) & KEY_PRESSED) != 0; 15 | } 16 | 17 | long GetAccessibleRole(NodePtr node) 18 | { 19 | VARIANT self; 20 | self.vt = VT_I4; 21 | self.lVal = CHILDID_SELF; 22 | 23 | VARIANT role; 24 | if (S_OK == node->get_accRole(self, &role)) 25 | { 26 | if (role.vt == VT_I4) 27 | { 28 | return role.lVal; 29 | } 30 | } 31 | return 0; 32 | } 33 | 34 | long GetAccessibleState(NodePtr node) 35 | { 36 | VARIANT self; 37 | self.vt = VT_I4; 38 | self.lVal = CHILDID_SELF; 39 | 40 | VARIANT state; 41 | if (S_OK == node->get_accState(self, &state)) 42 | { 43 | if (state.vt == VT_I4) 44 | { 45 | return state.lVal; 46 | } 47 | } 48 | return 0; 49 | } 50 | 51 | template 52 | void TraversalAccessible(NodePtr node, Function f) 53 | { 54 | long childCount = 0; 55 | if (node && S_OK == node->get_accChildCount(&childCount) && childCount) 56 | { 57 | VARIANT* varChildren = (VARIANT*)malloc(sizeof(VARIANT) * childCount); 58 | if (S_OK == AccessibleChildren(node.Get(), 0, childCount, varChildren, &childCount)) 59 | { 60 | for (int i = 0; i < childCount; i++) 61 | { 62 | if (varChildren[i].vt == VT_DISPATCH) 63 | { 64 | Microsoft::WRL::ComPtr dispatch = varChildren[i].pdispVal; 65 | NodePtr child = nullptr; 66 | if (S_OK == dispatch->QueryInterface(IID_IAccessible, (void**)&child)) 67 | { 68 | if ((GetAccessibleState(child) & STATE_SYSTEM_INVISIBLE) == 0) // 只遍历可见节点 69 | { 70 | if (f(child)) 71 | { 72 | break; 73 | } 74 | } 75 | } 76 | } 77 | } 78 | } 79 | free(varChildren); 80 | } 81 | } 82 | 83 | template 84 | void TraversalRawAccessible(NodePtr node, Function f) 85 | { 86 | long childCount = 0; 87 | if (node && S_OK == node->get_accChildCount(&childCount) && childCount) 88 | { 89 | VARIANT* varChildren = (VARIANT*)malloc(sizeof(VARIANT) * childCount); 90 | if (S_OK == AccessibleChildren(node.Get(), 0, childCount, varChildren, &childCount)) 91 | { 92 | for (int i = 0; i < childCount; i++) 93 | { 94 | if (varChildren[i].vt == VT_DISPATCH) 95 | { 96 | Microsoft::WRL::ComPtr dispatch = varChildren[i].pdispVal; 97 | NodePtr child = nullptr; 98 | if (S_OK == dispatch->QueryInterface(IID_IAccessible, (void**)&child)) 99 | { 100 | //if ((GetAccessibleState(child) & STATE_SYSTEM_INVISIBLE) == 0) // 只遍历可见节点 101 | { 102 | if (f(child)) 103 | { 104 | break; 105 | } 106 | } 107 | } 108 | } 109 | } 110 | } 111 | free(varChildren); 112 | } 113 | } 114 | 115 | NodePtr FindPageTabList(NodePtr node) 116 | { 117 | NodePtr PageTabList = nullptr; 118 | if (node) 119 | { 120 | TraversalAccessible(node, [&] 121 | (NodePtr child) 122 | { 123 | long role = GetAccessibleRole(child); 124 | if (role == ROLE_SYSTEM_PAGETABLIST) 125 | { 126 | PageTabList = child; 127 | } 128 | else if (role == ROLE_SYSTEM_PANE || role == ROLE_SYSTEM_TOOLBAR) 129 | { 130 | PageTabList = FindPageTabList(child); 131 | } 132 | return PageTabList; 133 | }); 134 | } 135 | return PageTabList; 136 | } 137 | 138 | 139 | NodePtr FindPageTab(NodePtr node) 140 | { 141 | NodePtr PageTab = nullptr; 142 | if (node) 143 | { 144 | TraversalAccessible(node, [&] 145 | (NodePtr child) 146 | { 147 | long role = GetAccessibleRole(child); 148 | if (role == ROLE_SYSTEM_PAGETAB) 149 | { 150 | PageTab = child; 151 | } 152 | else if (role == ROLE_SYSTEM_PANE || role == ROLE_SYSTEM_TOOLBAR) 153 | { 154 | PageTab = FindPageTab(child); 155 | } 156 | return PageTab; 157 | }); 158 | } 159 | return PageTab; 160 | } 161 | 162 | NodePtr GetParentElement(NodePtr child) 163 | { 164 | NodePtr element = nullptr; 165 | Microsoft::WRL::ComPtr dispatch = nullptr; 166 | if (S_OK == child->get_accParent(&dispatch) && dispatch) 167 | { 168 | NodePtr parent = nullptr; 169 | if (S_OK == dispatch->QueryInterface(IID_IAccessible, (void**)&parent)) 170 | { 171 | element = parent; 172 | } 173 | } 174 | return element; 175 | } 176 | 177 | NodePtr GetTopContainerView(HWND hwnd) 178 | { 179 | NodePtr TopContainerView = nullptr; 180 | wchar_t name[MAX_PATH]; 181 | if (GetClassName(hwnd, name, MAX_PATH) && wcscmp(name, L"Chrome_WidgetWin_1") == 0) 182 | { 183 | NodePtr paccMainWindow = nullptr; 184 | if (S_OK == AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_PPV_ARGS(&paccMainWindow))) 185 | { 186 | NodePtr PageTabList = FindPageTabList(paccMainWindow); 187 | if (PageTabList) 188 | { 189 | TopContainerView = GetParentElement(PageTabList); 190 | } 191 | if (!TopContainerView) 192 | { 193 | DebugLog(L"FindTopContainerView failed"); 194 | } 195 | } 196 | } 197 | return TopContainerView; 198 | } 199 | 200 | NodePtr FindChildElement(NodePtr parent, long role, int skipcount = 0) 201 | { 202 | NodePtr element = nullptr; 203 | if (parent) 204 | { 205 | int i = 0; 206 | TraversalAccessible(parent, [&element, &role, &i, &skipcount] 207 | (NodePtr child) 208 | { 209 | //DebugLog(L"当前 %d,%d", i, skipcount); 210 | if (GetAccessibleRole(child) == role) 211 | { 212 | if (i == skipcount) 213 | { 214 | element = child; 215 | } 216 | i++; 217 | } 218 | return element != nullptr; 219 | }); 220 | } 221 | return element; 222 | } 223 | 224 | template 225 | void GetAccessibleSize(NodePtr node, Function f) 226 | { 227 | VARIANT self; 228 | self.vt = VT_I4; 229 | self.lVal = CHILDID_SELF; 230 | 231 | RECT rect; 232 | if (S_OK == node->accLocation(&rect.left, &rect.top, &rect.right, &rect.bottom, self)) 233 | { 234 | //rect.left = (int)((float)rect.left); 235 | //rect.top = (int)((float)rect.top); 236 | //rect.right = (int)((float)rect.right); 237 | //rect.bottom = (int)((float)rect.bottom); 238 | 239 | rect.right += rect.left; 240 | rect.bottom += rect.top; 241 | 242 | f(rect); 243 | } 244 | } 245 | 246 | // 鼠标是否在某个标签上 247 | bool IsOnOneTab(NodePtr top, POINT pt) 248 | { 249 | bool flag = false; 250 | NodePtr PageTabList = FindPageTabList(top); 251 | if (PageTabList) 252 | { 253 | NodePtr PageTab = FindPageTab(PageTabList); 254 | if (PageTab) 255 | { 256 | NodePtr PageTabPane = GetParentElement(PageTab); 257 | if (PageTabPane) 258 | { 259 | TraversalAccessible(PageTabPane, [&flag, &pt] 260 | (NodePtr child) 261 | { 262 | if (GetAccessibleRole(child) == ROLE_SYSTEM_PAGETAB) 263 | { 264 | GetAccessibleSize(child, [&flag, &pt] 265 | (RECT rect) 266 | { 267 | if (PtInRect(&rect, pt)) 268 | { 269 | flag = true; 270 | } 271 | }); 272 | } 273 | return flag; 274 | }); 275 | } 276 | } 277 | } 278 | else 279 | { 280 | // if (top) DebugLog(L"IsOnOneTab failed"); 281 | } 282 | return flag; 283 | } 284 | 285 | // 是否只有一个标签 286 | bool IsOnlyOneTab(NodePtr top) 287 | { 288 | NodePtr PageTabList = FindPageTabList(top); 289 | if (PageTabList) 290 | { 291 | //DebugLog(L"IsOnlyOneTab"); 292 | long tab_count = 0; 293 | 294 | NodePtr PageTab = FindPageTab(PageTabList); 295 | if (PageTab) 296 | { 297 | NodePtr PageTabPane = GetParentElement(PageTab); 298 | if (PageTabPane) 299 | { 300 | TraversalAccessible(PageTabPane, [&tab_count] 301 | (NodePtr child) 302 | { 303 | //if (GetAccessibleRole(child) == ROLE_SYSTEM_PAGETAB && GetChildCount(child)) 304 | if (GetAccessibleRole(child) == ROLE_SYSTEM_PAGETAB) 305 | { 306 | tab_count++; 307 | } 308 | return false; 309 | }); 310 | } 311 | } 312 | //DebugLog(L"closing %d,%d", closing, tab_count); 313 | return tab_count <= 1; 314 | } 315 | else 316 | { 317 | // if (top) DebugLog(L"IsOnlyOneTab failed"); 318 | } 319 | return false; 320 | } 321 | 322 | // 鼠标是否在标签栏上 323 | bool IsOnTheTab(NodePtr top, POINT pt) 324 | { 325 | bool flag = false; 326 | NodePtr PageTabList = FindPageTabList(top); 327 | if (PageTabList) 328 | { 329 | GetAccessibleSize(PageTabList, [&flag, &pt] 330 | (RECT rect) 331 | { 332 | if (PtInRect(&rect, pt)) 333 | { 334 | flag = true; 335 | } 336 | }); 337 | } 338 | else 339 | { 340 | // if (top) DebugLog(L"IsOnTheTab failed"); 341 | } 342 | return flag; 343 | } 344 | 345 | LRESULT CALLBACK MouseProc(int nCode, WPARAM wParam, LPARAM lParam) 346 | { 347 | static bool wheel_tab_ing = false; 348 | static bool double_click_ing = false; 349 | 350 | if (nCode != HC_ACTION) 351 | { 352 | return CallNextHookEx(mouse_hook, nCode, wParam, lParam); 353 | } 354 | 355 | if (nCode == HC_ACTION) 356 | { 357 | PMOUSEHOOKSTRUCT pmouse = (PMOUSEHOOKSTRUCT)lParam; 358 | 359 | if (wParam == WM_MOUSEMOVE || wParam == WM_NCMOUSEMOVE) 360 | { 361 | return CallNextHookEx(mouse_hook, nCode, wParam, lParam); 362 | } 363 | 364 | if (pmouse->dwExtraInfo == MAGIC_CODE) 365 | { 366 | //DebugLog(L"MAGIC_CODE %x", wParam); 367 | goto next; 368 | } 369 | 370 | if (wParam == WM_RBUTTONUP && wheel_tab_ing) 371 | { 372 | //DebugLog(L"wheel_tab_ing"); 373 | wheel_tab_ing = false; 374 | return 1; 375 | } 376 | 377 | 378 | //if (wParam == WM_MBUTTONDOWN) 379 | //{ 380 | // //DebugLog(L"wheel_tab_ing"); 381 | // return 1; 382 | //} 383 | //if (wParam == WM_LBUTTONUP && double_click_ing) 384 | //{ 385 | // //DebugLog(L"double_click_ing"); 386 | // double_click_ing = false; 387 | // return 1; 388 | //} 389 | 390 | if (wParam == WM_MOUSEWHEEL) 391 | { 392 | HWND hwnd = WindowFromPoint(pmouse->pt); 393 | NodePtr TopContainerView = GetTopContainerView(hwnd); 394 | 395 | PMOUSEHOOKSTRUCTEX pwheel = (PMOUSEHOOKSTRUCTEX)lParam; 396 | int zDelta = GET_WHEEL_DELTA_WPARAM(pwheel->mouseData); 397 | 398 | if (IsOnTheTab(TopContainerView, pmouse->pt) || IsPressed(VK_RBUTTON)) 399 | { 400 | hwnd = GetTopWnd(hwnd); 401 | if (zDelta > 0) 402 | { 403 | ExecuteCommand(IDC_SELECT_PREVIOUS_TAB, hwnd); 404 | } 405 | else 406 | { 407 | ExecuteCommand(IDC_SELECT_NEXT_TAB, hwnd); 408 | } 409 | 410 | wheel_tab_ing = true; 411 | if (TopContainerView) 412 | { 413 | } 414 | //DebugLog(L"WM_MOUSEWHEEL"); 415 | return 1; 416 | } 417 | } 418 | 419 | if (wParam == WM_LBUTTONDBLCLK) 420 | { 421 | HWND hwnd = WindowFromPoint(pmouse->pt); 422 | NodePtr TopContainerView = GetTopContainerView(hwnd); 423 | 424 | bool isOnOneTab = IsOnOneTab(TopContainerView, pmouse->pt); 425 | bool isOnlyOneTab = IsOnlyOneTab(TopContainerView); 426 | 427 | if (TopContainerView) 428 | { 429 | } 430 | 431 | // 双击关闭 432 | if (isOnOneTab) 433 | { 434 | if (isOnlyOneTab) 435 | { 436 | //DebugLog(L"keep_tab"); 437 | //ExecuteCommand(IDC_NEW_TAB, hwnd); 438 | ExecuteCommand(IDC_NEW_TAB); 439 | ExecuteCommand(IDC_SELECT_PREVIOUS_TAB); 440 | ExecuteCommand(IDC_CLOSE_TAB); 441 | } 442 | else 443 | { 444 | ExecuteCommand(IDC_CLOSE_TAB); 445 | } 446 | } 447 | } 448 | 449 | if (wParam == WM_MBUTTONUP) 450 | { 451 | HWND hwnd = WindowFromPoint(pmouse->pt); 452 | NodePtr TopContainerView = GetTopContainerView(hwnd); 453 | 454 | bool isOnOneTab = IsOnOneTab(TopContainerView, pmouse->pt); 455 | bool isOnlyOneTab = IsOnlyOneTab(TopContainerView); 456 | 457 | if (TopContainerView) 458 | { 459 | } 460 | 461 | if (isOnOneTab && isOnlyOneTab) 462 | { 463 | //DebugLog(L"keep_tab"); 464 | //ExecuteCommand(IDC_NEW_TAB, hwnd); 465 | ExecuteCommand(IDC_NEW_TAB); 466 | // ExecuteCommand(IDC_SELECT_PREVIOUS_TAB); 467 | //ExecuteCommand(IDC_CLOSE_TAB); 468 | } 469 | } 470 | } 471 | next: 472 | //DebugLog(L"CallNextHookEx %X", wParam); 473 | return CallNextHookEx(mouse_hook, nCode, wParam, lParam); 474 | } 475 | 476 | bool IsNeedKeep() 477 | { 478 | bool keep_tab = false; 479 | 480 | NodePtr TopContainerView = GetTopContainerView(GetForegroundWindow()); 481 | if (IsOnlyOneTab(TopContainerView)) 482 | { 483 | keep_tab = true; 484 | } 485 | 486 | if (TopContainerView) 487 | { 488 | } 489 | 490 | return keep_tab; 491 | } 492 | 493 | HHOOK keyboard_hook = NULL; 494 | LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) 495 | { 496 | if (nCode == HC_ACTION && !(lParam & 0x80000000)) // pressed 497 | { 498 | bool keep_tab = false; 499 | 500 | if (wParam == 'W' && IsPressed(VK_CONTROL) && !IsPressed(VK_SHIFT)) 501 | { 502 | keep_tab = IsNeedKeep(); 503 | } 504 | if (wParam == VK_F4 && IsPressed(VK_CONTROL)) 505 | { 506 | keep_tab = IsNeedKeep(); 507 | } 508 | 509 | if (keep_tab) 510 | { 511 | ExecuteCommand(IDC_NEW_TAB); 512 | ExecuteCommand(IDC_SELECT_PREVIOUS_TAB); 513 | ExecuteCommand(IDC_CLOSE_TAB); 514 | return 1; 515 | } 516 | } 517 | return CallNextHookEx(keyboard_hook, nCode, wParam, lParam); 518 | } 519 | 520 | void TabBookmark() 521 | { 522 | mouse_hook = SetWindowsHookEx(WH_MOUSE, MouseProc, hInstance, GetCurrentThreadId()); 523 | keyboard_hook = SetWindowsHookEx(WH_KEYBOARD, KeyboardProc, hInstance, GetCurrentThreadId()); 524 | } 525 | -------------------------------------------------------------------------------- /src/appid.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | typedef HRESULT(WINAPI* pPSStringFromPropertyKey) 6 | ( 7 | REFPROPERTYKEY pkey, 8 | LPWSTR psz, 9 | UINT cch 10 | ); 11 | pPSStringFromPropertyKey RawPSStringFromPropertyKey = nullptr; 12 | 13 | HRESULT WINAPI MyPSStringFromPropertyKey( 14 | 15 | REFPROPERTYKEY pkey, 16 | LPWSTR psz, 17 | UINT cch 18 | ) 19 | { 20 | HRESULT result = RawPSStringFromPropertyKey(pkey, psz, cch); 21 | if (SUCCEEDED(result)) 22 | { 23 | if (pkey == PKEY_AppUserModel_ID) 24 | { 25 | //DebugLog(L"MyPSStringFromPropertyKey %s", psz); 26 | return -1; 27 | } 28 | } 29 | return result; 30 | } 31 | 32 | void SetAppId() 33 | { 34 | HMODULE Propsys = LoadLibrary(L"Propsys.dll"); 35 | 36 | PBYTE PSStringFromPropertyKey = (PBYTE)GetProcAddress(Propsys, "PSStringFromPropertyKey"); 37 | MH_STATUS status = MH_CreateHook(PSStringFromPropertyKey, MyPSStringFromPropertyKey, (LPVOID*)& RawPSStringFromPropertyKey); 38 | if (status == MH_OK) 39 | { 40 | MH_EnableHook(PSStringFromPropertyKey); 41 | } 42 | else 43 | { 44 | DebugLog(L"MH_CreateHook PSStringFromPropertyKey failed:%d", status); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/chrome++.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | HMODULE hInstance; 6 | 7 | #define MAGIC_CODE 0x1603ABD9 8 | 9 | #include "MinHook.h" 10 | #include "version.h" 11 | 12 | #include "hijack.h" 13 | #include "utils.h" 14 | #include "TabBookmark.h" 15 | #include "portable.h" 16 | #include "PakPatch.h" 17 | #include "appid.h" 18 | #include "green.h" 19 | 20 | typedef int(*Startup) (); 21 | Startup ExeMain = NULL; 22 | 23 | void ChromePlus() 24 | { 25 | // 快捷方式 26 | SetAppId(); 27 | 28 | // 便携化补丁 29 | MakeGreen(); 30 | 31 | // 标签页,书签,地址栏增强 32 | TabBookmark(); 33 | 34 | // 给pak文件打补丁 35 | PakPatch(); 36 | } 37 | 38 | void ChromePlusCommand(LPWSTR param) 39 | { 40 | if (!wcsstr(param, L"--shuax")) 41 | { 42 | Portable(param); 43 | } 44 | else 45 | { 46 | ChromePlus(); 47 | } 48 | } 49 | 50 | int Loader() 51 | { 52 | // 只关注主界面 53 | LPWSTR param = GetCommandLineW(); 54 | //DebugLog(L"param %s", param); 55 | if (!wcsstr(param, L"-type=")) 56 | { 57 | ChromePlusCommand(param); 58 | } 59 | 60 | //返回到主程序 61 | return ExeMain(); 62 | } 63 | 64 | 65 | void InstallLoader() 66 | { 67 | //获取程序入口点 68 | MODULEINFO mi; 69 | GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &mi, sizeof(MODULEINFO)); 70 | PBYTE entry = (PBYTE)mi.EntryPoint; 71 | 72 | // 入口点跳转到Loader 73 | MH_STATUS status = MH_CreateHook(entry, Loader, (LPVOID*)&ExeMain); 74 | if (status == MH_OK) 75 | { 76 | MH_EnableHook(entry); 77 | } 78 | else 79 | { 80 | DebugLog(L"MH_CreateHook InstallLoader failed:%d", status); 81 | } 82 | } 83 | #define EXTERNC extern "C" 84 | 85 | // 86 | EXTERNC __declspec(dllexport) void shuax() 87 | { 88 | } 89 | 90 | EXTERNC BOOL WINAPI DllMain(HINSTANCE hModule, DWORD dwReason, LPVOID pv) 91 | { 92 | if (dwReason == DLL_PROCESS_ATTACH) 93 | { 94 | DisableThreadLibraryCalls(hModule); 95 | hInstance = hModule; 96 | 97 | // 保持系统dll原有功能 98 | LoadSysDll(hModule); 99 | 100 | // 初始化HOOK库成功以后安装加载器 101 | MH_STATUS status = MH_Initialize(); 102 | if (status == MH_OK) 103 | { 104 | InstallLoader(); 105 | } 106 | else 107 | { 108 | DebugLog(L"MH_Initialize failed:%d", status); 109 | } 110 | } 111 | return TRUE; 112 | } 113 | -------------------------------------------------------------------------------- /src/chrome++.rc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yumaoss/chrome_plus/fadc91cdf8dd785d05535beaa5f2fae0a3dec75f/src/chrome++.rc -------------------------------------------------------------------------------- /src/fastsearch.h: -------------------------------------------------------------------------------- 1 | #ifndef FAST_SEARCH_H_ 2 | #define FAST_SEARCH_H_ 3 | 4 | #include 5 | 6 | static const uint8_t* ForceSearch(const uint8_t* s, int n, const uint8_t* p) 7 | { 8 | int i; 9 | for (i = 0; i < n; i++) 10 | { 11 | if (*(s + i) == *p) 12 | { 13 | return s + i; 14 | } 15 | } 16 | 17 | return NULL; 18 | } 19 | 20 | static const uint8_t* BNDMSearch(const uint8_t* s, int n, const uint8_t* p, int m) 21 | { 22 | int i, j; 23 | 24 | uint32_t maskv[256] = {0}; 25 | for (i = 0; i < m; ++i) 26 | { 27 | maskv[p[i]] |= 1 << (m - 1 - i); 28 | } 29 | 30 | for (i = 0; i <= n - m; i += j) 31 | { 32 | uint32_t mask = maskv[s[i + m - 1]]; 33 | for (j = m; mask;) 34 | { 35 | if (!--j) return s + i; 36 | mask = (mask << 1) & maskv[s[i + j - 1]]; 37 | } 38 | } 39 | 40 | return NULL; 41 | } 42 | 43 | #include 44 | 45 | #define INTBITS (sizeof(int)*8) 46 | static inline void setbit(void *v, int p) // p in 0..255 47 | { 48 | ((int *)v)[p / INTBITS] |= 1 << (p & (INTBITS - 1)); 49 | } 50 | 51 | static inline __m128i xm_shl_001(__m128i x) 52 | { 53 | return _mm_or_si128(_mm_slli_epi64(x, 1), _mm_srli_epi64(_mm_slli_si128(x, 8), 63)); 54 | } 55 | 56 | static const uint8_t* SSEBNDMSearch(const uint8_t* s, int n, const uint8_t* p, int m) 57 | { 58 | uint8_t *tgt = (uint8_t *) s, *pat = (uint8_t *) p; 59 | uint8_t *tgtend = tgt + n - m; 60 | 61 | int i, skip; 62 | __m128i mask, maskv[256], zero = {}; 63 | int8_t used[256] = { }; 64 | 65 | for (i = 0; i < m; ++i) 66 | { 67 | if (!used[pat[i]]) 68 | used[pat[i]] = 1, maskv[pat[i]] = zero; 69 | setbit(&maskv[pat[i]], sizeof(mask) * 8 - 1 - i); 70 | } 71 | 72 | for (; tgt <= tgtend; tgt += skip) 73 | { 74 | i = skip = m; 75 | if (!used[tgt[--i]]) 76 | continue; 77 | mask = maskv[tgt[i]]; // gteed not zero. 78 | do 79 | { 80 | if (0 > (int16_t) _mm_movemask_epi8(mask) && !(skip = i)) 81 | return tgt; 82 | if (!used[tgt[--i]]) 83 | break; 84 | mask = _mm_and_si128(xm_shl_001(mask), maskv[tgt[i]]); 85 | } 86 | while (0xFFFF != _mm_movemask_epi8(_mm_cmpeq_epi8(mask, zero))); 87 | } 88 | 89 | return NULL; 90 | } 91 | 92 | static const uint8_t* SundaySearch(const uint8_t* s, int n, const uint8_t* p, int m) 93 | { 94 | int i; 95 | 96 | size_t skip[256]; 97 | 98 | for ( i = 0; i < 256; i++ ) 99 | { 100 | *(skip + i) = m + 1; 101 | } 102 | 103 | for ( i = 0; i < m; i++ ) 104 | { 105 | *(skip + *(p + i)) = m - i; 106 | } 107 | 108 | while ( s < s + (n - m + 1) ) 109 | { 110 | for ( i = 0; *(s + i) == *(p + i) ; ) 111 | { 112 | i++; 113 | if (i >= m ) 114 | { 115 | return s; 116 | } 117 | } 118 | 119 | s += *(skip + *(s + m)); 120 | } 121 | 122 | return NULL; 123 | } 124 | 125 | const uint8_t* FastSearch(const uint8_t* s, int n, const uint8_t* p, int m) 126 | { 127 | if ( !s || !p || n < m ) 128 | return NULL; 129 | 130 | if (m == 0) 131 | { 132 | return s; 133 | } 134 | else if (m == 1) 135 | { 136 | return ForceSearch(s, n, p); 137 | } 138 | else if (m <= 32) 139 | { 140 | return BNDMSearch(s, n, p, m); 141 | } 142 | else if (m <= 128) 143 | { 144 | return SSEBNDMSearch(s, n, p, m); 145 | } 146 | 147 | return SundaySearch(s, n, p, m); 148 | } 149 | 150 | #endif // FAST_SEARCH_H_ 151 | -------------------------------------------------------------------------------- /src/green.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | BOOL WINAPI FakeGetComputerName( 4 | _Out_ LPTSTR lpBuffer, 5 | _Inout_ LPDWORD lpnSize 6 | ) 7 | { 8 | return 0; 9 | } 10 | 11 | BOOL WINAPI FakeGetVolumeInformation( 12 | _In_opt_ LPCTSTR lpRootPathName, 13 | _Out_opt_ LPTSTR lpVolumeNameBuffer, 14 | _In_ DWORD nVolumeNameSize, 15 | _Out_opt_ LPDWORD lpVolumeSerialNumber, 16 | _Out_opt_ LPDWORD lpMaximumComponentLength, 17 | _Out_opt_ LPDWORD lpFileSystemFlags, 18 | _Out_opt_ LPTSTR lpFileSystemNameBuffer, 19 | _In_ DWORD nFileSystemNameSize 20 | ) 21 | { 22 | return 0; 23 | } 24 | 25 | BOOL WINAPI MyCryptProtectData( 26 | _In_ DATA_BLOB* pDataIn, 27 | _In_opt_ LPCWSTR szDataDescr, 28 | _In_opt_ DATA_BLOB* pOptionalEntropy, 29 | _Reserved_ PVOID pvReserved, 30 | _In_opt_ CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, 31 | _In_ DWORD dwFlags, 32 | _Out_ DATA_BLOB* pDataOut 33 | ) 34 | { 35 | pDataOut->cbData = pDataIn->cbData; 36 | pDataOut->pbData = (BYTE*)LocalAlloc(LMEM_FIXED, pDataOut->cbData); 37 | memcpy(pDataOut->pbData, pDataIn->pbData, pDataOut->cbData); 38 | return true; 39 | } 40 | 41 | typedef BOOL(WINAPI* pCryptUnprotectData)( 42 | _In_ DATA_BLOB* pDataIn, 43 | _Out_opt_ LPWSTR* ppszDataDescr, 44 | _In_opt_ DATA_BLOB* pOptionalEntropy, 45 | _Reserved_ PVOID pvReserved, 46 | _In_opt_ CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, 47 | _In_ DWORD dwFlags, 48 | _Out_ DATA_BLOB* pDataOut 49 | ); 50 | 51 | pCryptUnprotectData RawCryptUnprotectData = NULL; 52 | 53 | BOOL WINAPI MyCryptUnprotectData( 54 | _In_ DATA_BLOB* pDataIn, 55 | _Out_opt_ LPWSTR* ppszDataDescr, 56 | _In_opt_ DATA_BLOB* pOptionalEntropy, 57 | _Reserved_ PVOID pvReserved, 58 | _In_opt_ CRYPTPROTECT_PROMPTSTRUCT* pPromptStruct, 59 | _In_ DWORD dwFlags, 60 | _Out_ DATA_BLOB* pDataOut 61 | ) 62 | { 63 | if (RawCryptUnprotectData(pDataIn, ppszDataDescr, pOptionalEntropy, pvReserved, pPromptStruct, dwFlags, pDataOut)) 64 | { 65 | return true; 66 | } 67 | 68 | pDataOut->cbData = pDataIn->cbData; 69 | pDataOut->pbData = (BYTE*)LocalAlloc(LMEM_FIXED, pDataOut->cbData); 70 | memcpy(pDataOut->pbData, pDataIn->pbData, pDataOut->cbData); 71 | return true; 72 | } 73 | 74 | 75 | 76 | 77 | typedef DWORD(WINAPI* pLogonUserW)( 78 | LPCWSTR lpszUsername, 79 | LPCWSTR lpszDomain, 80 | LPCWSTR lpszPassword, 81 | DWORD dwLogonType, 82 | DWORD dwLogonProvider, 83 | PHANDLE phToken 84 | ); 85 | 86 | pLogonUserW RawLogonUserW = NULL; 87 | 88 | DWORD WINAPI MyLogonUserW( 89 | LPCWSTR lpszUsername, 90 | LPCWSTR lpszDomain, 91 | LPCWSTR lpszPassword, 92 | DWORD dwLogonType, 93 | DWORD dwLogonProvider, 94 | PHANDLE phToken 95 | ) 96 | { 97 | DWORD ret = RawLogonUserW(lpszUsername, lpszDomain, lpszPassword, dwLogonType, dwLogonProvider, phToken); 98 | 99 | SetLastError(ERROR_ACCOUNT_RESTRICTION); 100 | return ret; 101 | } 102 | 103 | typedef BOOL(WINAPI* pIsOS)( 104 | DWORD dwOS 105 | ); 106 | 107 | pIsOS RawIsOS = NULL; 108 | 109 | BOOL WINAPI MyIsOS( 110 | DWORD dwOS 111 | ) 112 | { 113 | DWORD ret = RawIsOS(dwOS); 114 | if (dwOS == OS_DOMAINMEMBER) 115 | { 116 | return false; 117 | } 118 | 119 | return ret; 120 | } 121 | 122 | typedef NET_API_STATUS(WINAPI* pNetUserGetInfo)( 123 | LPCWSTR servername, 124 | LPCWSTR username, 125 | DWORD level, 126 | LPBYTE* bufptr 127 | ); 128 | 129 | pNetUserGetInfo RawNetUserGetInfo = NULL; 130 | 131 | NET_API_STATUS WINAPI MyNetUserGetInfo( 132 | LPCWSTR servername, 133 | LPCWSTR username, 134 | DWORD level, 135 | LPBYTE* bufptr 136 | ) 137 | { 138 | //DebugLog(L"MyNetUserGetInfo %s", username); 139 | 140 | NET_API_STATUS ret = RawNetUserGetInfo(servername, username, level, bufptr); 141 | if (level == 1 && ret == 0) 142 | { 143 | LPUSER_INFO_1 user_info = (LPUSER_INFO_1)* bufptr; 144 | //DebugLog(L"user_info %d %s", user_info->usri1_password_age, user_info->usri1_name); 145 | user_info->usri1_password_age = 0; 146 | //DebugLog(L"user_info %d", user_info->usri1_password_age); 147 | 148 | //DebugLog(L"User account name: %s\n", user_info->usri1_name); 149 | //DebugLog(L"Password: %s\n", user_info->usri1_password); 150 | //DebugLog(L"Password age (seconds): %d\n", user_info->usri1_password_age); 151 | //DebugLog(L"Privilege level: %d\n", user_info->usri1_priv); 152 | //DebugLog(L"Home directory: %s\n", user_info->usri1_home_dir); 153 | //DebugLog(L"User comment: %s\n", user_info->usri1_comment); 154 | //DebugLog(L"Flags (in hex): %x\n", user_info->usri1_flags); 155 | //DebugLog(L"Script path: %s\n", user_info->usri1_script_path); 156 | } 157 | 158 | return ret; 159 | } 160 | 161 | 162 | void MakeGreen() 163 | { 164 | HMODULE kernel32 = LoadLibraryW(L"kernel32.dll"); 165 | if (kernel32) 166 | { 167 | PBYTE GetComputerNameW = (PBYTE)GetProcAddress(kernel32, "GetComputerNameW"); 168 | PBYTE GetVolumeInformationW = (PBYTE)GetProcAddress(kernel32, "GetVolumeInformationW"); 169 | 170 | MH_STATUS status = MH_CreateHook(GetComputerNameW, FakeGetComputerName, NULL); 171 | if (status == MH_OK) 172 | { 173 | MH_EnableHook(GetComputerNameW); 174 | } 175 | else 176 | { 177 | DebugLog(L"MH_CreateHook GetComputerNameW failed:%d", status); 178 | } 179 | status = MH_CreateHook(GetVolumeInformationW, FakeGetVolumeInformation, NULL); 180 | if (status == MH_OK) 181 | { 182 | MH_EnableHook(GetVolumeInformationW); 183 | } 184 | else 185 | { 186 | DebugLog(L"MH_CreateHook GetVolumeInformationW failed:%d", status); 187 | } 188 | } 189 | 190 | //components/os_crypt/os_crypt_win.cc 191 | HMODULE Crypt32 = LoadLibraryW(L"Crypt32.dll"); 192 | if (Crypt32) 193 | { 194 | PBYTE CryptProtectData = (PBYTE)GetProcAddress(Crypt32, "CryptProtectData"); 195 | PBYTE CryptUnprotectData = (PBYTE)GetProcAddress(Crypt32, "CryptUnprotectData"); 196 | 197 | MH_STATUS status = MH_CreateHook(CryptProtectData, MyCryptProtectData, NULL); 198 | if (status == MH_OK) 199 | { 200 | MH_EnableHook(CryptProtectData); 201 | } 202 | else 203 | { 204 | DebugLog(L"MH_CreateHook CryptProtectData failed:%d", status); 205 | } 206 | status = MH_CreateHook(CryptUnprotectData, MyCryptUnprotectData, (LPVOID*)& RawCryptUnprotectData); 207 | if (status == MH_OK) 208 | { 209 | MH_EnableHook(CryptUnprotectData); 210 | } 211 | else 212 | { 213 | DebugLog(L"MH_CreateHook CryptUnprotectData failed:%d", status); 214 | } 215 | } 216 | 217 | HMODULE Advapi32 = LoadLibraryW(L"Advapi32.dll"); 218 | if (Advapi32) 219 | { 220 | PBYTE LogonUserW = (PBYTE)GetProcAddress(Advapi32, "LogonUserW"); 221 | 222 | MH_STATUS status = MH_CreateHook(LogonUserW, MyLogonUserW, (LPVOID*)& RawLogonUserW); 223 | if (status == MH_OK) 224 | { 225 | //DebugLog(L"MH_EnableHook LogonUserW"); 226 | MH_EnableHook(LogonUserW); 227 | } 228 | else 229 | { 230 | DebugLog(L"MH_CreateHook LogonUserW failed:%d", status); 231 | } 232 | } 233 | 234 | HMODULE Shlwapi = LoadLibraryW(L"Shlwapi.dll"); 235 | if (Shlwapi) 236 | { 237 | PBYTE IsOS = (PBYTE)GetProcAddress(Shlwapi, "IsOS"); 238 | 239 | MH_STATUS status = MH_CreateHook(IsOS, MyIsOS, (LPVOID*)& RawIsOS); 240 | if (status == MH_OK) 241 | { 242 | //DebugLog(L"MH_EnableHook IsOS"); 243 | MH_EnableHook(IsOS); 244 | } 245 | else 246 | { 247 | DebugLog(L"MH_CreateHook IsOS failed:%d", status); 248 | } 249 | } 250 | 251 | HMODULE Netapi32 = LoadLibraryW(L"Netapi32.dll"); 252 | if (Netapi32) 253 | { 254 | PBYTE NetUserGetInfo = (PBYTE)GetProcAddress(Netapi32, "NetUserGetInfo"); 255 | 256 | MH_STATUS status = MH_CreateHook(NetUserGetInfo, MyNetUserGetInfo, (LPVOID*)& RawNetUserGetInfo); 257 | if (status == MH_OK) 258 | { 259 | MH_EnableHook(NetUserGetInfo); 260 | } 261 | else 262 | { 263 | DebugLog(L"MH_CreateHook NetUserGetInfo failed:%d", status); 264 | } 265 | } 266 | } 267 | -------------------------------------------------------------------------------- /src/hijack.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | namespace hijack 6 | { 7 | 8 | 9 | #define NOP_FUNC { \ 10 | __nop();\ 11 | __nop();\ 12 | __nop();\ 13 | __nop();\ 14 | __nop();\ 15 | __nop();\ 16 | __nop();\ 17 | __nop();\ 18 | __nop();\ 19 | __nop();\ 20 | __nop();\ 21 | __nop();\ 22 | return __COUNTER__;\ 23 | } 24 | // 用 __COUNTER__ 来生成一点不一样的代码,避免被 VS 自动合并相同函数 25 | 26 | 27 | #define EXPORT(api) int __cdecl api() NOP_FUNC 28 | 29 | 30 | #pragma region 声明导出函数 31 | // 声明导出函数 32 | #pragma comment(linker, "/export:GetFileVersionInfoA=?GetFileVersionInfoA@hijack@@YAHXZ,@1") 33 | #pragma comment(linker, "/export:GetFileVersionInfoByHandle=?GetFileVersionInfoByHandle@hijack@@YAHXZ,@2") 34 | #pragma comment(linker, "/export:GetFileVersionInfoExA=?GetFileVersionInfoExA@hijack@@YAHXZ,@3") 35 | #pragma comment(linker, "/export:GetFileVersionInfoExW=?GetFileVersionInfoExW@hijack@@YAHXZ,@4") 36 | #pragma comment(linker, "/export:GetFileVersionInfoSizeA=?GetFileVersionInfoSizeA@hijack@@YAHXZ,@5") 37 | #pragma comment(linker, "/export:GetFileVersionInfoSizeExA=?GetFileVersionInfoSizeExA@hijack@@YAHXZ,@6") 38 | #pragma comment(linker, "/export:GetFileVersionInfoSizeExW=?GetFileVersionInfoSizeExW@hijack@@YAHXZ,@7") 39 | #pragma comment(linker, "/export:GetFileVersionInfoSizeW=?GetFileVersionInfoSizeW@hijack@@YAHXZ,@8") 40 | #pragma comment(linker, "/export:GetFileVersionInfoW=?GetFileVersionInfoW@hijack@@YAHXZ,@9") 41 | #pragma comment(linker, "/export:VerFindFileA=?VerFindFileA@hijack@@YAHXZ,@10") 42 | #pragma comment(linker, "/export:VerFindFileW=?VerFindFileW@hijack@@YAHXZ,@11") 43 | #pragma comment(linker, "/export:VerInstallFileA=?VerInstallFileA@hijack@@YAHXZ,@12") 44 | #pragma comment(linker, "/export:VerInstallFileW=?VerInstallFileW@hijack@@YAHXZ,@13") 45 | #pragma comment(linker, "/export:VerLanguageNameA=?VerLanguageNameA@hijack@@YAHXZ,@14") 46 | #pragma comment(linker, "/export:VerLanguageNameW=?VerLanguageNameW@hijack@@YAHXZ,@15") 47 | #pragma comment(linker, "/export:VerQueryValueA=?VerQueryValueA@hijack@@YAHXZ,@16") 48 | #pragma comment(linker, "/export:VerQueryValueW=?VerQueryValueW@hijack@@YAHXZ,@17") 49 | 50 | EXPORT(GetFileVersionInfoA) 51 | EXPORT(GetFileVersionInfoByHandle) 52 | EXPORT(GetFileVersionInfoExA) 53 | EXPORT(GetFileVersionInfoExW) 54 | EXPORT(GetFileVersionInfoSizeA) 55 | EXPORT(GetFileVersionInfoSizeExA) 56 | EXPORT(GetFileVersionInfoSizeExW) 57 | EXPORT(GetFileVersionInfoSizeW) 58 | EXPORT(GetFileVersionInfoW) 59 | EXPORT(VerFindFileA) 60 | EXPORT(VerFindFileW) 61 | EXPORT(VerInstallFileA) 62 | EXPORT(VerInstallFileW) 63 | EXPORT(VerLanguageNameA) 64 | EXPORT(VerLanguageNameW) 65 | EXPORT(VerQueryValueA) 66 | EXPORT(VerQueryValueW) 67 | } 68 | #pragma endregion 69 | 70 | #pragma region 还原导出函数 71 | bool WriteMemory(PBYTE BaseAddress, PBYTE Buffer, DWORD nSize) 72 | { 73 | DWORD ProtectFlag = 0; 74 | if (VirtualProtectEx(GetCurrentProcess(), BaseAddress, nSize, PAGE_EXECUTE_READWRITE, &ProtectFlag)) 75 | { 76 | memcpy(BaseAddress, Buffer, nSize); 77 | FlushInstructionCache(GetCurrentProcess(), BaseAddress, nSize); 78 | VirtualProtectEx(GetCurrentProcess(), BaseAddress, nSize, ProtectFlag, &ProtectFlag); 79 | return true; 80 | } 81 | return false; 82 | } 83 | 84 | // 还原导出函数 85 | void InstallJMP(PBYTE BaseAddress, uintptr_t Function) 86 | { 87 | if (*BaseAddress == 0xE9) 88 | { 89 | BaseAddress++; 90 | BaseAddress = BaseAddress + *(uint32_t*)BaseAddress + 4; 91 | } 92 | #ifdef _WIN64 93 | BYTE move[] = {0x48, 0xB8};//move rax,xxL); 94 | BYTE jump[] = {0xFF, 0xE0};//jmp rax 95 | 96 | WriteMemory(BaseAddress, move, sizeof(move)); 97 | BaseAddress += sizeof(move); 98 | 99 | WriteMemory(BaseAddress, (PBYTE)&Function, sizeof(uintptr_t)); 100 | BaseAddress += sizeof(uintptr_t); 101 | 102 | WriteMemory(BaseAddress, jump, sizeof(jump)); 103 | #else 104 | BYTE jump[] = {0xE9}; 105 | WriteMemory(BaseAddress, jump, sizeof(jump)); 106 | BaseAddress += sizeof(jump); 107 | 108 | uintptr_t offset = Function - (uintptr_t)BaseAddress - 4; 109 | WriteMemory(BaseAddress, (PBYTE)&offset, sizeof(offset)); 110 | #endif // _WIN64 111 | } 112 | #pragma endregion 113 | 114 | #pragma region 加载系统dll 115 | void LoadVersion(HINSTANCE hModule) 116 | { 117 | PBYTE pImageBase = (PBYTE)hModule; 118 | PIMAGE_DOS_HEADER pimDH = (PIMAGE_DOS_HEADER)pImageBase; 119 | if (pimDH->e_magic == IMAGE_DOS_SIGNATURE) 120 | { 121 | PIMAGE_NT_HEADERS pimNH = (PIMAGE_NT_HEADERS)(pImageBase + pimDH->e_lfanew); 122 | if (pimNH->Signature == IMAGE_NT_SIGNATURE) 123 | { 124 | PIMAGE_EXPORT_DIRECTORY pimExD = (PIMAGE_EXPORT_DIRECTORY)(pImageBase + pimNH->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT].VirtualAddress); 125 | DWORD* pName = (DWORD*)(pImageBase + pimExD->AddressOfNames); 126 | DWORD* pFunction = (DWORD*)(pImageBase + pimExD->AddressOfFunctions); 127 | WORD* pNameOrdinals = (WORD*)(pImageBase + pimExD->AddressOfNameOrdinals); 128 | 129 | wchar_t szSysDirectory[MAX_PATH + 1]; 130 | GetSystemDirectory(szSysDirectory, MAX_PATH); 131 | 132 | wchar_t szDLLPath[MAX_PATH + 1]; 133 | lstrcpy(szDLLPath, szSysDirectory); 134 | lstrcat(szDLLPath, TEXT("\\version.dll")); 135 | 136 | HINSTANCE module = LoadLibrary(szDLLPath); 137 | for (size_t i = 0; i < pimExD->NumberOfNames; i++) 138 | { 139 | uintptr_t Original = (uintptr_t)GetProcAddress(module, (char*)(pImageBase + pName[i])); 140 | if (Original) 141 | { 142 | InstallJMP(pImageBase + pFunction[pNameOrdinals[i]], Original); 143 | } 144 | } 145 | } 146 | } 147 | } 148 | #pragma endregion 149 | 150 | void LoadSysDll(HINSTANCE hModule) 151 | { 152 | LoadVersion(hModule); 153 | } 154 | -------------------------------------------------------------------------------- /src/portable.h: -------------------------------------------------------------------------------- 1 | std::wstring QuoteSpaceIfNeeded(const std::wstring &str) 2 | { 3 | if (str.find(L' ') == std::wstring::npos) 4 | return std::move(str); 5 | 6 | std::wstring escaped(L"\""); 7 | for (auto c : str) 8 | { 9 | if (c == L'"') 10 | escaped += L'"'; 11 | escaped += c; 12 | } 13 | escaped += L'"'; 14 | return std::move(escaped); 15 | } 16 | 17 | std::wstring JoinArgsString(std::vector lines, const std::wstring &delimiter) 18 | { 19 | std::wstring text; 20 | bool first = true; 21 | for (auto &line : lines) 22 | { 23 | if (!first) 24 | text += delimiter; 25 | else 26 | first = false; 27 | text += QuoteSpaceIfNeeded(line); 28 | } 29 | return text; 30 | } 31 | 32 | bool IsExistsPortable() 33 | { 34 | std::wstring path = GetAppDir() + L"\\portable"; 35 | if (PathFileExists(path.data())) 36 | { 37 | return true; 38 | } 39 | return false; 40 | } 41 | 42 | bool IsNeedPortable() 43 | { 44 | return true; 45 | static bool need_portable = IsExistsPortable(); 46 | return need_portable; 47 | } 48 | 49 | std::wstring GetUserDataDir() 50 | { 51 | std::wstring path = GetAppDir() + L"\\..\\Data"; 52 | 53 | TCHAR temp[MAX_PATH]; 54 | ::PathCanonicalize(temp, path.data()); 55 | 56 | return temp; 57 | } 58 | std::wstring GetDiskCacheDir() 59 | { 60 | std::wstring path = GetAppDir() + L"\\..\\Cache"; 61 | 62 | TCHAR temp[MAX_PATH]; 63 | ::PathCanonicalize(temp, path.data()); 64 | 65 | return temp; 66 | } 67 | 68 | // 构造新命令行 69 | std::wstring GetCommand(LPWSTR param) 70 | { 71 | std::vector args; 72 | 73 | int argc; 74 | LPWSTR* argv = CommandLineToArgvW(param, &argc); 75 | 76 | int insert_pos = 0; 77 | for (int i = 0; i < argc; i++) 78 | { 79 | if (wcscmp(argv[i], L"--") == 0) 80 | { 81 | break; 82 | } 83 | if (wcscmp(argv[i], L"--single-argument") == 0) 84 | { 85 | break; 86 | } 87 | insert_pos = i; 88 | } 89 | for (int i = 0; i < argc; i++) 90 | { 91 | // 保留原来参数 92 | if(i) 93 | args.push_back(argv[i]); 94 | 95 | // 追加参数 96 | if (i == insert_pos) 97 | { 98 | args.push_back(L"--shuax"); 99 | 100 | // args.push_back(L"--force-local-ntp"); 101 | // args.push_back(L"--disable-background-networking"); 102 | 103 | args.push_back(L"--disable-features=RendererCodeIntegrity,FlashDeprecationWarning"); 104 | 105 | //if (IsNeedPortable()) 106 | { 107 | auto diskcache = GetDiskCacheDir(); 108 | 109 | wchar_t temp[MAX_PATH]; 110 | wsprintf(temp, L"--disk-cache-dir=%s", diskcache.c_str()); 111 | args.push_back(temp); 112 | } 113 | { 114 | auto userdata = GetUserDataDir(); 115 | 116 | wchar_t temp[MAX_PATH]; 117 | wsprintf(temp, L"--user-data-dir=%s", userdata.c_str()); 118 | args.push_back(temp); 119 | } 120 | } 121 | } 122 | LocalFree(argv); 123 | 124 | return JoinArgsString(args, L" ");; 125 | } 126 | 127 | 128 | void Portable(LPWSTR param) 129 | { 130 | wchar_t path[MAX_PATH]; 131 | ::GetModuleFileName(NULL, path, MAX_PATH); 132 | 133 | std::wstring args = GetCommand(param); 134 | 135 | SHELLEXECUTEINFO sei = { 0 }; 136 | sei.cbSize = sizeof(SHELLEXECUTEINFO); 137 | sei.fMask = SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI; 138 | sei.lpVerb = L"open"; 139 | sei.lpFile = path; 140 | sei.nShow = SW_SHOWNORMAL; 141 | 142 | sei.lpParameters = args.c_str(); 143 | if (ShellExecuteEx(&sei)) 144 | { 145 | ExitProcess(0); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "FastSearch.h" 8 | 9 | std::wstring Format(const wchar_t* format, va_list args) 10 | { 11 | std::vector buffer; 12 | 13 | size_t length = _vscwprintf(format, args); 14 | 15 | buffer.resize((length + 1) * sizeof(wchar_t)); 16 | 17 | _vsnwprintf_s(&buffer[0], length + 1, length, format, args); 18 | 19 | return std::wstring(&buffer[0]);; 20 | } 21 | 22 | std::wstring Format(const wchar_t* format, ...) 23 | { 24 | va_list args; 25 | 26 | va_start(args, format); 27 | auto str = Format(format, args); 28 | va_end(args); 29 | 30 | return str; 31 | } 32 | 33 | void DebugLog(const wchar_t* format, ...) 34 | { 35 | va_list args; 36 | 37 | va_start(args, format); 38 | auto str = Format(format, args); 39 | va_end(args); 40 | 41 | str = Format(L"[chrome++]%s\n", str.c_str()); 42 | 43 | OutputDebugStringW(str.c_str()); 44 | } 45 | 46 | // 搜索内存 47 | uint8_t* memmem(uint8_t* src, int n, const uint8_t* sub, int m) 48 | { 49 | return (uint8_t*)FastSearch(src, n, sub, m); 50 | } 51 | 52 | uint8_t* SearchModuleRaw(HMODULE module, const uint8_t* sub, int m) 53 | { 54 | uint8_t* buffer = (uint8_t*)module; 55 | 56 | PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)(buffer + ((PIMAGE_DOS_HEADER)buffer)->e_lfanew); 57 | PIMAGE_SECTION_HEADER section = (PIMAGE_SECTION_HEADER)((char*)nt_header + sizeof(DWORD) + 58 | sizeof(IMAGE_FILE_HEADER) + nt_header->FileHeader.SizeOfOptionalHeader); 59 | 60 | for (int i = 0; i < nt_header->FileHeader.NumberOfSections; i++) 61 | { 62 | if (strcmp((const char*)section[i].Name, ".text") == 0) 63 | { 64 | return memmem(buffer + section[i].PointerToRawData, section[i].SizeOfRawData, sub, m); 65 | break; 66 | } 67 | } 68 | return NULL; 69 | } 70 | 71 | uint8_t* SearchModuleRaw2(HMODULE module, const uint8_t* sub, int m) 72 | { 73 | uint8_t* buffer = (uint8_t*)module; 74 | 75 | PIMAGE_NT_HEADERS nt_header = (PIMAGE_NT_HEADERS)(buffer + ((PIMAGE_DOS_HEADER)buffer)->e_lfanew); 76 | PIMAGE_SECTION_HEADER section = (PIMAGE_SECTION_HEADER)((char*)nt_header + sizeof(DWORD) + 77 | sizeof(IMAGE_FILE_HEADER) + nt_header->FileHeader.SizeOfOptionalHeader); 78 | 79 | for (int i = 0; i < nt_header->FileHeader.NumberOfSections; i++) 80 | { 81 | if (strcmp((const char*)section[i].Name, ".rdata") == 0) 82 | { 83 | return memmem(buffer + section[i].PointerToRawData, section[i].SizeOfRawData, sub, m); 84 | break; 85 | } 86 | } 87 | return NULL; 88 | } 89 | #include 90 | #pragma comment(lib,"Shlwapi.lib") 91 | 92 | // 获得程序所在文件夹 93 | std::wstring GetAppDir() 94 | { 95 | wchar_t path[MAX_PATH]; 96 | ::GetModuleFileName(NULL, path, MAX_PATH); 97 | ::PathRemoveFileSpec(path); 98 | return path; 99 | } 100 | 101 | #define IDC_NEW_TAB 34014 102 | #define IDC_CLOSE_TAB 34015 103 | #define IDC_SELECT_NEXT_TAB 34016 104 | #define IDC_SELECT_PREVIOUS_TAB 34017 105 | #define IDC_SELECT_TAB_0 34018 106 | #define IDC_SELECT_TAB_1 34019 107 | #define IDC_SELECT_TAB_2 34020 108 | #define IDC_SELECT_TAB_3 34021 109 | #define IDC_SELECT_TAB_4 34022 110 | #define IDC_SELECT_TAB_5 34023 111 | #define IDC_SELECT_TAB_6 34024 112 | #define IDC_SELECT_TAB_7 34025 113 | #define IDC_SELECT_LAST_TAB 34026 114 | 115 | #define IDC_UPGRADE_DIALOG 40024 116 | 117 | HWND GetTopWnd(HWND hwnd) 118 | { 119 | while (::GetParent(hwnd) && ::IsWindowVisible(::GetParent(hwnd))) 120 | { 121 | hwnd = ::GetParent(hwnd); 122 | } 123 | return hwnd; 124 | } 125 | 126 | void ExecuteCommand(int id, HWND hwnd = 0) 127 | { 128 | if (hwnd == 0) hwnd = GetForegroundWindow(); 129 | //hwnd = GetTopWnd(hwnd); 130 | //hwnd = GetForegroundWindow(); 131 | //PostMessage(hwnd, WM_SYSCOMMAND, id, 0); 132 | ::SendMessageTimeoutW(hwnd, WM_SYSCOMMAND, id, 0, 0, 1000, 0); 133 | } 134 | 135 | // 发送按键 136 | template 137 | void SendKey(T ... keys) 138 | { 139 | std::vector inputs; 140 | std::vector keys_ = { keys ... }; 141 | for (auto & key : keys_) 142 | { 143 | INPUT input = { 0 }; 144 | input.type = INPUT_KEYBOARD; 145 | input.ki.dwFlags = KEYEVENTF_EXTENDEDKEY; 146 | input.ki.wVk = (WORD)key; 147 | input.ki.dwExtraInfo = MAGIC_CODE; 148 | 149 | // 修正鼠标消息 150 | switch (input.ki.wVk) 151 | { 152 | case VK_RBUTTON: 153 | input.type = INPUT_MOUSE; 154 | input.mi.dwFlags = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_RIGHTDOWN; 155 | input.mi.dwExtraInfo = MAGIC_CODE; 156 | break; 157 | case VK_LBUTTON: 158 | input.type = INPUT_MOUSE; 159 | input.mi.dwFlags = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_LEFTDOWN; 160 | input.mi.dwExtraInfo = MAGIC_CODE; 161 | break; 162 | case VK_MBUTTON: 163 | input.type = INPUT_MOUSE; 164 | input.mi.dwFlags = MOUSEEVENTF_MIDDLEDOWN; 165 | input.mi.dwExtraInfo = MAGIC_CODE; 166 | break; 167 | } 168 | 169 | inputs.push_back(input); 170 | } 171 | std::reverse(keys_.begin(), keys_.end()); 172 | for (auto & key : keys_) 173 | { 174 | INPUT input = { 0 }; 175 | input.type = INPUT_KEYBOARD; 176 | input.ki.dwFlags = KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP; 177 | input.ki.wVk = (WORD)key; 178 | input.ki.dwExtraInfo = MAGIC_CODE; 179 | 180 | // 修正鼠标消息 181 | switch (input.ki.wVk) 182 | { 183 | case VK_RBUTTON: 184 | input.type = INPUT_MOUSE; 185 | input.mi.dwFlags = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE ? MOUSEEVENTF_LEFTUP : MOUSEEVENTF_RIGHTUP; 186 | input.mi.dwExtraInfo = MAGIC_CODE; 187 | break; 188 | case VK_LBUTTON: 189 | input.type = INPUT_MOUSE; 190 | input.mi.dwFlags = ::GetSystemMetrics(SM_SWAPBUTTON) == TRUE ? MOUSEEVENTF_RIGHTUP : MOUSEEVENTF_LEFTUP; 191 | input.mi.dwExtraInfo = MAGIC_CODE; 192 | break; 193 | case VK_MBUTTON: 194 | input.type = INPUT_MOUSE; 195 | input.mi.dwFlags = MOUSEEVENTF_MIDDLEUP; 196 | input.mi.dwExtraInfo = MAGIC_CODE; 197 | break; 198 | } 199 | 200 | inputs.push_back(input); 201 | } 202 | //for (auto & key : inputs) 203 | //{ 204 | // DebugLog(L"%X %X", key.ki.wVk, key.mi.dwFlags); 205 | //} 206 | 207 | ::SendInput((UINT)inputs.size(), &inputs[0], sizeof(INPUT)); 208 | } 209 | 210 | 211 | //发送鼠标消息 212 | void SendOneMouse(int mouse) 213 | { 214 | // 交换左右键 215 | if (::GetSystemMetrics(SM_SWAPBUTTON) == TRUE) 216 | { 217 | if (mouse == MOUSEEVENTF_RIGHTDOWN) mouse = MOUSEEVENTF_LEFTDOWN; 218 | else if (mouse == MOUSEEVENTF_RIGHTUP) mouse = MOUSEEVENTF_LEFTUP; 219 | } 220 | 221 | INPUT input[1]; 222 | memset(input, 0, sizeof(input)); 223 | 224 | input[0].type = INPUT_MOUSE; 225 | 226 | input[0].mi.dwFlags = mouse; 227 | input[0].mi.dwExtraInfo = MAGIC_CODE; 228 | ::SendInput(1, input, sizeof(INPUT)); 229 | } 230 | 231 | bool isEndWith(const wchar_t *s, const wchar_t *sub) 232 | { 233 | if (!s || !sub) return false; 234 | size_t len1 = wcslen(s); 235 | size_t len2 = wcslen(sub); 236 | if (len2 > len1) return false; 237 | return !_memicmp(s + len1 - len2, sub, len2 * sizeof(wchar_t)); 238 | } 239 | 240 | // 压缩HTML 241 | std::string <rim(std::string &s) { 242 | s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {return !std::isspace(ch); })); 243 | return s; 244 | } 245 | std::string &rtrim(std::string &s) { 246 | s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {return !std::isspace(ch); }).base(), s.end()); 247 | return s; 248 | } 249 | 250 | std::string &trim(std::string &s) { 251 | return ltrim(rtrim(s)); 252 | } 253 | 254 | std::vector split(const std::string &text, char sep) { 255 | std::vector tokens; 256 | std::size_t start = 0, end = 0; 257 | while ((end = text.find(sep, start)) != std::string::npos) { 258 | std::string temp = text.substr(start, end - start); 259 | tokens.push_back(temp); 260 | start = end + 1; 261 | } 262 | std::string temp = text.substr(start); 263 | tokens.push_back(temp); 264 | return tokens; 265 | } 266 | 267 | void compression_html(std::string& html) 268 | { 269 | auto lines = split(html, '\n'); 270 | html.clear(); 271 | for (auto &line : lines) 272 | { 273 | html += "\n"; 274 | html += trim(line); 275 | } 276 | } 277 | 278 | // 替换字符串 279 | bool ReplaceStringInPlace(std::string& subject, const std::string& search, const std::string& replace) 280 | { 281 | bool find = false; 282 | size_t pos = 0; 283 | while ((pos = subject.find(search, pos)) != std::string::npos) { 284 | subject.replace(pos, search.length(), replace); 285 | pos += replace.length(); 286 | find = true; 287 | } 288 | return find; 289 | } 290 | //bool WriteMemory(PBYTE BaseAddress, PBYTE Buffer, DWORD nSize) 291 | //{ 292 | // DWORD ProtectFlag = 0; 293 | // if (VirtualProtectEx(GetCurrentProcess(), BaseAddress, nSize, PAGE_EXECUTE_READWRITE, &ProtectFlag)) 294 | // { 295 | // memcpy(BaseAddress, Buffer, nSize); 296 | // FlushInstructionCache(GetCurrentProcess(), BaseAddress, nSize); 297 | // VirtualProtectEx(GetCurrentProcess(), BaseAddress, nSize, ProtectFlag, &ProtectFlag); 298 | // return true; 299 | // } 300 | // return false; 301 | //} 302 | -------------------------------------------------------------------------------- /src/version.h: -------------------------------------------------------------------------------- 1 | #define RELEASE_VER_MAIN 1 2 | #define RELEASE_VER_SUB 5 3 | #define RELEASE_VER_FIX 2 4 | 5 | #define TOSTRING2(arg) #arg 6 | #define TOSTRING(arg) TOSTRING2(arg) 7 | 8 | #define RELEASE_VER_STR TOSTRING(RELEASE_VER_MAIN) "." TOSTRING(RELEASE_VER_SUB) "." TOSTRING(RELEASE_VER_FIX) 9 | -------------------------------------------------------------------------------- /xmake.lua: -------------------------------------------------------------------------------- 1 | includes("VC-LTL5.lua") 2 | 3 | add_rules("mode.debug", "mode.release") 4 | 5 | set_warnings("more") 6 | 7 | add_defines("WIN32", "_WIN32") 8 | add_defines("UNICODE", "_UNICODE", "_CRT_SECURE_NO_WARNINGS", "_CRT_NONSTDC_NO_DEPRECATE") 9 | 10 | if is_mode("release") then 11 | add_defines("NDEBUG") 12 | add_cxflags("/O2", "/Os", "/Gy", "/MT", "/EHsc", "/fp:precise") 13 | add_ldflags("/DYNAMICBASE", "/LTCG") 14 | end 15 | 16 | add_cxflags("/utf-8") 17 | 18 | add_links("gdiplus", "kernel32", "user32", "gdi32", "winspool", "comdlg32") 19 | add_links("advapi32", "shell32", "ole32", "oleaut32", "uuid", "odbc32", "odbccp32") 20 | 21 | target("minhook") 22 | set_kind("static") 23 | add_files("minhook/src/**.c") 24 | add_includedirs("minhook/include", {public=true}) 25 | 26 | target("chrome_plus") 27 | set_kind("shared") 28 | set_targetdir("$(buildir)/release") 29 | set_basename("version") 30 | add_deps("minhook") 31 | add_files("src/*.cpp") 32 | add_files("src/*.rc") 33 | add_links("user32") 34 | after_build(function (target) 35 | os.rm("$(buildir)/release/version.exp") 36 | os.rm("$(buildir)/release/version.lib") 37 | end) 38 | --------------------------------------------------------------------------------