1 /* 2 * Copyright 2016-2017, 2021 Hugh McMaster 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 17 */ 18 19 #include <stdio.h> 20 #include "reg.h" 21 22 static const WCHAR *reg_type_to_wchar(DWORD type) 23 { 24 int i, array_size = ARRAY_SIZE(type_rels); 25 26 for (i = 0; i < array_size; i++) 27 { 28 if (type == type_rels[i].type) 29 return type_rels[i].name; 30 } 31 32 return NULL; 33 } 34 35 static WCHAR *reg_data_to_wchar(DWORD type, const BYTE *src, DWORD size_bytes) 36 { 37 WCHAR *buffer = NULL; 38 int i; 39 40 switch (type) 41 { 42 case REG_SZ: 43 case REG_EXPAND_SZ: 44 buffer = malloc(size_bytes); 45 lstrcpyW(buffer, (WCHAR *)src); 46 break; 47 case REG_NONE: 48 case REG_BINARY: 49 { 50 WCHAR *ptr; 51 52 buffer = malloc((size_bytes * 2 + 1) * sizeof(WCHAR)); 53 54 if (!size_bytes) 55 { 56 *buffer = 0; 57 break; 58 } 59 60 ptr = buffer; 61 62 for (i = 0; i < size_bytes; i++) 63 ptr += swprintf(ptr, L"%02X", src[i]); 64 break; 65 } 66 case REG_DWORD: 67 /* case REG_DWORD_LITTLE_ENDIAN: */ 68 case REG_DWORD_BIG_ENDIAN: 69 { 70 const int zero_x_dword = 10; 71 72 buffer = malloc((zero_x_dword + 1) * sizeof(WCHAR)); 73 swprintf(buffer, L"0x%x", *(DWORD *)src); 74 break; 75 } 76 case REG_MULTI_SZ: 77 { 78 const int two_wchars = 2 * sizeof(WCHAR); 79 DWORD tmp_size; 80 const WCHAR *tmp = (const WCHAR *)src; 81 int len, destindex; 82 83 if (size_bytes <= two_wchars) 84 { 85 buffer = malloc(sizeof(WCHAR)); 86 *buffer = 0; 87 return buffer; 88 } 89 90 tmp_size = size_bytes - two_wchars; /* exclude both null terminators */ 91 buffer = malloc(tmp_size * 2 + sizeof(WCHAR)); 92 len = tmp_size / sizeof(WCHAR); 93 94 for (i = 0, destindex = 0; i < len; i++, destindex++) 95 { 96 if (tmp[i]) 97 buffer[destindex] = tmp[i]; 98 else 99 { 100 buffer[destindex++] = '\\'; 101 buffer[destindex] = '0'; 102 } 103 } 104 buffer[destindex] = 0; 105 break; 106 } 107 } 108 return buffer; 109 } 110 111 static const WCHAR *newlineW = L"\n"; 112 113 static void output_value(const WCHAR *value_name, DWORD type, BYTE *data, DWORD data_size) 114 { 115 static const WCHAR *fmt = L" %1"; 116 WCHAR defval[32]; 117 WCHAR *reg_data; 118 119 if (value_name && value_name[0]) 120 output_string(fmt, value_name); 121 else 122 { 123 LoadStringW(GetModuleHandleW(NULL), STRING_DEFAULT_VALUE, defval, ARRAY_SIZE(defval)); 124 output_string(fmt, defval); 125 } 126 output_string(fmt, reg_type_to_wchar(type)); 127 128 if (data) 129 { 130 reg_data = reg_data_to_wchar(type, data, data_size); 131 output_string(fmt, reg_data); 132 free(reg_data); 133 } 134 else 135 { 136 LoadStringW(GetModuleHandleW(NULL), STRING_VALUE_NOT_SET, defval, ARRAY_SIZE(defval)); 137 output_string(fmt, defval); 138 } 139 output_string(newlineW); 140 } 141 142 static unsigned int num_values_found = 0; 143 static REGSAM sam = 0; 144 145 static int query_value(HKEY hkey, WCHAR *value_name, WCHAR *path, BOOL recurse) 146 { 147 LONG rc; 148 DWORD max_data_bytes = 2048, data_size; 149 DWORD subkey_len; 150 DWORD type, path_len, i; 151 BYTE *data; 152 static const WCHAR *fmt = L"%1\n"; 153 WCHAR *subkey_name, *subkey_path; 154 HKEY subkey; 155 156 data = malloc(max_data_bytes); 157 158 for (;;) 159 { 160 data_size = max_data_bytes; 161 rc = RegQueryValueExW(hkey, value_name, NULL, &type, data, &data_size); 162 if (rc == ERROR_MORE_DATA) 163 { 164 max_data_bytes = data_size; 165 data = realloc(data, max_data_bytes); 166 } 167 else break; 168 } 169 170 if (rc == ERROR_SUCCESS) 171 { 172 output_string(fmt, path); 173 output_value(value_name, type, data, data_size); 174 output_string(newlineW); 175 num_values_found++; 176 } 177 178 free(data); 179 180 if (!recurse) 181 { 182 if (rc == ERROR_FILE_NOT_FOUND) 183 { 184 if (value_name && *value_name) 185 { 186 output_message(STRING_VALUE_NONEXIST); 187 return 1; 188 } 189 output_string(fmt, path); 190 output_value(NULL, REG_SZ, NULL, 0); 191 } 192 return 0; 193 } 194 195 subkey_name = malloc(MAX_SUBKEY_LEN * sizeof(WCHAR)); 196 197 path_len = lstrlenW(path); 198 199 i = 0; 200 for (;;) 201 { 202 subkey_len = MAX_SUBKEY_LEN; 203 rc = RegEnumKeyExW(hkey, i, subkey_name, &subkey_len, NULL, NULL, NULL, NULL); 204 if (rc == ERROR_SUCCESS) 205 { 206 subkey_path = build_subkey_path(path, path_len, subkey_name, subkey_len); 207 if (!RegOpenKeyExW(hkey, subkey_name, 0, KEY_READ|sam, &subkey)) 208 { 209 query_value(subkey, value_name, subkey_path, recurse); 210 RegCloseKey(subkey); 211 } 212 free(subkey_path); 213 i++; 214 } 215 else break; 216 } 217 218 free(subkey_name); 219 return 0; 220 } 221 222 static int query_all(HKEY hkey, WCHAR *path, BOOL recurse, BOOL recursing) 223 { 224 LONG rc; 225 DWORD num_subkeys, num_values; 226 DWORD max_value_len = 256, value_len; 227 DWORD max_data_bytes = 2048, data_size; 228 DWORD subkey_len; 229 DWORD i, type, path_len; 230 WCHAR *value_name, *subkey_name, *subkey_path; 231 BYTE *data; 232 HKEY subkey; 233 234 rc = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, &num_subkeys, NULL, 235 NULL, &num_values, NULL, NULL, NULL, NULL); 236 if (rc) return 1; 237 238 if (num_values || recursing) 239 output_string(L"%1\n", path); 240 241 value_name = malloc(max_value_len * sizeof(WCHAR)); 242 data = malloc(max_data_bytes); 243 244 i = 0; 245 for (;;) 246 { 247 value_len = max_value_len; 248 data_size = max_data_bytes; 249 rc = RegEnumValueW(hkey, i, value_name, &value_len, NULL, &type, data, &data_size); 250 if (rc == ERROR_SUCCESS) 251 { 252 output_value(value_name, type, data, data_size); 253 i++; 254 } 255 else if (rc == ERROR_MORE_DATA) 256 { 257 if (data_size > max_data_bytes) 258 { 259 max_data_bytes = data_size; 260 data = realloc(data, max_data_bytes); 261 } 262 else 263 { 264 max_value_len *= 2; 265 value_name = realloc(value_name, max_value_len * sizeof(WCHAR)); 266 } 267 } 268 else break; 269 } 270 271 free(data); 272 free(value_name); 273 274 if (i || recursing) 275 output_string(newlineW); 276 277 if (!num_subkeys) 278 return 0; 279 280 subkey_name = malloc(MAX_SUBKEY_LEN * sizeof(WCHAR)); 281 282 path_len = lstrlenW(path); 283 284 i = 0; 285 for (;;) 286 { 287 subkey_len = MAX_SUBKEY_LEN; 288 rc = RegEnumKeyExW(hkey, i, subkey_name, &subkey_len, NULL, NULL, NULL, NULL); 289 if (rc == ERROR_SUCCESS) 290 { 291 if (recurse) 292 { 293 subkey_path = build_subkey_path(path, path_len, subkey_name, subkey_len); 294 if (!RegOpenKeyExW(hkey, subkey_name, 0, KEY_READ|sam, &subkey)) 295 { 296 query_all(subkey, subkey_path, recurse, TRUE); 297 RegCloseKey(subkey); 298 } 299 free(subkey_path); 300 } 301 else output_string(L"%1\\%2\n", path, subkey_name); 302 i++; 303 } 304 else break; 305 } 306 307 free(subkey_name); 308 return 0; 309 } 310 311 static int run_query(HKEY root, WCHAR *path, WCHAR *key_name, WCHAR *value_name, 312 BOOL value_empty, BOOL recurse) 313 { 314 HKEY hkey; 315 int ret; 316 317 if (RegOpenKeyExW(root, path, 0, KEY_READ|sam, &hkey)) 318 { 319 output_message(STRING_KEY_NONEXIST); 320 return 1; 321 } 322 323 output_string(newlineW); 324 325 if (value_name || value_empty) 326 { 327 ret = query_value(hkey, value_name, key_name, recurse); 328 if (recurse) 329 output_message(STRING_MATCHES_FOUND, num_values_found); 330 } 331 else 332 ret = query_all(hkey, key_name, recurse, FALSE); 333 334 RegCloseKey(hkey); 335 336 return ret; 337 } 338 339 int reg_query(int argc, WCHAR *argvW[]) 340 { 341 HKEY root; 342 WCHAR *path, *key_name, *value_name = NULL; 343 BOOL value_empty = FALSE, recurse = FALSE; 344 int i; 345 346 if (!parse_registry_key(argvW[2], &root, &path)) 347 return 1; 348 349 for (i = 3; i < argc; i++) 350 { 351 WCHAR *str; 352 353 if (argvW[i][0] != '/' && argvW[i][0] != '-') 354 goto invalid; 355 356 str = &argvW[i][1]; 357 358 if (!lstrcmpiW(str, L"ve")) 359 { 360 if (value_empty) goto invalid; 361 value_empty = TRUE; 362 continue; 363 } 364 else if (!lstrcmpiW(str, L"reg:32")) 365 { 366 if (sam & KEY_WOW64_32KEY) goto invalid; 367 sam |= KEY_WOW64_32KEY; 368 continue; 369 } 370 else if (!lstrcmpiW(str, L"reg:64")) 371 { 372 if (sam & KEY_WOW64_64KEY) goto invalid; 373 sam |= KEY_WOW64_64KEY; 374 continue; 375 } 376 else if (!str[0] || str[1]) 377 goto invalid; 378 379 switch (towlower(*str)) 380 { 381 case 'v': 382 if (value_name || !(value_name = argvW[++i])) 383 goto invalid; 384 break; 385 case 's': 386 if (recurse) goto invalid; 387 recurse = TRUE; 388 break; 389 default: 390 goto invalid; 391 } 392 } 393 394 if (value_name && value_empty) 395 goto invalid; 396 397 if (sam == (KEY_WOW64_32KEY|KEY_WOW64_64KEY)) 398 goto invalid; 399 400 key_name = get_long_key(root, path); 401 402 return run_query(root, path, key_name, value_name, value_empty, recurse); 403 404 invalid: 405 output_message(STRING_INVALID_SYNTAX); 406 output_message(STRING_FUNC_HELP, _wcsupr(argvW[1])); 407 return 1; 408 } 409