Skip to content

Commit

Permalink
Create Anisette cassettes, a format holding pregenerated anisette data
Browse files Browse the repository at this point in the history
  • Loading branch information
Dadoum committed Apr 2, 2023
1 parent b863447 commit 04807e5
Show file tree
Hide file tree
Showing 10 changed files with 252 additions and 21 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
bin/
.dub/
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/cmake-d/cmake-d)

project(Provision D)
option(build_anisetteserver "Build Anisette server" ON)
option(build_mkcassette "Build mkcassette" OFF)
option(link_libplist_dynamic "Load libplist at runtime" OFF)

include(cmake/dependencies.cmake)
Expand Down Expand Up @@ -65,3 +66,13 @@ if(build_anisetteserver)

target_link_libraries(anisette_server provision handy-httpd)
endif()

if(build_mkcassette)
set(MKCASSETTE_SERVER_SOURCE_DIR "mkcassette/")
file(GLOB_RECURSE MKCASSETTE_D_SOURCES "${MKCASSETTE_SOURCE_DIR}*.d")

add_executable(mkcassette ${MKCASSETTE_D_SOURCES})
target_include_directories(mkcassette PUBLIC ${MKCASSETTE_SOURCE_DIR})

target_link_libraries(mkcassette provision)
endif()
1 change: 1 addition & 0 deletions anisette_server/app.d
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ static __gshared ADI* adi;
static __gshared ulong rinfo;

void main(string[] args) {
writeln(anisetteServerBranding, " v", anisetteServerVersion);
auto serverConfig = ServerConfig.defaultValues;
serverConfig.hostname = "0.0.0.0";
serverConfig.port = 6969;
Expand Down
2 changes: 1 addition & 1 deletion anisette_server/constants.d
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module constants;

enum anisetteServerBranding = "anisette-server-provision";
enum anisetteServerVersion = "1.2.2";
enum anisetteServerVersion = "1.3.0";
enum nativesUrl = "https://apps.mzstatic.com/content/android-apple-music-apk/applemusic.apk";
12 changes: 12 additions & 0 deletions dub.sdl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,18 @@ subPackage {
dependency "provision" version="*"
}

subPackage {
name "mkcassette"
targetType "executable"
targetPath "bin"

workingDirectory "bin"

sourcePaths "mkcassette"

dependency "provision" version="*"
}

subPackage {
name "anisette-server"
targetType "executable"
Expand Down
19 changes: 18 additions & 1 deletion lib/provision/adi.d
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,9 @@ enum string libraryPath = "lib/" ~ architectureIdentifier ~ "/";
throw new FileException(libraryPath ~ "libstoreservicescore.so", "Apple libraries are not installed correctly. Refer to README for instructions. ");
}

this.libstoreservicescore = new AndroidLibrary(libraryPath ~ "libstoreservicescore.so");
this.libstoreservicescore = new AndroidLibrary(libraryPath ~ "libstoreservicescore.so", (string symbol) {
return symbol == "gettimeofday" ? cast(void*) &gettimeofday_timeTravel : getSymbolImplementation(symbol);
});

debug {
stderr.writeln("Loading Android-specific symbols...");
Expand Down Expand Up @@ -491,3 +493,18 @@ string translateADIErrorCode(int errorCode) {

return format!"%d"(errorCode);
}

import core.sys.posix.sys.time;
import std.parallelism;

public __gshared bool doTimeTravel = false;
public __gshared TaskPool.WorkerLocalStorage!timeval targetTime;

private extern (C) int gettimeofday_timeTravel(timeval* timeval, void* ptr) {
auto ret = gettimeofday(timeval, ptr);
if (doTimeTravel) {
*timeval = targetTime.get();
}

return ret;
}
39 changes: 36 additions & 3 deletions lib/provision/androidlibrary.d
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import std.conv;
import std.experimental.allocator;
import std.experimental.allocator.mallocator;
import std.experimental.allocator.mmap_allocator;
import std.functional;
import std.mmfile;
import std.path;
import std.random;
Expand All @@ -30,8 +31,12 @@ public struct AndroidLibrary {
package ElfW!"Sym"[] dynamicSymbolTable;
package SymbolHashTable hashTable;

public this(string libraryName) {
alias HookCallback = void* delegate(string symbolName);
package HookCallback hookCallback;

public this(string libraryName, HookCallback hookCallback = (&getSymbolImplementation).toDelegate()) {
elfFile = new MmFile(libraryName);
this.hookCallback = hookCallback;

auto elfHeader = elfFile.identify!(ElfW!"Ehdr")(0);
auto programHeaders = elfFile.identifyArray!(ElfW!"Phdr")(elfHeader.e_phoff, elfHeader.e_phnum);
Expand Down Expand Up @@ -71,6 +76,10 @@ public struct AndroidLibrary {
if (mmapped_alloc == MAP_FAILED) {
throw new LoaderException("Cannot allocate the memory: " ~ to!string(errno));
}
memoryTable[MemoryBlock(cast(size_t) mmapped_alloc, cast(size_t) mmapped_alloc + allocSize)] = &this;
debug {
stderr.writefln("Allocating %x - %x for %s", cast(size_t) mmapped_alloc, cast(size_t) mmapped_alloc + allocSize, libraryName);
}
allocation = mmapped_alloc[0..allocSize];

size_t fileStart;
Expand Down Expand Up @@ -153,7 +162,7 @@ public struct AndroidLibrary {
addend = *cast(size_t*) (cast(size_t) allocation.ptr + offset);
}
}
auto symbol = getSymbolImplementation(getSymbolName(dynamicSymbolTable[symbolIndex]));
auto symbol = hookCallback(getSymbolName(dynamicSymbolTable[symbolIndex]));

auto location = cast(size_t*) (cast(size_t) allocation.ptr + offset);

Expand Down Expand Up @@ -200,6 +209,30 @@ public struct AndroidLibrary {
}
}

private struct MemoryBlock {
size_t start;
size_t end;
}

private __gshared AndroidLibrary*[MemoryBlock] memoryTable;
AndroidLibrary* memoryOwner(size_t address) {
foreach(memoryBlock; memoryTable.keys()) {
if (address > memoryBlock.start && address < memoryBlock.end) {
return memoryTable[memoryBlock];
}
}

return null;
}

import core.sys.linux.execinfo;
pragma(inline, true) AndroidLibrary* rootLibrary() {
enum MAXFRAMES = 4;
void*[MAXFRAMES] callstack;
auto numframes = backtrace(callstack.ptr, MAXFRAMES);
return memoryOwner(cast(size_t) callstack[numframes - 1]);
}

interface SymbolHashTable {
ElfW!"Sym" lookup(string symbolName, AndroidLibrary* library);
}
Expand Down Expand Up @@ -378,7 +411,7 @@ RetType reinterpret(RetType, FromType)(FromType[] obj) {
return (cast(RetType[]) obj)[0];
}

private static void* getSymbolImplementation(string symbolName) {
static void* getSymbolImplementation(string symbolName) {
import provision.symbols;
return lookupSymbol(symbolName);
}
Expand Down
30 changes: 14 additions & 16 deletions lib/provision/symbols.d
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module provision.symbols;

import core.memory;
import core.stdc.errno;
import core.stdc.stdlib;
import core.stdc.string;
Expand Down Expand Up @@ -39,33 +40,30 @@ private extern (C) noreturn undefinedSymbol() {
}

private extern (C) AndroidLibrary* dlopenWrapper(const char* name) {
stderr.writeln("Attempting to load ", name.fromStringz());
debug {
stderr.writeln("Attempting to load ", name.fromStringz());
}
try {
return Mallocator.instance.make!AndroidLibrary(cast(string) name.fromStringz());
auto lib = new AndroidLibrary(cast(string) name.fromStringz(), rootLibrary().hookCallback);
GC.addRoot(lib);
return lib;
} catch (Throwable) {
return null;
}
}

private extern (C) void* dlsymWrapper(AndroidLibrary* library, const char* symbolName) {
stderr.writeln("Attempting to load ", symbolName.fromStringz());
debug {
stderr.writeln("Attempting to load symbol ", symbolName.fromStringz());
}
return library.load(cast(string) symbolName.fromStringz());
}

private extern (C) void dlcloseWrapper(AndroidLibrary* library) {
return Mallocator.instance.dispose(library);
}

public bool doTimeTravel = false;
public timeval targetTime;

private extern (C) ReturnType!gettimeofday gettimeofday_timeTravel(timeval* timeval, void* ptr) {
auto ret = gettimeofday(timeval, ptr);
if (doTimeTravel) {
*timeval = targetTime;
if (library) {
GC.removeRoot(library);
destroy(library);
}

return ret;
}

// gperf generated code:
Expand Down Expand Up @@ -146,7 +144,7 @@ package void* lookupSymbol(string str) {
{"pthread_mutex_unlock", &emptyStub},
{"pthread_rwlock_rdlock", &emptyStub}, {
"gettimeofday",
&gettimeofday_timeTravel
&gettimeofday
}, {""}, {"read", &read},
{"mkdir", &mkdir}, {"malloc", &malloc}, {""}, {""}, {""}, {""},
{"__system_property_get", &__system_property_get_impl}, {""}, {""},
Expand Down
153 changes: 153 additions & 0 deletions mkcassette/app.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
module app;

import core.sys.posix.sys.time;
import std.algorithm;
import std.array;
import std.base64;
import std.datetime.stopwatch: StopWatch;
import file = std.file;
import std.format;
import std.getopt;
import std.math;
import std.net.curl;
import std.parallelism;
import std.path;
import std.stdio;
import std.zip;

import provision;
import provision.symbols;

import constants;

struct AnisetteCassetteHeader {
align(1):
ubyte[6] magicHeader = ['C', 'S', 'S', 'T', 'E', 0x69];
ubyte formatVersion = 0;
ulong baseTime;

ubyte[64] machineId;
}

__gshared ulong origTime;
int main(string[] args) {
writeln(mkcassetteBranding, " v", mkcassetteVersion);

string identifier = "ba10defe42ea69ff";
string path = "~/.adi";
ulong days = 90;
bool onlyInit = false;
bool apkDownloadAllowed = true;

auto helpInformation = getopt(
args,
"i|identifier", format!"The identifier used for the cassette (default: %s)"(identifier), &identifier,
"a|adi-path", format!"Where the provisioning information should be stored on the computer (default: %s)"(path), &path,
"d|days", format!"Number of days in the cassette (default: %s)"(days), &days,
"init-only", format!"Download libraries and exit (default: %s)"(onlyInit), &onlyInit,
"can-download", format!"If turned on, may download the dependencies automatically (default: %s)"(apkDownloadAllowed), &apkDownloadAllowed,
);

if (helpInformation.helpWanted) {
defaultGetoptPrinter("This program allows you to host anisette through libprovision!", helpInformation.options);
return 0;
}

auto coreADIPath = libraryPath.buildPath("libCoreADI.so");
auto SSCPath = libraryPath.buildPath("libstoreservicescore.so");

if (!(file.exists(coreADIPath) && file.exists(SSCPath)) && apkDownloadAllowed) {
auto http = HTTP();
http.onProgress = (size_t dlTotal, size_t dlNow, size_t ulTotal, size_t ulNow) {
write("Downloading libraries from Apple servers... ");
if (dlTotal != 0) {
write((dlNow * 100)/dlTotal, "% \r");
} else {
// Convert dlNow (in bytes) to a human readable string
float downloadedSize = dlNow;

enum units = ["B", "kB", "MB", "GB", "TB"];
int i = 0;
while (downloadedSize > 1000 && i < units.length - 1) {
downloadedSize = floor(downloadedSize) / 1000;
++i;
}

write(downloadedSize, units[i], " \r");
}
return 0;
};
auto apkData = get!(HTTP, ubyte)(nativesUrl, http);
writeln("Downloading libraries from Apple servers... done! \r");
auto apk = new ZipArchive(apkData);
auto dir = apk.directory();

if (!file.exists("lib/")) {
file.mkdir("lib/");
}
if (!file.exists(libraryPath)) {
file.mkdir(libraryPath);
}
file.write(coreADIPath, apk.expand(dir[coreADIPath]));
file.write(SSCPath, apk.expand(dir[SSCPath]));
}

if (onlyInit) {
return 0;
}

auto numberOfOTP = days*24*60;

ubyte[] mid;
ubyte[][] otps = new ubyte[][](numberOfOTP);

{
scope ADI* adi = new ADI(expandTilde("~/.adi"));

if (!adi.isMachineProvisioned()) {
stderr.write("Machine requires provisioning... ");
ulong rinfo;
adi.provisionDevice(rinfo);
stderr.writeln("done !");
}
adi.getOneTimePassword(mid, otps[0]);
}

__gshared timeval origTimeVal;
gettimeofday(&origTimeVal, null);
origTime = origTimeVal.tv_sec;

StopWatch sw;
writeln("Starting generation of ", numberOfOTP, " otps with ", totalCPUs, " threads.");
sw.start();

auto adi = taskPool.workerLocalStorage(new ADI(expandTilde("~/.adi")));
targetTime = taskPool.workerLocalStorage(origTimeVal);
doTimeTravel = true;
foreach (idx, ref otp; parallel(otps)) {
scope localAdi = adi.get();
scope time = targetTime.get();
time.tv_sec = origTime + idx * 30;
targetTime.get() = time;
localAdi.getOneTimePassword!false(mid, otp);

assert(targetTime.get().tv_sec == origTime + idx * 30);
}

auto otpBlob = otps
.map!((otp) => otp[8..24])
.join();
sw.stop();
writeln("Success. Duration: ", sw.peek());

auto anisetteCassetteHeader = AnisetteCassetteHeader();
anisetteCassetteHeader.baseTime = origTime;
anisetteCassetteHeader.machineId[0..mid.length] = mid;

auto anisetteCassetteHeaderBytes = (cast(ubyte*) &anisetteCassetteHeader)[0..AnisetteCassetteHeader.sizeof];

file.write("./otp-blob.bin", anisetteCassetteHeaderBytes);
file.append("./otp-blob.bin", otpBlob);

return 0;
}
5 changes: 5 additions & 0 deletions mkcassette/constants.d
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module constants;

enum mkcassetteBranding = "mkcassette";
enum mkcassetteVersion = "1.3.0-beta";
enum nativesUrl = "https://apps.mzstatic.com/content/android-apple-music-apk/applemusic.apk";

0 comments on commit 04807e5

Please sign in to comment.