├── .gitignore ├── Dockerfile ├── README.md ├── anisette_extract ├── AltWindowsAnisette.cpp ├── AltWindowsAnisette.sln ├── AltWindowsAnisette.vcxproj ├── AltWindowsAnisette.vcxproj.filters ├── AltWindowsAnisette.vcxproj.user ├── Release │ ├── AltWindowsAnisette.exe │ └── AltWindowsAnisette.pdb └── base64.h ├── http_wrap ├── README.md ├── server.exe ├── server.js └── server_linux.js └── scripts ├── icloud_login.ahk ├── install_icloud.sh └── run_icloud.sh /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | Debug 3 | Release 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM nyamisty/docker-wine-dotnet:win32 2 | 3 | RUN mkdir -p /tmp/iCloud \ 4 | && wget -O /tmp/iCloud/iCloudSetup.exe http://updates-http.cdn-apple.com/2020/windows/001-39935-20200911-1A70AA56-F448-11EA-8CC0-99D41950005E/iCloudSetup.exe \ 5 | && true 6 | 7 | ADD scripts/install_icloud.sh /app/ 8 | RUN entrypoint true \ 9 | && cd /tmp/iCloud \ 10 | && bash /app/install_icloud.sh \ 11 | && rm -rf /tmp/iCloud 12 | 13 | RUN apt-get update && apt-get install -y p7zip-full vim nodejs 14 | 15 | RUN mkdir -p /tmp/ahk && cd /tmp/ahk \ 16 | && wget -O /tmp/ahk/ahk.exe https://github.com/Lexikos/AutoHotkey_L/releases/download/v1.1.33.10/AutoHotkey_1.1.33.10_setup.exe \ 17 | && (mkdir /ahk && cd /ahk && 7z x /tmp/ahk/ahk.exe AutoHotkeyA32.exe AutoHotkeyU32.exe AutoHotkeyU64.exe WindowSpy.ahk) \ 18 | && chmod +x /ahk/AutoHotkey*.exe 19 | 20 | ADD http_wrap/server* /app/ 21 | ADD anisette_extract/Release/AltWindowsAnisette.exe /app/ 22 | ADD scripts/* /app/ 23 | 24 | EXPOSE 6969 25 | 26 | CMD ["/app/run_icloud.sh"] 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # alt-anisette-server 2 | 3 | Anisette Data as Service, like Sideloadly's, based on AltServer-Windows! 4 | 5 | ## What's Anisette Data 6 | 7 | An unknown data generated by Apple softwares, currently mainly used by AltServer to login Apple Developer 8 | 9 | ## How to use 10 | 11 | To start it: 12 | 13 | ``` 14 | docker pull nyamisty/alt_anisette_server 15 | docker run -d --rm -p 6969:6969 -it nyamisty/alt_anisette_server 16 | ``` 17 | 18 | After seeing "Server running on ..." message, you can do `curl 127.0.0.1:6969` to see the Anisette data returned 19 | 20 | ## Credits 21 | 22 | - Sideloadly: for idea of AnisetteData-as-Service 23 | 24 | - AltServer-Windows: for code to extract Anisette Data from Windows iTunes 25 | 26 | -------------------------------------------------------------------------------- /anisette_extract/AltWindowsAnisette.cpp: -------------------------------------------------------------------------------- 1 | #define _CRT_SECURE_NO_WARNINGS 2 | #include 3 | namespace fs = std::filesystem; 4 | 5 | //#define _SILENCE_EXPERIMENTAL_FILESYSTEM_DEPRECATION_WARNING 6 | //#include 7 | //namespace fs = std::experimental::filesystem; 8 | 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #pragma region Error 19 | 20 | class Error : public std::exception 21 | { 22 | public: 23 | Error(int code) : _code(code), _userInfo(std::map()) 24 | { 25 | } 26 | 27 | Error(int code, std::map userInfo) : _code(code), _userInfo(userInfo) 28 | { 29 | } 30 | 31 | virtual std::string localizedDescription() const 32 | { 33 | return ""; 34 | } 35 | 36 | int code() const 37 | { 38 | return _code; 39 | } 40 | 41 | std::map userInfo() const 42 | { 43 | return _userInfo; 44 | } 45 | 46 | virtual std::string domain() const 47 | { 48 | return "com.rileytestut.AltServer.Error"; 49 | } 50 | 51 | friend std::ostream& operator<<(std::ostream& os, const Error& error) 52 | { 53 | os << "Error: (" << error.domain() << "): " << error.localizedDescription() << " (" << error.code() << ")"; 54 | return os; 55 | } 56 | 57 | private: 58 | int _code; 59 | std::map _userInfo; 60 | }; 61 | 62 | typedef std::map AnisetteData; 63 | 64 | enum class AnisetteErrorCode 65 | { 66 | iTunesNotInstalled, 67 | iCloudNotInstalled, 68 | MissingApplicationSupportFolder, 69 | MissingAOSKit, 70 | MissingObjc, 71 | MissingFoundation, 72 | InvalidiTunesInstallation, 73 | }; 74 | 75 | class AnisetteError : Error 76 | { 77 | public: 78 | AnisetteError(AnisetteErrorCode code) : Error((int)code) 79 | { 80 | } 81 | 82 | virtual std::string domain() const 83 | { 84 | return "com.rileytestut.AltServer.AnisetteError"; 85 | } 86 | 87 | virtual std::string localizedDescription() const 88 | { 89 | if (this->_localizedDescription.size() > 0) 90 | { 91 | return this->_localizedDescription; 92 | } 93 | 94 | switch ((AnisetteErrorCode)this->code()) 95 | { 96 | case AnisetteErrorCode::iTunesNotInstalled: return "iTunes Not Found"; 97 | case AnisetteErrorCode::iCloudNotInstalled: return "iCloud Not Found"; 98 | case AnisetteErrorCode::MissingApplicationSupportFolder: return "Missing 'Application Support' in 'Apple' Folder."; 99 | case AnisetteErrorCode::MissingAOSKit: return "Missing 'AOSKit.dll' in 'Internet Services' Folder."; 100 | case AnisetteErrorCode::MissingFoundation: return "Missing 'Foundation.dll' in 'Apple Application Support' Folder."; 101 | case AnisetteErrorCode::MissingObjc: return "Missing 'objc.dll' in 'Apple Application Support' Folder."; 102 | case AnisetteErrorCode::InvalidiTunesInstallation: return "Invalid iTunes installation."; 103 | } 104 | 105 | return ""; 106 | } 107 | 108 | void setLocalizedDescription(std::string localizedDescription) 109 | { 110 | _localizedDescription = localizedDescription; 111 | } 112 | 113 | private: 114 | std::string _localizedDescription; 115 | }; 116 | 117 | #pragma endregion Error 118 | 119 | class AnisetteDataManager 120 | { 121 | public: 122 | static AnisetteDataManager* instance(); 123 | 124 | std::shared_ptr FetchAnisetteData(); 125 | bool LoadDependencies(); 126 | 127 | bool ResetProvisioning(); 128 | 129 | private: 130 | AnisetteDataManager(); 131 | ~AnisetteDataManager(); 132 | 133 | static AnisetteDataManager* _instance; 134 | 135 | bool ReprovisionDevice(std::function provisionCallback); 136 | bool LoadiCloudDependencies(); 137 | 138 | bool loadedDependencies; 139 | }; 140 | 141 | #include 142 | 143 | #include "base64.h" 144 | 145 | std::string getServerID() { 146 | return "5E03738B-03F6-46B5-9A6A-F022279B3040"; 147 | } 148 | 149 | std::string appleFolderPath() 150 | { 151 | wchar_t* programFilesCommonDirectory; 152 | SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0, NULL, &programFilesCommonDirectory); 153 | 154 | fs::path appleDirectoryPath(programFilesCommonDirectory); 155 | appleDirectoryPath.append("Apple"); 156 | 157 | return appleDirectoryPath.string(); 158 | } 159 | 160 | std::string internetServicesFolderPath() 161 | { 162 | fs::path internetServicesDirectoryPath(appleFolderPath()); 163 | internetServicesDirectoryPath.append("Internet Services"); 164 | return internetServicesDirectoryPath.string(); 165 | } 166 | 167 | std::string applicationSupportFolderPath() 168 | { 169 | fs::path applicationSupportDirectoryPath(appleFolderPath()); 170 | applicationSupportDirectoryPath.append("Apple Application Support"); 171 | return applicationSupportDirectoryPath.string(); 172 | } 173 | 174 | //#include 175 | #include 176 | //#include 177 | 178 | #include 179 | 180 | //#define SPOOF_MAC 1 181 | 182 | #define id void* 183 | #define SEL void* 184 | 185 | typedef id(__cdecl* GETCLASSFUNC)(const char* name); 186 | typedef id(__cdecl* REGISTERSELFUNC)(const char* name); 187 | typedef id(__cdecl* SENDMSGFUNC)(id self, void* _cmd); 188 | typedef id(__cdecl* SENDMSGFUNC_OBJ)(id self, void* _cmd, id parameter1); 189 | typedef id(__cdecl* SENDMSGFUNC_INT)(id self, void* _cmd, int parameter1); 190 | typedef id* (__cdecl* COPYMETHODLISTFUNC)(id cls, unsigned int* outCount); 191 | typedef id(__cdecl* GETMETHODNAMEFUNC)(id method); 192 | typedef const char* (__cdecl* GETSELNAMEFUNC)(SEL sel); 193 | typedef id(__cdecl* GETOBJCCLASSFUNC)(id obj); 194 | 195 | typedef id(__cdecl* GETOBJECTFUNC)(); 196 | 197 | typedef id(__cdecl* CLIENTINFOFUNC)(id obj); 198 | typedef id(__cdecl* COPYANISETTEDATAFUNC)(void*, int, void*); 199 | 200 | #define odslog(msg) { std::stringstream ss; ss << msg << std::endl; OutputDebugStringA(ss.str().c_str()); } 201 | 202 | extern std::string StringFromWideString(std::wstring wideString); 203 | 204 | GETCLASSFUNC objc_getClass; 205 | REGISTERSELFUNC sel_registerName; 206 | SENDMSGFUNC objc_msgSend; 207 | COPYMETHODLISTFUNC class_copyMethodList; 208 | GETMETHODNAMEFUNC method_getName; 209 | GETSELNAMEFUNC sel_getName; 210 | GETOBJCCLASSFUNC object_getClass; 211 | 212 | GETOBJECTFUNC GetDeviceID; 213 | GETOBJECTFUNC GetLocalUserID; 214 | CLIENTINFOFUNC GetClientInfo; 215 | COPYANISETTEDATAFUNC CopyAnisetteData; 216 | 217 | class ObjcObject 218 | { 219 | public: 220 | id isa; 221 | 222 | std::string description() const 223 | { 224 | id descriptionSEL = sel_registerName("description"); 225 | id descriptionNSString = objc_msgSend((void*)this, descriptionSEL); 226 | 227 | id cDescriptionSEL = sel_registerName("UTF8String"); 228 | const char* cDescription = ((const char* (*)(id, SEL))objc_msgSend)(descriptionNSString, cDescriptionSEL); 229 | 230 | std::string description(cDescription); 231 | return description; 232 | } 233 | }; 234 | 235 | 236 | id __cdecl ALTClientInfoReplacementFunction(void*) 237 | { 238 | ObjcObject* NSString = (ObjcObject*)objc_getClass("NSString"); 239 | id stringInit = sel_registerName("stringWithUTF8String:"); 240 | 241 | ObjcObject* clientInfo = (ObjcObject*)((id(*)(id, SEL, const char*))objc_msgSend)(NSString, stringInit, " "); 242 | 243 | odslog("Swizzled Client Info: " << clientInfo->description()); 244 | 245 | return clientInfo; 246 | } 247 | 248 | id __cdecl ALTDeviceIDReplacementFunction() 249 | { 250 | ObjcObject* NSString = (ObjcObject*)objc_getClass("NSString"); 251 | id stringInit = sel_registerName("stringWithUTF8String:"); 252 | 253 | auto deviceIDString = getServerID(); 254 | 255 | ObjcObject* deviceID = (ObjcObject*)((id(*)(id, SEL, const char*))objc_msgSend)(NSString, stringInit, deviceIDString.c_str()); 256 | 257 | odslog("Swizzled Device ID: " << deviceID->description()); 258 | 259 | return deviceID; 260 | } 261 | 262 | void convert_filetime(struct timeval* out_tv, const FILETIME* filetime) 263 | { 264 | // Microseconds between 1601-01-01 00:00:00 UTC and 1970-01-01 00:00:00 UTC 265 | static const uint64_t EPOCH_DIFFERENCE_MICROS = 11644473600000000ull; 266 | 267 | // First convert 100-ns intervals to microseconds, then adjust for the 268 | // epoch difference 269 | uint64_t total_us = (((uint64_t)filetime->dwHighDateTime << 32) | (uint64_t)filetime->dwLowDateTime) / 10; 270 | total_us -= EPOCH_DIFFERENCE_MICROS; 271 | 272 | // Convert to (seconds, microseconds) 273 | out_tv->tv_sec = (time_t)(total_us / 1000000); 274 | out_tv->tv_usec = (long)(total_us % 1000000); 275 | } 276 | 277 | AnisetteDataManager* AnisetteDataManager::_instance = nullptr; 278 | 279 | AnisetteDataManager* AnisetteDataManager::instance() 280 | { 281 | if (_instance == 0) 282 | { 283 | _instance = new AnisetteDataManager(); 284 | } 285 | 286 | return _instance; 287 | } 288 | 289 | AnisetteDataManager::AnisetteDataManager() : loadedDependencies(false) 290 | { 291 | } 292 | 293 | AnisetteDataManager::~AnisetteDataManager() 294 | { 295 | } 296 | 297 | #define JUMP_INSTRUCTION_SIZE 5 // 0x86 jump instruction is 5 bytes total (opcode + 4 byte address). 298 | 299 | bool AnisetteDataManager::LoadiCloudDependencies() 300 | { 301 | wchar_t* programFilesCommonDirectory; 302 | SHGetKnownFolderPath(FOLDERID_ProgramFilesCommon, 0, NULL, &programFilesCommonDirectory); 303 | 304 | fs::path appleDirectoryPath(programFilesCommonDirectory); 305 | appleDirectoryPath.append("Apple"); 306 | 307 | fs::path internetServicesDirectoryPath(appleDirectoryPath); 308 | internetServicesDirectoryPath.append("Internet Services"); 309 | 310 | fs::path iCloudMainPath(internetServicesDirectoryPath); 311 | iCloudMainPath.append("iCloud_main.dll"); 312 | 313 | HINSTANCE iCloudMain = LoadLibrary(iCloudMainPath.c_str()); 314 | if (iCloudMain == NULL) 315 | { 316 | return false; 317 | } 318 | 319 | // Retrieve known exported function address to provide reference point for accessing private functions. 320 | uintptr_t exportedFunctionAddress = (uintptr_t)GetProcAddress(iCloudMain, "PL_FreeArenaPool"); 321 | size_t exportedFunctionDisassembledOffset = 0x1aa2a0; 322 | 323 | 324 | /* Reprovision Anisette Function */ 325 | 326 | size_t anisetteFunctionDisassembledOffset = 0x241ee0; 327 | size_t difference = anisetteFunctionDisassembledOffset - 0x1aa2a0; 328 | 329 | CopyAnisetteData = (COPYANISETTEDATAFUNC)(exportedFunctionAddress + difference); 330 | if (CopyAnisetteData == NULL) 331 | { 332 | return false; 333 | } 334 | 335 | 336 | /* Anisette Data Functions */ 337 | 338 | size_t clientInfoFunctionDisassembledOffset = 0x23e730; 339 | size_t clientInfoFunctionRelativeOffset = clientInfoFunctionDisassembledOffset - exportedFunctionDisassembledOffset; 340 | GetClientInfo = (CLIENTINFOFUNC)(exportedFunctionAddress + clientInfoFunctionRelativeOffset); 341 | 342 | size_t deviceIDFunctionDisassembledOffset = 0x23d8b0; 343 | size_t deviceIDFunctionRelativeOffset = deviceIDFunctionDisassembledOffset - exportedFunctionDisassembledOffset; 344 | GetDeviceID = (GETOBJECTFUNC)(exportedFunctionAddress + deviceIDFunctionRelativeOffset); 345 | 346 | size_t localUserIDFunctionDisassembledOffset = 0x23db30; 347 | size_t localUserIDFunctionRelativeOffset = localUserIDFunctionDisassembledOffset - exportedFunctionDisassembledOffset; 348 | GetLocalUserID = (GETOBJECTFUNC)(exportedFunctionAddress + localUserIDFunctionRelativeOffset); 349 | 350 | if (GetClientInfo == NULL || GetDeviceID == NULL || GetLocalUserID == NULL) 351 | { 352 | return false; 353 | } 354 | 355 | { 356 | /** Client Info Swizzling */ 357 | 358 | int64_t* targetFunction = (int64_t*)GetClientInfo; 359 | int64_t* replacementFunction = (int64_t*)&ALTClientInfoReplacementFunction; 360 | 361 | SYSTEM_INFO system; 362 | GetSystemInfo(&system); 363 | int pageSize = system.dwAllocationGranularity; 364 | 365 | uintptr_t startAddress = (uintptr_t)targetFunction; 366 | uintptr_t endAddress = startAddress + 1; 367 | uintptr_t pageStart = startAddress & -pageSize; 368 | 369 | // Mark page containing the target function implementation as writable so we can inject our own instruction. 370 | DWORD permissions = 0; 371 | BOOL value = VirtualProtect((LPVOID)pageStart, endAddress - pageStart, PAGE_EXECUTE_READWRITE, &permissions); 372 | 373 | if (!value) 374 | { 375 | return false; 376 | } 377 | 378 | int32_t jumpOffset = (int64_t)replacementFunction - ((int64_t)targetFunction + JUMP_INSTRUCTION_SIZE); // Add jumpInstructionSize because offset is relative to _next_ instruction. 379 | 380 | // Construct jump instruction. 381 | // Jump doesn't return execution to target function afterwards, allowing us to completely replace the implementation. 382 | char instruction[5]; 383 | instruction[0] = '\xE9'; // E9 = "Jump near (relative)" opcode 384 | ((int32_t*)(instruction + 1))[0] = jumpOffset; // Next 4 bytes = jump offset 385 | 386 | // Replace first instruction in target target function with our unconditional jump to replacement function. 387 | char* functionImplementation = (char*)targetFunction; 388 | for (int i = 0; i < JUMP_INSTRUCTION_SIZE; i++) 389 | { 390 | functionImplementation[i] = instruction[i]; 391 | } 392 | } 393 | 394 | { 395 | /** Device ID Swizzling */ 396 | 397 | int64_t* targetFunction = (int64_t*)GetDeviceID; 398 | int64_t* replacementFunction = (int64_t*)&ALTDeviceIDReplacementFunction; 399 | 400 | SYSTEM_INFO system; 401 | GetSystemInfo(&system); 402 | int pageSize = system.dwAllocationGranularity; 403 | 404 | uintptr_t startAddress = (uintptr_t)targetFunction; 405 | uintptr_t endAddress = startAddress + 1; 406 | uintptr_t pageStart = startAddress & -pageSize; 407 | 408 | // Mark page containing the target function implementation as writable so we can inject our own instruction. 409 | DWORD permissions = 0; 410 | BOOL value = VirtualProtect((LPVOID)pageStart, endAddress - pageStart, PAGE_EXECUTE_READWRITE, &permissions); 411 | 412 | if (!value) 413 | { 414 | return false; 415 | } 416 | 417 | int32_t jumpOffset = (int64_t)replacementFunction - ((int64_t)targetFunction + JUMP_INSTRUCTION_SIZE); // Add jumpInstructionSize because offset is relative to _next_ instruction. 418 | 419 | // Construct jump instruction. 420 | // Jump doesn't return execution to target function afterwards, allowing us to completely replace the implementation. 421 | char instruction[5]; 422 | instruction[0] = '\xE9'; // E9 = "Jump near (relative)" opcode 423 | ((int32_t*)(instruction + 1))[0] = jumpOffset; // Next 4 bytes = jump offset 424 | 425 | // Replace first instruction in target target function with our unconditional jump to replacement function. 426 | char* functionImplementation = (char*)targetFunction; 427 | for (int i = 0; i < JUMP_INSTRUCTION_SIZE; i++) 428 | { 429 | functionImplementation[i] = instruction[i]; 430 | } 431 | } 432 | } 433 | 434 | bool AnisetteDataManager::LoadDependencies() 435 | { 436 | fs::path appleFolderPath(appleFolderPath()); 437 | if (!fs::exists(appleFolderPath)) 438 | { 439 | throw AnisetteError(AnisetteErrorCode::iTunesNotInstalled); 440 | } 441 | 442 | fs::path internetServicesDirectoryPath(internetServicesFolderPath()); 443 | if (!fs::exists(internetServicesDirectoryPath)) 444 | { 445 | throw AnisetteError(AnisetteErrorCode::iCloudNotInstalled); 446 | } 447 | 448 | fs::path aosKitPath(internetServicesDirectoryPath); 449 | aosKitPath.append("AOSKit.dll"); 450 | 451 | if (!fs::exists(aosKitPath)) 452 | { 453 | throw AnisetteError(AnisetteErrorCode::MissingAOSKit); 454 | } 455 | 456 | fs::path applicationSupportDirectoryPath(applicationSupportFolderPath()); 457 | if (!fs::exists(applicationSupportDirectoryPath)) 458 | { 459 | throw AnisetteError(AnisetteErrorCode::MissingApplicationSupportFolder); 460 | } 461 | 462 | fs::path objcPath(applicationSupportDirectoryPath); 463 | objcPath.append("objc.dll"); 464 | 465 | if (!fs::exists(objcPath)) 466 | { 467 | throw AnisetteError(AnisetteErrorCode::MissingObjc); 468 | } 469 | 470 | fs::path foundationPath(applicationSupportDirectoryPath); 471 | foundationPath.append("Foundation.dll"); 472 | 473 | if (!fs::exists(foundationPath)) 474 | { 475 | throw AnisetteError(AnisetteErrorCode::MissingFoundation); 476 | } 477 | 478 | BOOL result = SetCurrentDirectory(applicationSupportDirectoryPath.c_str()); 479 | DWORD dwError = GetLastError(); 480 | 481 | HINSTANCE objcLibrary = LoadLibrary(objcPath.c_str()); 482 | HINSTANCE foundationLibrary = LoadLibrary(foundationPath.c_str()); 483 | HINSTANCE AOSKit = LoadLibrary(aosKitPath.c_str()); 484 | 485 | dwError = GetLastError(); 486 | 487 | if (objcLibrary == NULL || AOSKit == NULL || foundationLibrary == NULL) 488 | { 489 | char buffer[256]; 490 | FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 491 | MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), buffer, 256, NULL); 492 | 493 | auto error = AnisetteError(AnisetteErrorCode::InvalidiTunesInstallation); 494 | error.setLocalizedDescription(buffer); 495 | throw error; 496 | } 497 | 498 | /* Objective-C runtime functions */ 499 | 500 | objc_getClass = (GETCLASSFUNC)GetProcAddress(objcLibrary, "objc_getClass"); 501 | sel_registerName = (REGISTERSELFUNC)GetProcAddress(objcLibrary, "sel_registerName"); 502 | objc_msgSend = (SENDMSGFUNC)GetProcAddress(objcLibrary, "objc_msgSend"); 503 | 504 | class_copyMethodList = (COPYMETHODLISTFUNC)GetProcAddress(objcLibrary, "class_copyMethodList"); 505 | method_getName = (GETMETHODNAMEFUNC)GetProcAddress(objcLibrary, "method_getName"); 506 | sel_getName = (GETSELNAMEFUNC)GetProcAddress(objcLibrary, "sel_getName"); 507 | object_getClass = (GETOBJCCLASSFUNC)GetProcAddress(objcLibrary, "object_getClass"); 508 | 509 | if (objc_getClass == NULL) 510 | { 511 | throw AnisetteError(AnisetteErrorCode::InvalidiTunesInstallation); 512 | } 513 | 514 | #if SPOOF_MAC 515 | if (!this->LoadiCloudDependencies()) 516 | { 517 | return false; 518 | } 519 | #endif 520 | 521 | this->loadedDependencies = true; 522 | 523 | return true; 524 | } 525 | 526 | std::shared_ptr AnisetteDataManager::FetchAnisetteData() 527 | { 528 | if (!this->loadedDependencies) 529 | { 530 | this->LoadDependencies(); 531 | } 532 | 533 | #if SPOOF_MAC 534 | if (GetClientInfo == NULL || GetDeviceID == NULL || GetLocalUserID == NULL) 535 | { 536 | return NULL; 537 | } 538 | #endif 539 | 540 | std::shared_ptr anisetteData = NULL; 541 | 542 | this->ReprovisionDevice([&anisetteData]() { 543 | // Device is temporarily provisioned as a Mac, so access anisette data now. 544 | 545 | ObjcObject* NSString = (ObjcObject*)objc_getClass("NSString"); 546 | id stringInit = sel_registerName("stringWithUTF8String:"); 547 | 548 | /* One-Time Pasword */ 549 | ObjcObject* dsidString = (ObjcObject*)((id(*)(id, SEL, const char*))objc_msgSend)(NSString, stringInit, "-2"); 550 | ObjcObject* machineIDKey = (ObjcObject*)((id(*)(id, SEL, const char*))objc_msgSend)(NSString, stringInit, "X-Apple-MD-M"); 551 | ObjcObject* otpKey = (ObjcObject*)((id(*)(id, SEL, const char*))objc_msgSend)(NSString, stringInit, "X-Apple-MD"); 552 | 553 | ObjcObject* AOSUtilities = (ObjcObject*)objc_getClass("AOSUtilities"); 554 | ObjcObject* headers = (ObjcObject*)((id(*)(id, SEL, id))objc_msgSend)(AOSUtilities, sel_registerName("retrieveOTPHeadersForDSID:"), dsidString); 555 | 556 | ObjcObject* machineID = (ObjcObject*)((id(*)(id, SEL, id))objc_msgSend)(headers, sel_registerName("objectForKey:"), machineIDKey); 557 | ObjcObject* otp = (ObjcObject*)((id(*)(id, SEL, id))objc_msgSend)(headers, sel_registerName("objectForKey:"), otpKey); 558 | 559 | if (otp == NULL || machineID == NULL) 560 | { 561 | std::cerr << "otp or machine is Null!" << std::endl; 562 | return; 563 | } 564 | 565 | odslog("OTP: " << otp->description() << " MachineID: " << machineID->description()); 566 | 567 | /* Device Hardware */ 568 | 569 | ObjcObject* deviceDescription = (ObjcObject*)ALTClientInfoReplacementFunction(NULL); 570 | ObjcObject* deviceID = (ObjcObject*)ALTDeviceIDReplacementFunction(); 571 | 572 | if (deviceDescription == NULL || deviceID == NULL) 573 | { 574 | std::cerr << "device Info is Null!" << std::endl; 575 | return; 576 | } 577 | 578 | #if SPOOF_MAC 579 | ObjcObject* localUserID = (ObjcObject*)GetLocalUserID(); 580 | #else 581 | std::string description = deviceID->description(); 582 | 583 | std::vector deviceIDData(description.begin(), description.end()); 584 | //auto encodedDeviceID = StringFromWideString(utility::conversions::to_base64(deviceIDData)); 585 | auto encodedDeviceID = macaron::Base64::Encode(deviceIDData); 586 | 587 | ObjcObject* localUserID = (ObjcObject*)((id(*)(id, SEL, const char*))objc_msgSend)(NSString, stringInit, encodedDeviceID.c_str()); 588 | #endif 589 | 590 | std::string deviceSerialNumber = "C02LKHBBFD57"; 591 | 592 | if (localUserID == NULL) 593 | { 594 | std::cerr << "localUserID is Null!" << std::endl; 595 | return; 596 | } 597 | 598 | anisetteData = std::make_shared(); 599 | anisetteData->insert(std::make_pair("X-Apple-I-MD-M", machineID->description())); 600 | anisetteData->insert(std::make_pair("X-Apple-I-MD", otp->description())); 601 | anisetteData->insert(std::make_pair("X-Apple-I-MD-LU", localUserID->description())); 602 | anisetteData->insert(std::make_pair("X-Apple-I-MD-RINFO", "17106176")); 603 | anisetteData->insert(std::make_pair("X-Mme-Device-Id", deviceID->description())); 604 | anisetteData->insert(std::make_pair("X-Apple-I-SRL-NO", deviceSerialNumber)); 605 | anisetteData->insert(std::make_pair("X-MMe-Client-Info", deviceDescription->description())); 606 | 607 | time_t ts = time(NULL); 608 | char dateString[100] = { 0 }; 609 | strftime(dateString, sizeof dateString, "%Y-%m-%dT%H:%M:%SZ", gmtime(&ts)); 610 | anisetteData->insert(std::make_pair("X-Apple-I-Client-Time", dateString)); 611 | anisetteData->insert(std::make_pair("X-Apple-Locale", "en_US")); 612 | anisetteData->insert(std::make_pair("X-Apple-I-TimeZone", "PST")); 613 | 614 | }); 615 | 616 | return anisetteData; 617 | } 618 | 619 | bool AnisetteDataManager::ReprovisionDevice(std::function provisionCallback) 620 | { 621 | #if !SPOOF_MAC 622 | provisionCallback(); 623 | return true; 624 | #else 625 | std::string adiDirectoryPath = "C:\\ProgramData\\Apple Computer\\iTunes\\adi"; 626 | 627 | /* Start Provisioning */ 628 | 629 | // Move iCloud's ADI files (so we don't mess with them). 630 | for (const auto& entry : fs::directory_iterator(adiDirectoryPath)) 631 | { 632 | if (entry.path().extension() == ".pb") 633 | { 634 | fs::path backupPath = entry.path(); 635 | backupPath += ".icloud"; 636 | 637 | fs::rename(entry.path(), backupPath); 638 | } 639 | } 640 | 641 | // Copy existing AltServer .pb files into original location to reuse the MID. 642 | for (const auto& entry : fs::directory_iterator(adiDirectoryPath)) 643 | { 644 | if (entry.path().extension() == ".altserver") 645 | { 646 | fs::path path = entry.path(); 647 | path.replace_extension(); 648 | 649 | fs::rename(entry.path(), path); 650 | } 651 | } 652 | 653 | auto cleanUp = [adiDirectoryPath]() { 654 | /* Finish Provisioning */ 655 | 656 | // Backup AltServer ADI files. 657 | for (const auto& entry : fs::directory_iterator(adiDirectoryPath)) 658 | { 659 | // Backup AltStore file 660 | if (entry.path().extension() == ".pb") 661 | { 662 | fs::path backupPath = entry.path(); 663 | backupPath += ".altserver"; 664 | 665 | fs::rename(entry.path(), backupPath); 666 | } 667 | } 668 | 669 | // Copy iCloud ADI files back to original location. 670 | for (const auto& entry : fs::directory_iterator(adiDirectoryPath)) 671 | { 672 | if (entry.path().extension() == ".icloud") 673 | { 674 | // Move backup file to original location 675 | fs::path path = entry.path(); 676 | path.replace_extension(); 677 | 678 | fs::rename(entry.path(), path); 679 | 680 | odslog("Copying iCloud file from: " << entry.path().string() << " to: " << path.string()); 681 | } 682 | } 683 | }; 684 | 685 | // Calling CopyAnisetteData implicitly generates new anisette data, 686 | // using the new client info string we injected. 687 | ObjcObject* error = NULL; 688 | ObjcObject* anisetteDictionary = (ObjcObject*)CopyAnisetteData(NULL, 0x1, &error); 689 | 690 | try 691 | { 692 | if (anisetteDictionary == NULL) 693 | { 694 | odslog("Reprovision Error:" << ((ObjcObject*)error)->description()); 695 | 696 | ObjcObject* localizedDescription = (ObjcObject*)((id(*)(id, SEL))objc_msgSend)(error, sel_registerName("localizedDescription")); 697 | if (localizedDescription) 698 | { 699 | int errorCode = ((int(*)(id, SEL))objc_msgSend)(error, sel_registerName("code")); 700 | throw LocalizedError(errorCode, localizedDescription->description()); 701 | } 702 | else 703 | { 704 | throw ServerError(ServerErrorCode::InvalidAnisetteData); 705 | } 706 | } 707 | 708 | odslog("Reprovisioned Anisette:" << anisetteDictionary->description()); 709 | 710 | setReprovisionedDevice(true); 711 | 712 | // Call callback while machine is provisioned for AltServer. 713 | provisionCallback(); 714 | } 715 | catch (std::exception& exception) 716 | { 717 | cleanUp(); 718 | 719 | throw; 720 | } 721 | 722 | cleanUp(); 723 | 724 | return true; 725 | #endif 726 | } 727 | 728 | bool AnisetteDataManager::ResetProvisioning() 729 | { 730 | std::string adiDirectoryPath = "C:\\ProgramData\\Apple Computer\\iTunes\\adi"; 731 | 732 | // Remove existing AltServer .pb files so we can create new ones next time we provision this device. 733 | for (const auto& entry : fs::directory_iterator(adiDirectoryPath)) 734 | { 735 | if (entry.path().extension() == ".altserver") 736 | { 737 | fs::remove(entry.path()); 738 | } 739 | } 740 | 741 | return true; 742 | } 743 | 744 | #include 745 | 746 | int main() 747 | { 748 | auto anisetteData = AnisetteDataManager::instance()->FetchAnisetteData(); 749 | if (!anisetteData) { 750 | std::cerr << "Anisette Data failed!" << std::endl; 751 | return 1; 752 | } 753 | for (const auto& elem : *anisetteData) 754 | { 755 | std::cout << elem.first << ": " << elem.second << "\n"; 756 | } 757 | } 758 | -------------------------------------------------------------------------------- /anisette_extract/AltWindowsAnisette.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.32002.261 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AltWindowsAnisette", "AltWindowsAnisette.vcxproj", "{E91FC25A-698B-4D25-91EF-D582DAE89BA8}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {E91FC25A-698B-4D25-91EF-D582DAE89BA8}.Debug|x64.ActiveCfg = Debug|x64 17 | {E91FC25A-698B-4D25-91EF-D582DAE89BA8}.Debug|x64.Build.0 = Debug|x64 18 | {E91FC25A-698B-4D25-91EF-D582DAE89BA8}.Debug|x86.ActiveCfg = Debug|Win32 19 | {E91FC25A-698B-4D25-91EF-D582DAE89BA8}.Debug|x86.Build.0 = Debug|Win32 20 | {E91FC25A-698B-4D25-91EF-D582DAE89BA8}.Release|x64.ActiveCfg = Release|x64 21 | {E91FC25A-698B-4D25-91EF-D582DAE89BA8}.Release|x64.Build.0 = Release|x64 22 | {E91FC25A-698B-4D25-91EF-D582DAE89BA8}.Release|x86.ActiveCfg = Release|Win32 23 | {E91FC25A-698B-4D25-91EF-D582DAE89BA8}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {FC4509AB-2CC0-49FA-B3B6-50D1F0ED8214} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /anisette_extract/AltWindowsAnisette.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {e91fc25a-698b-4d25-91ef-d582dae89ba8} 25 | AltWindowsAnisette 26 | 27 | 28 | 29 | Application 30 | true 31 | v141_xp 32 | Unicode 33 | 34 | 35 | Application 36 | false 37 | v141_xp 38 | true 39 | Unicode 40 | 41 | 42 | Application 43 | true 44 | v141_xp 45 | Unicode 46 | 47 | 48 | Application 49 | false 50 | v141_xp 51 | true 52 | Unicode 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | true 74 | 75 | 76 | false 77 | 78 | 79 | true 80 | 81 | 82 | false 83 | 84 | 85 | 86 | Level3 87 | true 88 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 89 | true 90 | stdcpp17 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | stdcpp17 106 | MultiThreaded 107 | 108 | 109 | Console 110 | true 111 | true 112 | true 113 | 114 | 115 | 116 | 117 | Level3 118 | true 119 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 120 | true 121 | stdcpp17 122 | Default 123 | 124 | 125 | Console 126 | true 127 | 128 | 129 | 130 | 131 | Level3 132 | true 133 | true 134 | true 135 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 136 | true 137 | stdcpp17 138 | MultiThreaded 139 | 140 | 141 | Console 142 | true 143 | true 144 | true 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | -------------------------------------------------------------------------------- /anisette_extract/AltWindowsAnisette.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 源文件 20 | 21 | 22 | 23 | 24 | 源文件 25 | 26 | 27 | -------------------------------------------------------------------------------- /anisette_extract/AltWindowsAnisette.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /anisette_extract/Release/AltWindowsAnisette.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NyaMisty/alt-anisette-server/f200b040ac0c1cf1ca62a65461879eddf3fe2c1a/anisette_extract/Release/AltWindowsAnisette.exe -------------------------------------------------------------------------------- /anisette_extract/Release/AltWindowsAnisette.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NyaMisty/alt-anisette-server/f200b040ac0c1cf1ca62a65461879eddf3fe2c1a/anisette_extract/Release/AltWindowsAnisette.pdb -------------------------------------------------------------------------------- /anisette_extract/base64.h: -------------------------------------------------------------------------------- 1 | #ifndef _MACARON_BASE64_H_ 2 | #define _MACARON_BASE64_H_ 3 | 4 | /** 5 | * The MIT License (MIT) 6 | * Copyright (c) 2016 tomykaira 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining 9 | * a copy of this software and associated documentation files (the 10 | * "Software"), to deal in the Software without restriction, including 11 | * without limitation the rights to use, copy, modify, merge, publish, 12 | * distribute, sublicense, and/or sell copies of the Software, and to 13 | * permit persons to whom the Software is furnished to do so, subject to 14 | * the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be 17 | * included in all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 21 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 | * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 23 | * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 24 | * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 25 | * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | */ 27 | 28 | #include 29 | 30 | namespace macaron { 31 | 32 | class Base64 { 33 | public: 34 | 35 | template 36 | static std::string Encode(T data) { 37 | static constexpr char sEncodingTable[] = { 38 | 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 39 | 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 40 | 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 41 | 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 42 | 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 43 | 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 44 | 'w', 'x', 'y', 'z', '0', '1', '2', '3', 45 | '4', '5', '6', '7', '8', '9', '+', '/' 46 | }; 47 | 48 | size_t in_len = data.size(); 49 | size_t out_len = 4 * ((in_len + 2) / 3); 50 | std::string ret(out_len, '\0'); 51 | size_t i; 52 | char* p = const_cast(ret.c_str()); 53 | 54 | for (i = 0; i < in_len - 2; i += 3) { 55 | *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; 56 | *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)]; 57 | *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2) | ((int)(data[i + 2] & 0xC0) >> 6)]; 58 | *p++ = sEncodingTable[data[i + 2] & 0x3F]; 59 | } 60 | if (i < in_len) { 61 | *p++ = sEncodingTable[(data[i] >> 2) & 0x3F]; 62 | if (i == (in_len - 1)) { 63 | *p++ = sEncodingTable[((data[i] & 0x3) << 4)]; 64 | *p++ = '='; 65 | } 66 | else { 67 | *p++ = sEncodingTable[((data[i] & 0x3) << 4) | ((int)(data[i + 1] & 0xF0) >> 4)]; 68 | *p++ = sEncodingTable[((data[i + 1] & 0xF) << 2)]; 69 | } 70 | *p++ = '='; 71 | } 72 | 73 | return ret; 74 | } 75 | 76 | static std::string Decode(const std::string& input, std::string& out) { 77 | static constexpr unsigned char kDecodingTable[] = { 78 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 79 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 80 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 81 | 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 82 | 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 83 | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, 84 | 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 85 | 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 86 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 87 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 88 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 89 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 90 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 91 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 92 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 93 | 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64 94 | }; 95 | 96 | size_t in_len = input.size(); 97 | if (in_len % 4 != 0) return "Input data size is not a multiple of 4"; 98 | 99 | size_t out_len = in_len / 4 * 3; 100 | if (input[in_len - 1] == '=') out_len--; 101 | if (input[in_len - 2] == '=') out_len--; 102 | 103 | out.resize(out_len); 104 | 105 | for (size_t i = 0, j = 0; i < in_len;) { 106 | uint32_t a = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; 107 | uint32_t b = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; 108 | uint32_t c = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; 109 | uint32_t d = input[i] == '=' ? 0 & i++ : kDecodingTable[static_cast(input[i++])]; 110 | 111 | uint32_t triple = (a << 3 * 6) + (b << 2 * 6) + (c << 1 * 6) + (d << 0 * 6); 112 | 113 | if (j < out_len) out[j++] = (triple >> 2 * 8) & 0xFF; 114 | if (j < out_len) out[j++] = (triple >> 1 * 8) & 0xFF; 115 | if (j < out_len) out[j++] = (triple >> 0 * 8) & 0xFF; 116 | } 117 | 118 | return ""; 119 | } 120 | 121 | }; 122 | 123 | } 124 | 125 | #endif /* _MACARON_BASE64_H_ */ -------------------------------------------------------------------------------- /http_wrap/README.md: -------------------------------------------------------------------------------- 1 | # Http wrapper for AltWindowsAnisette 2 | 3 | Build: 4 | 5 | ``` 6 | nexe -t windows-x86-14.15.3 server.js 7 | ``` 8 | -------------------------------------------------------------------------------- /http_wrap/server.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NyaMisty/alt-anisette-server/f200b040ac0c1cf1ca62a65461879eddf3fe2c1a/http_wrap/server.exe -------------------------------------------------------------------------------- /http_wrap/server.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const util = require('util'); 3 | const exec = util.promisify(require('child_process').exec); 4 | 5 | function splitLines(t) { return t.split(/\r\n|\r|\n/); } 6 | 7 | async function _getAnisetteData() { 8 | const { stdout, stderr } = await exec("AltWindowsAnisette.exe") 9 | 10 | let resbody = {} 11 | 12 | const lines = splitLines(stdout) 13 | for (const line of lines) { 14 | const lineparts = line.split(': ', 2) 15 | resbody[lineparts[0]] = lineparts[1] 16 | } 17 | console.log("Generated anisetteData: " + JSON.stringify(resbody)) 18 | return resbody 19 | } 20 | 21 | let anisetteStore = { 22 | data: null, 23 | update: null, 24 | } 25 | const anisetteCacheTime = 20 26 | 27 | async function getAnisetteData() { 28 | if (!anisetteStore.data || new Date() - anisetteStore.update > anisetteCacheTime * 1000) { 29 | anisetteStore.data = await _getAnisetteData() 30 | anisetteStore.update = new Date() 31 | } 32 | return anisetteStore.data 33 | } 34 | 35 | const hostname = '0.0.0.0'; 36 | const port = 6969; 37 | 38 | const server = http.createServer(async (req, res) => { 39 | console.log("[" + new Date() + "] - " + req.socket.localAddress + " - " + req.method + " " + req.url) 40 | res.setHeader('Content-Type', 'application/json'); 41 | 42 | let resbody = null 43 | try { 44 | const retJson = await getAnisetteData() 45 | resbody = JSON.stringify(retJson) 46 | res.statusCode = 200; 47 | } catch (e) { 48 | console.log(e) 49 | resbody = e.toString() 50 | res.statusCode = 500; 51 | } 52 | res.end(resbody); 53 | }); 54 | 55 | server.listen(port, hostname, () => { 56 | console.log(`Anisette Server running at http://${hostname}:${port}/`); 57 | }); 58 | -------------------------------------------------------------------------------- /http_wrap/server_linux.js: -------------------------------------------------------------------------------- 1 | const http = require('http'); 2 | const util = require('util'); 3 | const exec = util.promisify(require('child_process').exec); 4 | 5 | function splitLines(t) { return t.split(/\r\n|\r|\n/); } 6 | 7 | async function _getAnisetteData() { 8 | const { stdout, stderr } = await exec("wine AltWindowsAnisette.exe") 9 | 10 | let resbody = {} 11 | 12 | const lines = splitLines(stdout) 13 | for (const line of lines) { 14 | const lineparts = line.split(': ', 2) 15 | resbody[lineparts[0]] = lineparts[1] 16 | } 17 | console.log("Generated anisetteData: " + JSON.stringify(resbody)) 18 | return resbody 19 | } 20 | 21 | let anisetteStore = { 22 | data: null, 23 | update: null, 24 | } 25 | const anisetteCacheTime = 20 26 | 27 | async function getAnisetteData() { 28 | if (!anisetteStore.data || new Date() - anisetteStore.update > anisetteCacheTime * 1000) { 29 | anisetteStore.data = await _getAnisetteData() 30 | anisetteStore.update = new Date() 31 | } 32 | return anisetteStore.data 33 | } 34 | 35 | const hostname = '0.0.0.0'; 36 | const port = 6969; 37 | 38 | const server = http.createServer(async (req, res) => { 39 | console.log("[" + new Date() + "] - " + req.socket.localAddress + " - " + req.method + " " + req.url) 40 | res.setHeader('Content-Type', 'application/json'); 41 | 42 | let resbody = null 43 | try { 44 | const retJson = await getAnisetteData() 45 | resbody = JSON.stringify(retJson) 46 | res.statusCode = 200; 47 | } catch (e) { 48 | console.log(e) 49 | resbody = e.toString() 50 | res.statusCode = 500; 51 | } 52 | res.end(resbody); 53 | }); 54 | 55 | server.listen(port, hostname, () => { 56 | console.log(`Anisette Server running at http://${hostname}:${port}/`); 57 | }); 58 | -------------------------------------------------------------------------------- /scripts/icloud_login.ahk: -------------------------------------------------------------------------------- 1 | #ErrorStdOut 2 | 3 | ; Works regardless if debug argument is passed in. 4 | debug(string) { 5 | string .= "`n" 6 | FileAppend %string%,* ;* goes to stdout 7 | } 8 | 9 | debug("[i] Initializing") 10 | 11 | SetWinDelay 1000 12 | SetTitleMatchMode, 2 13 | 14 | 15 | debug("[i] Finding iCloud Window") 16 | WinWait, iCloud 17 | 18 | debug("[i] Finding Account Input") 19 | ControlClick, Edit1 20 | sleep 1000 21 | debug("[i] Sending Account Name") 22 | ControlSetText, Edit1, aaa@icloud.com 23 | 24 | debug("[i] Finding Password Input") 25 | ControlClick, Edit2 26 | sleep 1000 27 | debug("[i] Sending Password") 28 | ControlSetText, Edit2, Aa123456 29 | 30 | While True 31 | { 32 | WinWait, iCloud 33 | debug("[i] Clicking login") 34 | ControlClick, Button1 35 | debug("[i] Waiting for error dialog") 36 | WinWait Verification Failed 37 | ControlGetText text, Static2 38 | debug("[i] got msg: " . text) 39 | If InStr(text, "Visit iForgot to reset your account") 40 | { 41 | debug("[i] Got locked ret msg, good!") 42 | Break 43 | } 44 | If InStr(text, "There was an error connecting to") 45 | { 46 | debug("[i] Got first-time connection error, wait for locked msg!") 47 | WinClose 48 | sleep 2500 49 | Continue 50 | } 51 | else 52 | { 53 | debug("[e] Unknown message!") 54 | ExitApp 1 55 | } 56 | } 57 | 58 | debug("[i] iCloud Initialized successfully!") 59 | 60 | -------------------------------------------------------------------------------- /scripts/install_icloud.sh: -------------------------------------------------------------------------------- 1 | set -x -e 2 | echo "Extracting iCloud..." && wine iCloudSetup.exe /extract 3 | echo "Installing Application Support..." && wine MsiExec.exe /i AppleApplicationSupport.msi /qn 4 | 5 | bash -c "while true; do wine taskkill /f /im iCloudServices.exe; sleep 5; done" & 6 | 7 | echo "Installing iCloud..." && wine MsiExec.exe /i iCloud.msi REBOOT=ReallySuppress /qn || echo "msiexec return: $?" 8 | 9 | echo "Cleanup..." && kill %1 10 | -------------------------------------------------------------------------------- /scripts/run_icloud.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | echo "-----------------------------------------------" 4 | echo "" 5 | echo 'If after 60 seconds you are still not seeing "Anisette Server running at http://0.0.0.0:6969/"' 6 | echo 'Please re-run the docker container as the initialize process are stucked' 7 | echo "" 8 | echo "-----------------------------------------------" 9 | 10 | export WINEDEBUG=-all 11 | 12 | set -x 13 | set -e 14 | 15 | cd /app 16 | 17 | wine 'C:\Program Files\Common Files\Apple\Internet Services\iCloud.exe' & 18 | 19 | sleep 10 20 | 21 | timeout 45s wine "Z:\\ahk\\AutoHotkeyU32.exe" "Z:\\app\\icloud_login.ahk" 22 | 23 | sleep 1 24 | 25 | export NODE_SKIP_PLATFORM_CHECK=1 26 | #wine "Z:\\app\\server.exe" 27 | (cd /app; node server_linux.js) --------------------------------------------------------------------------------