forked from Dadoum/Provision
-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.d
224 lines (177 loc) · 7.53 KB
/
app.d
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
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.mmfile;
import std.net.curl;
import std.parallelism;
import std.path;
import process = std.process;
import std.range;
import std.zip;
import slf4d;
import provision;
import provision.androidlibrary;
import provision.compat.windows;
import provision.symbols;
import constants;
version (X86_64) {
enum string architectureIdentifier = "x86_64";
} else version (X86) {
enum string architectureIdentifier = "x86";
} else version (AArch64) {
enum string architectureIdentifier = "arm64-v8a";
} else version (ARM) {
enum string architectureIdentifier = "armeabi-v7a";
} else {
static assert(false, "Architecture not supported :(");
}
struct AnisetteCassetteHeader {
align(1):
ubyte[7] magicHeader = [0x69, 'C', 'A', 'S', 'S', 'T', 'E'];
ubyte formatVersion = 0;
ulong baseTime;
ubyte[64] machineId;
}
static assert(AnisetteCassetteHeader.sizeof % 16 == 0);
__gshared ulong origTime;
int main(string[] args) {
Logger log = getLogger();
log.infoF!"%s v%s"(mkcassetteBranding, provisionVersion);
char[] identifier = cast(char[]) "ba10defe42ea69ff";
string outputFile = "./otp-file.acs";
ulong days = 90;
bool onlyInit = false;
bool apkDownloadAllowed = true;
version (Windows) {
string configurationPath = process.environment["LocalAppData"].buildPath("Provision");
} else {
string configurationPath;
string xdgConfigPath = process.environment.get("XDG_CONFIG_HOME");
if (xdgConfigPath) {
configurationPath = xdgConfigPath.buildPath("Provision");
} else {
configurationPath = expandTilde("~/.config/Provision/");
}
}
// Parse command-line arguments
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)"(configurationPath), &configurationPath,
"d|days", format!"Number of days in the cassette (default: %s)"(days), &days,
"o|output", format!"Output location (default: %s)"(outputFile), &outputFile,
"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;
}
if (!file.exists(configurationPath)) {
file.mkdirRecurse(configurationPath);
}
string libraryPath = configurationPath.buildPath("lib/" ~ architectureIdentifier);
auto coreADIPath = libraryPath.buildPath("libCoreADI.so");
auto SSCPath = libraryPath.buildPath("libstoreservicescore.so");
// Download APK if needed
if (!(file.exists(coreADIPath) && file.exists(SSCPath)) && apkDownloadAllowed) {
auto http = HTTP();
log.info("Downloading libraries from Apple servers...");
auto apkData = get!(HTTP, ubyte)(nativesUrl, http);
log.info("Done !");
auto apk = new ZipArchive(apkData);
auto dir = apk.directory();
if (!file.exists(libraryPath)) {
file.mkdirRecurse(libraryPath);
}
file.write(coreADIPath, apk.expand(dir["lib/" ~ architectureIdentifier ~ "/libCoreADI.so"]));
file.write(SSCPath, apk.expand(dir["lib/" ~ architectureIdentifier ~ "/libstoreservicescore.so"]));
}
if (onlyInit) {
return 0;
}
// 1 per minute
auto numberOfOTP = days*24*60;
ubyte[] mid;
ubyte[] nothing;
// We store the real time in a shared variable, and create a thread-local time variable.
__gshared timeval origTimeVal;
gettimeofday(&origTimeVal, null);
origTime = origTimeVal.tv_sec;
targetTime = taskPool.workerLocalStorage(origTimeVal);
// Initializing ADI and machine if it has not already been made.
version (Windows) {
enum nullFilename = "NUL";
} else {
enum nullFilename = "/dev/null";
}
Device device = new Device(nullFilename);
{
ADI adi = new ADI("lib/" ~ architectureIdentifier);
adi.provisioningPath = configurationPath;
if (!device.initialized) {
log.info("Creating machine... ");
import std.digest;
import std.random;
import std.range;
import std.uni;
import std.uuid;
device.serverFriendlyDescription = "<MacBookPro13,2> <macOS;13.1;22C65> <com.apple.AuthKit/1 (com.apple.dt.Xcode/3594.4.19)>";
device.uniqueDeviceIdentifier = randomUUID().toString().toUpper();
device.adiIdentifier = (cast(ubyte[]) rndGen.take(2).array()).toHexString().toLower();
device.localUserUUID = (cast(ubyte[]) rndGen.take(8).array()).toHexString().toUpper();
log.info("Machine creation done!");
}
adi.identifier = device.adiIdentifier;
ProvisioningSession provisioningSession = new ProvisioningSession(adi, device);
provisioningSession.provision(-2);
mid = adi.requestOTP(-2).machineIdentifier;
}
auto adi = taskPool().workerLocalStorage!ADI({
// We hook the gettimeofday function in the library to change the date.
AndroidLibrary storeServicesCore = new AndroidLibrary(SSCPath, [
"gettimeofday": cast(void*) &gettimeofday_timeTravel
]);
ADI adi = new ADI(libraryPath, storeServicesCore);
adi.provisioningPath = configurationPath;
adi.identifier = device.adiIdentifier;
return adi;
}());
StopWatch sw;
log.infoF!"Starting generation of %d otps (%d days) with %d threads."(numberOfOTP, days, totalCPUs);
sw.start();
auto anisetteCassetteHeader = AnisetteCassetteHeader();
anisetteCassetteHeader.baseTime = origTime;
anisetteCassetteHeader.machineId[0..mid.length] = mid;
auto anisetteCassetteHeaderBytes = (cast(ubyte*) &anisetteCassetteHeader)[0..AnisetteCassetteHeader.sizeof];
// The file consists of 1 header and then all the 16-bytes long OTPs, so we make a memory-mapped file of the correct size.
scope otpFile = new MmFile(outputFile, MmFile.Mode.readWriteNew, AnisetteCassetteHeader.sizeof + 16 * numberOfOTP * ubyte.sizeof, null);
scope acs = cast(ubyte[]) otpFile[0..$];
acs[0..AnisetteCassetteHeader.sizeof] = anisetteCassetteHeaderBytes;
// we take every 16 bytes chunk of the OTP part of the file, and iterate concurrently through it.
foreach (idx, otp; parallel(std.range.chunks(cast(ubyte[]) acs[AnisetteCassetteHeader.sizeof..$], 16))) {
scope localAdi = adi.get();
scope time = targetTime.get();
time.tv_sec = origTime + idx * 30;
targetTime.get() = time;
otp[] = localAdi.requestOTP(-2).oneTimePassword[8..24];
assert(targetTime.get().tv_sec == origTime + idx * 30);
}
sw.stop();
log.infoF!"Success. File written at %s, duration %s"(outputFile, sw.peek());
return 0;
}
import core.sys.posix.sys.time;
import std.parallelism;
public __gshared TaskPool.WorkerLocalStorage!timeval targetTime;
private extern (C) int gettimeofday_timeTravel(timeval* timeval, void* ptr) {
*timeval = targetTime.get();
return 0;
}