├── README.md ├── dvcid.hpp └── dvcid.cpp /README.md: -------------------------------------------------------------------------------- 1 | # Oreo device ID Changer 2 | 3 | ## A tool to change Device ID on Oreo 4 | 5 | **Android 8 (Oreo) has a major behavior change on how device ID is manipulated, this tool helps you Query / Modify the ID of applications on Oreo** 6 | 7 | # Flags changed, prior versions use different flags 8 | 9 | ## Usage 10 | 11 | | Options | Arguments | Description | 12 | | ------- | -------- | ----------- | 13 | | -q, --query | | To query current ID (May be inherited from the previous Android version) | 14 | | -Q, --query_default | | To query the default ID (The new ID decided by Oreo) | 15 | | -a, --assign | [ID] | To change current ID (Usually a 16-digits number) | 16 | | -A, --assign_default | [ID] | To change the default ID | 17 | | -b, --backup | [FILE_PATH] | To make a backup, output to the file specified | 18 | | -r, --restore | [FILE_PATH] | To restore from a backup file | 19 | | -p, --package | [PKG_NAME] | To specify application by package name, **THIS FLAG IS NECESSARY** | 20 | | -i, --inplace | | Modify ID directly (Print to stdout if not set, **USE THIS OPTION CAREFULLY**) | 21 | | -y, --force | | To skip security checks | 22 | | -f, --file | [FILE_PATH] | To Specify which file to be processed, don't set to use default | 23 | | -h, --help | | To show help information | 24 | 25 | ## Notice 26 | * Root privilege is needed. 27 | * A **REBOOT** is required for changes to go into effect. 28 | * A valid (uncorrupted) setting is expected, otherwise the behavior will be undefined. 29 | * Always preview before assigning inplacely, or make backups. 30 | * Invalid option combinations will trigger help_information() and NO action will be performed. 31 | * Specify ONLY ONE package, the last one works if more than one is given. 32 | 33 | ## Examples 34 | * To query the current ID of the package 'com.android.example' 35 | `dvcid --query --package com.android.example` 36 | * To change the current ID of the package 'com.android.example' to '0000000000000000' directly 37 | `dvcid -i --assign 0000000000000000 --package com.android.example` 38 | * To query the current ID of the package 'com.android.example' from the file 'settings-ssaid.xml' 39 | `dvcid --query --package com.android.example --file settings-ssaid.xml` 40 | * To set both ID of the package 'com.android.example' at the same time to '1234567890123456' and '0000000000000000' 41 | `dvcid --assign 1234567890123456 -A 0000000000000000 --package com.android.example` 42 | * To save current settings to file 'ssaid_backup.xml' 43 | `dvcid --backup ssaid_backup.xml` 44 | -------------------------------------------------------------------------------- /dvcid.hpp: -------------------------------------------------------------------------------- 1 | #ifndef DVCID_HPP_ 2 | #define DVCID_HPP_ 3 | 4 | #include 5 | 6 | struct ParsedLine 7 | { 8 | ParsedLine () = default; 9 | 10 | ParsedLine 11 | ( 12 | const std::string& id_A_, 13 | const std::string& name_A_, 14 | const std::string& value_A_, 15 | const std::string& package_A_, 16 | const std::string& default_value_A_, 17 | const std::string& default_sys_set_A_, 18 | const std::string& tag_A_ 19 | ) 20 | : id(id_A_), name(name_A_), value(value_A_), package(package_A_), 21 | default_value(default_value_A_), default_sys_set(default_sys_set_A_), 22 | tag(tag_A_) 23 | {} 24 | 25 | bool 26 | is_crrupted() const 27 | { 28 | return ( 29 | id.empty() && 30 | name.empty() && 31 | value.empty() && 32 | package.empty() && 33 | default_value.empty() && 34 | default_sys_set.empty() && 35 | tag.empty()); 36 | } 37 | 38 | std::string id; 39 | std::string name; 40 | std::string value; 41 | std::string package; 42 | std::string default_value; 43 | std::string default_sys_set; 44 | std::string tag; 45 | }; 46 | 47 | struct Options 48 | { 49 | Options 50 | ( 51 | const std::string& pkg_name = "", 52 | const std::string& dft_id = "", 53 | const std::string& crt_id = "", 54 | const std::string& xml_pth = "/data/system/users/0/settings_ssaid.xml", 55 | const std::string& bak_pth = "settings_ssaid.xml.bak", 56 | const std::string& rst_pth = "", 57 | bool to_help = false, 58 | bool to_as_dft = false, 59 | bool to_as_crt = false, 60 | bool to_qr_dft = false, 61 | bool to_qr_crt = false, 62 | bool to_backup = false, 63 | bool to_restore = false, 64 | bool do_inplace = false, 65 | bool do_forcibly = false 66 | ) 67 | : package_name(pkg_name), default_id(dft_id), current_id(crt_id), 68 | xml_file(xml_pth), backup_file(bak_pth), restore_file(rst_pth), 69 | help(to_help), assign_default(to_as_dft), assign_current(to_as_crt), 70 | query_default(to_qr_dft), backup(to_backup), restore(to_restore), 71 | query_current(to_qr_crt), inplace(do_inplace), force(do_forcibly) 72 | {} 73 | 74 | std::string 75 | package_name, 76 | default_id, 77 | current_id, 78 | xml_file, 79 | backup_file, 80 | restore_file; 81 | 82 | bool 83 | help, 84 | assign_default, 85 | assign_current, 86 | query_default, 87 | query_current, 88 | backup, 89 | restore, 90 | inplace, 91 | force; 92 | }; 93 | 94 | void parse_file(const std::string& xml_file); 95 | void write_back(const std::string& final_copy, const std::string& xml_file); 96 | void write_back(std::string&& final_copy, const std::string& xml_file); 97 | void print_out(const std::string& final_copy); 98 | void print_out(std::string&& final_copy); 99 | std::string query(const std::string& package_name, const bool& which); 100 | void assign_safe_guard(const std::string& package_name, const std::string& device_id); 101 | void assign(const std::string& package_name, const std::string& device_id, const bool&& which); 102 | void copy_file(const std::string& src_path, const std::string& dst_path); 103 | [[noreturn]] void help_information(std::string&& exit_info = "", int&& error_code = 0); 104 | Options get_options (int& argc, char** (&argv)); 105 | std::string get_final(bool&& committed = true); 106 | 107 | #define ___HELP_INFO___ "\ 108 | dvcid could help you query/modify Device IDs on Android 8 (Oreo)\n\ 109 | \n\ 110 | For further information about dvcid, see the page: https://github.com/CoNsTaRwU/oreo_device_id_changer/blob/master/README.md\n\ 111 | \n\ 112 | SYNOPSIS:\n\ 113 | dvcid [FLAG] [ARGUMENT] [FLAG] [ARGUMENT] ...\n\ 114 | \n\ 115 | Some Flags may not require an argument\n\ 116 | \n\ 117 | ARGUMENTS:\n\ 118 | [ID] Designated device ID to assign, usually a 16-digit number\n\ 119 | [PACKAGE_NAME] Package name of the designated application\n\ 120 | [FILE_PATH] Path to the file to be processed\n\ 121 | \n\ 122 | FLAGS:\n\ 123 | -q, --query_current Set this flag to query the current ID (May be inherited from the previous Android version)\n\ 124 | -Q, --query_default Set this flag to query the default ID (The new ID decided by Oreo)\n\ 125 | -a, --assign_current [ID] To change the current ID\n\ 126 | -A, --assign_default [ID] To change the default ID\n\ 127 | -p, --package [PACKAGE_NAME] To specify application by package name, THIS FLAG IS NECESSARY\n\ 128 | -b, --backup [FILE_PATH] To make a backup, output to the file specified\n\ 129 | -r, --restore [FILE_PATH] To restore device ID from a backup\n\ 130 | -i, --inplace To modify ID directly (Print to stdout if not set, USE THIS OPTION CAREFULLY)\n\ 131 | -y, --force To skip security checks\n\ 132 | -f, --file [FILE_PATH] To specify which file to be processed\n\ 133 | -h, --help To show this information\n\ 134 | \n\ 135 | EXAMPLES:\n\ 136 | To query the current ID of the package 'com.android.example':\n\ 137 | dvcid --query --package com.android.example\n\ 138 | \n\ 139 | To change the current ID of the package 'com.android.example' to '0000000000000000' directly:\n\ 140 | dvcid -i --assign 0000000000000000 --package com.android.example\n\ 141 | \n\ 142 | To query the current ID of the package 'com.android.example' from the file 'device_id':\n\ 143 | dvcid --query --package com.android.example --file device_id\n\ 144 | \n\ 145 | To set both ID of the package 'com.android.example' at the same time to '1234567890123456' and '0000000000000000':\n\ 146 | dvcid --assign 1234567890123456 -A 0000000000000000 --package com.android.example\n\ 147 | \n\ 148 | To save current settings to file 'device_id.bak':\n\ 149 | dvcid --backup ssaid_backup.xml\n\ 150 | \n\ 151 | To restore IDs from a backup file 'device_id.bak'\n\ 152 | dvcid --restore device_id.bak\n\ 153 | \n\ 154 | NOTICE:\n\ 155 | Root privilege is needed.\n\ 156 | A REBOOT is required for changes to go into effect.\n\ 157 | A valid (uncorrupted) setting is expected, otherwise the behavior will be undefined.\n\ 158 | Always preview before assigning inplacely, or make backups.\n\ 159 | Querying both current and default ID at the same time is NOT possible.\n\ 160 | To Assign the current and default ID at the same time, set the both flags.\n\ 161 | It is NOT possible to query and assign at the same time.\n\ 162 | Specify ONLY ONE package, the last one works if more than one is given.\n\n\ 163 | "; 164 | 165 | static const option long_opts_SG_ [] = 166 | { 167 | {"help", no_argument, nullptr, 'h'}, 168 | {"assign", required_argument, nullptr, 'a'}, 169 | {"assign_default", required_argument, nullptr, 'A'}, 170 | {"query", no_argument, nullptr, 'q'}, 171 | {"query_default", no_argument, nullptr, 'Q'}, 172 | {"backup", required_argument, nullptr, 'b'}, 173 | {"restore", required_argument, nullptr, 'r'}, 174 | {"package", required_argument, nullptr, 'p'}, 175 | {"file", required_argument, nullptr, 'f'}, 176 | {"inplace", no_argument, nullptr, 'i'}, 177 | {"force", no_argument, nullptr, 'y'}, 178 | {nullptr, 0, nullptr, 0} 179 | }; 180 | 181 | static const char opts_SG_ [] = "ha:A:qQb:r:p:f:iy"; 182 | 183 | #endif // DVCID_HPP_ 184 | -------------------------------------------------------------------------------- /dvcid.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "dvcid.hpp" 10 | 11 | namespace { 12 | 13 | static std::deque parsed_XML_SG_; 14 | static std::deque orign_XML_SG_; 15 | 16 | } 17 | 18 | int 19 | main(int argc, char* argv[]) 20 | { 21 | Options opts = get_options(argc, argv); 22 | 23 | if (opts.help) 24 | { help_information("", 0); } 25 | 26 | /* At least one action needs to be taken */ 27 | if (!(opts.query_default || opts.query_current || opts.assign_default || 28 | opts.assign_current || opts.backup || opts.restore)) 29 | { help_information("invalid option combination: nothing to do: ", 64); } 30 | 31 | /* Only one ID can be queried at a time */ 32 | if (opts.query_default && opts.query_current) 33 | { 34 | help_information 35 | ("invalid option combination: querying more than one IDs: ", 64); 36 | } 37 | 38 | /* Backup, restore cannot be performed with query */ 39 | if ( 40 | (opts.backup || opts.restore) && 41 | (opts.query_default || opts.query_current) 42 | ) 43 | { help_information("invalid option combination: ", 64); } 44 | 45 | /* Cannot restore while assigning */ 46 | if (opts.restore && (opts.assign_current || opts.assign_default)) 47 | { help_information("invalid option combination: ", 64); } 48 | 49 | /* Cannot query and assign at one time */ 50 | if ( 51 | (opts.query_current || opts.query_default) && 52 | (opts.assign_current || opts.assign_default) 53 | ) 54 | { help_information("invalid option combination: ", 64); } 55 | 56 | /* Action combination is safe here, option combination still needs to check */ 57 | 58 | /* Querying */ 59 | if (opts.query_current || opts.query_default) 60 | { 61 | parse_file(opts.xml_file); 62 | 63 | if (opts.inplace) 64 | { help_information("invalid option combination: ", 64); } 65 | 66 | if (opts.package_name.length() == 0) 67 | { help_information("package name not spefified: ", 64); } 68 | 69 | std::string id = query(opts.package_name, opts.query_current); 70 | 71 | if (id.length() != 0) 72 | { std::cout << id << std::endl; } 73 | } 74 | 75 | /* Backing up */ 76 | if (opts.backup) 77 | { copy_file(opts.xml_file, opts.backup_file); } 78 | 79 | /* Assigning */ 80 | if (opts.assign_current || opts.assign_default) 81 | { 82 | parse_file(opts.xml_file); 83 | 84 | if (opts.package_name.length() == 0) 85 | { help_information("package name not spefified: ", 64); } 86 | 87 | if (opts.assign_default) 88 | { 89 | if (!opts.force) 90 | { assign_safe_guard(opts.package_name, opts.default_id); } 91 | assign(opts.package_name, opts.default_id, false); 92 | } 93 | if (opts.assign_current) 94 | { 95 | if (!opts.force) 96 | { assign_safe_guard(opts.package_name, opts.current_id); } 97 | assign(opts.package_name, opts.current_id, true); 98 | } 99 | } 100 | 101 | /* Writing back */ 102 | 103 | if (opts.assign_current || opts.assign_default) 104 | { 105 | if (opts.inplace) 106 | { write_back(get_final(), opts.xml_file); } 107 | else 108 | { print_out(get_final()); } 109 | } 110 | 111 | if (opts.restore) 112 | { copy_file(opts.restore_file, opts.xml_file); } 113 | 114 | return 0; 115 | } 116 | 117 | namespace { 118 | 119 | std::string 120 | parse_key(std::size_t p, const std::string& line) 121 | { 122 | std::string parsed_key; 123 | bool found_key = false; 124 | 125 | for (p = p - 1; p != 0; --p) 126 | { 127 | if (line[p] != ' ') 128 | { 129 | found_key = true; 130 | parsed_key.insert(parsed_key.begin(), line[p]); 131 | } 132 | else if (found_key) 133 | { break; } 134 | } 135 | 136 | return parsed_key; 137 | } 138 | 139 | std::string 140 | parse_value (std::size_t p, const std::string& line) 141 | { 142 | std::string parsed_value; 143 | bool found_value = false; 144 | 145 | for (p = p + 2; p < line.length(); ++p) 146 | { 147 | if (line[p] != '"') 148 | { 149 | found_value = true; 150 | parsed_value.push_back(line[p]); 151 | } 152 | else if (found_value) 153 | { break; } 154 | } 155 | 156 | return parsed_value; 157 | } 158 | 159 | ParsedLine 160 | parse_line(const std::string& line) 161 | { 162 | std::map records; 163 | 164 | for (std::size_t i = 0; i < line.length(); ++i) 165 | { 166 | if (line[i] == '=') 167 | { 168 | std::string key = ::parse_key(i, line); 169 | std::string value = ::parse_value(i, line); 170 | records[key] = value; 171 | } 172 | } 173 | 174 | return ParsedLine 175 | ( 176 | records["id"], records["name"], records["value"], records["package"], 177 | records["defaultValue"], records["defaultSysSet"], records["tag"] 178 | ); 179 | } 180 | 181 | std::string 182 | rewrite_line(const ParsedLine& parsed_line) 183 | { 184 | return std::string() 185 | .append(" "); 194 | } 195 | 196 | bool 197 | is_valid_device_id(const std::string& str) 198 | { 199 | if (str.length() != 16) 200 | { return false; } 201 | for (const auto& c : str) 202 | { 203 | if (c < '0' || (c > '9' && c < 'a') || c > 'f') 204 | { return false; } 205 | } 206 | return true; 207 | } 208 | 209 | bool 210 | is_valid_userkey(const std::string& str) 211 | { 212 | if (str.length() != 64) 213 | { return false; } 214 | for (auto it = str.cbegin(); it != str.cend(); ++it) 215 | { 216 | if ( 217 | (*it > '9' && *it < 'A') || 218 | (*it < '0' || static_cast(*it) > 'F') 219 | ) 220 | { return false; } 221 | } 222 | return true; 223 | } 224 | } 225 | 226 | void 227 | parse_file(const std::string& xml_file) 228 | { 229 | std::ifstream input_stream(xml_file); 230 | 231 | if (input_stream.fail()) 232 | { 233 | std::cerr << "cannot open input: " << xml_file << std::endl; 234 | exit(66); 235 | } 236 | 237 | for (std::string line; std::getline(input_stream, line); ) 238 | { 239 | ::parsed_XML_SG_.push_back(::parse_line(line)); 240 | ::orign_XML_SG_.push_back(line); 241 | } 242 | 243 | input_stream.close(); 244 | } 245 | 246 | std::string 247 | get_final(bool&& committed) 248 | { 249 | std::string final_copy; 250 | for (std::size_t i = 0; i < ::orign_XML_SG_.size(); ++i) 251 | { 252 | final_copy.append 253 | ( 254 | (::parsed_XML_SG_[i].is_crrupted() || !committed) ? 255 | ::orign_XML_SG_[i] : ::rewrite_line(::parsed_XML_SG_[i]) 256 | ) 257 | .append("\n"); 258 | } 259 | return final_copy; 260 | } 261 | 262 | void 263 | write_back(const std::string& final_copy, const std::string& xml_file) 264 | { 265 | std::ofstream output_stream(xml_file); 266 | 267 | if (output_stream.fail()) 268 | { 269 | std::cerr << "can't create (user) output file: " << xml_file << std::endl; 270 | exit(73); 271 | } 272 | 273 | output_stream << final_copy; 274 | output_stream.close(); 275 | } 276 | 277 | void 278 | write_back(std::string&& final_copy, const std::string& xml_file) 279 | { 280 | std::ofstream output_stream(xml_file); 281 | 282 | if (output_stream.fail()) 283 | { 284 | std::cerr << "can't create (user) output file: " << xml_file << std::endl; 285 | exit(73); 286 | } 287 | 288 | output_stream << final_copy; 289 | output_stream.close(); 290 | } 291 | 292 | void 293 | print_out(const std::string& final_copy) 294 | { 295 | std::cout << final_copy; 296 | } 297 | 298 | void 299 | print_out(std::string&& final_copy) 300 | { 301 | std::cout << final_copy; 302 | } 303 | 304 | std::string 305 | query(const std::string& package_name, const bool& which) 306 | { 307 | for (auto const& parsed_line : ::parsed_XML_SG_) 308 | { 309 | if (parsed_line.package == package_name) 310 | { return (which ? parsed_line.default_value : parsed_line.value); } 311 | } 312 | 313 | return std::string(); 314 | } 315 | 316 | void 317 | assign_safe_guard(const std::string& package_name, const std::string& device_id) 318 | { 319 | if (package_name == "android") 320 | { 321 | if (!::is_valid_userkey(device_id)) 322 | { 323 | std::cerr 324 | << "the designated ID is NOT a valid userkey: " 325 | << package_name 326 | << std::endl 327 | << "use --force to perform anyway" 328 | << std::endl; 329 | } 330 | std::cerr 331 | << "changing ID of this package will cause system wide ID changes: " 332 | << package_name 333 | << std::endl 334 | << "use --force to perform anyway" 335 | << std::endl; 336 | exit(1); 337 | } 338 | else if (!::is_valid_device_id(device_id)) 339 | { 340 | std::cerr 341 | << "the designated ID is NOT valid: " 342 | << device_id 343 | << std::endl 344 | << "use --force to perform anyway" 345 | << std::endl; 346 | exit(1); 347 | } 348 | } 349 | 350 | void 351 | assign 352 | ( 353 | const std::string& package_name, 354 | const std::string& device_id, 355 | const bool&& which 356 | ) 357 | { 358 | for (auto& parsed_line : ::parsed_XML_SG_) 359 | { 360 | if (parsed_line.package == package_name) 361 | { 362 | which ? 363 | parsed_line.default_value = device_id : 364 | parsed_line.value = device_id; 365 | } 366 | } 367 | } 368 | 369 | void 370 | copy_file(const std::string& src_path, const std::string& dst_path) 371 | { 372 | std::ifstream inpt_stream(src_path, std::ios::binary); 373 | std::ofstream oupt_stream(dst_path, std::ios::binary); 374 | 375 | if (inpt_stream.fail() || oupt_stream.fail()) 376 | { 377 | if (inpt_stream.fail()) 378 | { std::cerr << "cannot open input: " << src_path << std::endl; } 379 | if (oupt_stream.fail()) 380 | { std::cerr << "cannot open output: " << dst_path << std::endl; } 381 | exit(73); 382 | } 383 | 384 | oupt_stream << inpt_stream.rdbuf(); 385 | 386 | inpt_stream.close(); 387 | oupt_stream.close(); 388 | } 389 | 390 | void 391 | help_information(std::string&& exit_info, int&& error_code) 392 | { 393 | if (exit_info.length() > 0) 394 | { std::cout << exit_info << error_code << std::endl << std::endl; } 395 | std::cout << ___HELP_INFO___; 396 | exit(error_code); 397 | } 398 | 399 | Options 400 | get_options (int& argc, char** (&argv)) 401 | { 402 | int c; 403 | Options options; 404 | 405 | while ((c = getopt_long(argc, argv, opts_SG_, long_opts_SG_, nullptr)) != -1) 406 | { 407 | switch (c) 408 | { 409 | case 'a': 410 | options.assign_default = true; 411 | options.default_id = optarg; 412 | break; 413 | case 'A': 414 | options.assign_current = true; 415 | options.current_id = optarg; 416 | break; 417 | case 'q': 418 | options.query_default = true; 419 | break; 420 | case 'Q': 421 | options.query_current = true; 422 | break; 423 | case 'b': 424 | options.backup = true; 425 | options.backup_file = optarg; 426 | break; 427 | case 'r': 428 | options.restore = true; 429 | options.restore_file = optarg; 430 | break; 431 | case 'p': 432 | options.package_name = optarg; 433 | break; 434 | case 'f': 435 | options.xml_file = optarg; 436 | break; 437 | case 'i': 438 | options.inplace = true; 439 | break; 440 | case 'y': 441 | options.force = true; 442 | break; 443 | case 'h': 444 | options.help = true; 445 | break; 446 | default: 447 | help_information("", 63); 448 | } 449 | } 450 | return options; 451 | } 452 | --------------------------------------------------------------------------------