This commit is contained in:
relikd
2024-04-02 21:54:37 +02:00
commit 8f04d673d9
119 changed files with 20136 additions and 0 deletions

View File

@@ -0,0 +1,92 @@
name: 'Get iTunes Headers & Kbsync'
inputs:
apple_id:
required: true
apple_id_pwd:
required: true
ngrok_token:
description: "Token for RDP debugging"
required: false
runs:
using: "composite"
steps:
- name: Check if initialized
id: check
run: |
echo "Checking if we need to initialize iTunes again"
if [[ "$(cat /c/.iTunesInitialized)" == "${{ inputs.ngrok-token }}" ]]; then
echo " Need to re-init iTunes"
echo "NEED_INIT=1" >> $GITHUB_ENV
else
echo " Needn't to do anything"
echo "NEED_INIT=0" >> $GITHUB_ENV
fi
working-directory: ${{ github.action_path }}
shell: bash
- name: Setup iTunes
if: ${{ env.NEED_INIT == 1 }}
run: |
echo Setup iTunes...
start /wait taskkill /f /im iTunes* python*
workflow_helper\iTunesInstall\install_itunes.bat
working-directory: ${{ github.action_path }}
shell: cmd
- name: Setup Python Dependencies
if: ${{ env.NEED_INIT == 1 }}
run: |
echo Setup Python Dependencies...
pip3 install pywinauto frida Flask
working-directory: ${{ github.action_path }}
shell: cmd
#- uses: NyaMisty/reverse-rdp-windows-github-actions-ng@master
# if: ${{ always() && github.event_name == 'workflow_dispatch' && github.event.inputs.itunes_debug_enabled }}
# with:
# ngrok-token: ${{ inputs.ngrok_token }}
# password: Aa123456
# foreground: false
- name: Login iTunes
if: ${{ env.NEED_INIT == 1 }}
env:
APPLEID: ${{ inputs.apple_id }}
APPLEID_PWD: ${{ inputs.apple_id_pwd }}
run: |
echo Login iTunes...
python3 workflow_helper/itunes_auto_login.py %APPLEID% %APPLEID_PWD%
working-directory: ${{ github.action_path }}
shell: cmd
- name: Finish Initialization
if: ${{ env.NEED_INIT == 1 }}
env:
APPLEID: ${{ inputs.apple_id }}
run: |
echo "Finish Initialization..."
echo "$APPLEID" > /c/.iTunesInitialized
working-directory: ${{ github.action_path }}
shell: bash
- name: Start Frida Header Server
run: |
echo Start Frida Header Server!
curl -Lo psexec64.exe https://github.com/ComputerGuyYash/psexec/raw/main/PsExec64.exe
psexec64.exe -accepteula -nobanner -i -d cmd /c "python3.exe workflow_helper/iTunesDownload/get_header.py > C:\get_header.log 2>&1"
exit 0
working-directory: ${{ github.action_path }}
shell: cmd
- name: Test Frida Header Server
run: |
sleep 5
ret=0
echo "---------------- Before query headers ----------------"
cat /c/get_header.log
curl --fail-with-body -vv 127.0.0.1:9000 || ret=$?
echo "---------------- After query headers ----------------"
cat /c/get_header.log
exit $ret
working-directory: ${{ github.action_path }}
shell: bash

View File

@@ -0,0 +1,22 @@
import os
# for debug
P = r"C:\Program Files\iTunes\iTunes1.exe"
if not os.path.exists(P):
# for production
P = r"C:\Program Files\iTunes\iTunes.exe"
with open(P, 'rb') as f:
data = f.read()
data = bytearray(data)
# Patch passwordSettings (actually useless)
data[0x77daf4:0x77daf4+5] = b'\xBF\x03\x00\x00\x00'
data[0x77db7e:0x77db7e+2] = b'\xEB\x0A'
# Patch signIn reason to serverDialog
data[0x7a9ed4:0x7a9ed4+6] = b'\xB9\x23\xFF\xFF\xFF\x90'
with open(P, 'wb') as f:
f.write(data)

View File

@@ -0,0 +1,113 @@
# frida_rpc_player.py
'''
POST /WebObjects/MZBuy.woa/wa/buyProduct HTTP/1.1
Host: p46-buy.itunes.apple.com
User-Agent: iTunes/12.6.5 (Windows; Microsoft Windows 10.0 x64 Enterprise Edition (Build 19042); x64) AppleWebKit/7605.1033.1002.2
Content-Length: 1226
Content-Type: application/x-apple-plist
X-Dsid: 12263680861
X-Apple-Tz: 28800
X-Apple-MD: AAAABAAAABA3hIN/KYf4Ri4tdZAiwaB5
Cookie: amp=JG7EpvImu+/h0yS5mTqQMLarQce6xuYNfyzC6cun/bRt+7VLQUqfUrDB4cdRyV6SLDGBRCG3Oz/PcY28nx94GdTGkdOYX83a54KHZTSq3jI=; amia-12263680861=dPQK5oK/9b6q7HeGIids4a6gE5oiE8yLcVjQO7EknSTERhI0OawL4FQcmJ60560H+PbZhCCt6Na8jLSDiU09aw==; mt-asn-12263680861=2; mt-tkn-12263680861=AmDauzfLChTup9vGq17bGVJvSurhbsyoCLyrpJG8t8uECuyAI3xr3o/vS9pVpKeg4dSlCxJtLUS8+H47Qtpou0VGgGBAn/dXAPozhdJKTyPdUqyT4My89dujXg48XNgIVUjFj2w4IhN1gg5wn3BfSg1bFChodxuHadsRibnYzkUPmR+TVTnivu9e4/QLJmh58Va8rmg=; mt-tkn-10255130069=Ala2nKdNzZWOlapGgvRzcNExINc8j4sbrcMn3ruJ9M3Cb17MGiGqvyTe9yUzh/fSNLEL+4s0j57Eht/AWXrG2e3+GptHZlTi1vZ4a+3PdD9dtfSP8no7s9/bMX6JTtnE+fwV1z8QudsvAs+kjEz0MqFgoxFZZntBKI1L81+CBTnmsBolRdk+wqqPGZ3AD5bdybUTwD4=; countryVerified=1; mzf_in=467080; itspod=46; vrep=CN3W49ctEgQIChAAEgQICBAAEgQIBxAAEgQIBBAAEgQIDRAAEgQIBhAAEgQIDBAAEgQIAxAAEgQICRAAEgQIEBAAEgQIDhAAEgQIARAAEgQIDxAAEgQIERAAEgQIBRAAEgQICxAAEgQIAhAA; wosid-lite=jRoFTHWbZZZkSOfvfDELf0; mz_at_ssl-12263680861=AwUAAAECAAHXHQAAAABgqAeMT5/7LbF5106/zxFn9xH4n7jgwEw=; mz_at0-12263680861=AwQAAAECAAHXHQAAAABgp/7Z1enk1z4615lhp9QKlDQfmo7Hq60=; pldfltcid=524b36a58d0d4249a127affd05921dab046; X-Dsid=12263680861; xp_ab=1#WqjkRLH+-2+TCEF_ea00#yNFpB6B+-2+c9imSgD01; xp_abc=TCEF_ea00; mz_at0-10255130069=AwQAAAECAAHXHQAAAABgnNBzeacT12/WPi/iSOROyaMVQrfmkqE=; mz_at_ssl-10255130069=AwUAAAECAAHXHQAAAABgnNBzNHT9PbcrsAARLEWi0+8ElnKZ54c=; xp_ci=3z3h6Y0vzFQQz4XtzB5DzZvQkYhyo
X-Apple-AMD-M: rjK8HkwTcIoiOk6UoA9LXta0h/19+ss41RU7YLxGunAtLnl1Fus5i4VS56O71ifgDWwrEl/jIbPrDLfS
X-Apple-AMD: AAAABAAAABCqItI0AgfIcPIc0+zGpNtr
X-Apple-Store-Front: 143462-9,32
Accept-Language: zh-cn
Date: Fri, 21 May 2021 21:33:42 GMT
X-Apple-MD-M: rjK8HkwTcIoiOk6UoA9LXta0h/2Qyn969daQuVAdJbkmCnWee7s+joaQ3fvc4v1JTJB0sUJcDwc0Q12z
X-Apple-I-MD-RINFO: 50660608
X-Apple-I-MD-M: rjK8HkwTcIoiOk6UoA9LXta0h/0W22FXNYql4KAM12/n20O3iVf54IK85PbrLb51CvEfn5/b6eoX5d1d
X-Apple-I-MD: AAAABQAAABA+EMexoFPqOMeyQ5zIkQYPAAAAAg==
Accept-Encoding: gzip, deflate
Connection: close
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0">
<dict>
<key>guid</key>
<string>46994789.C2E39C5C.B1D62DC2.025D2569.3D6E49B5.AB7F7EB4.F6D17565</string>
<key>kbsync</key>
<data>
AAQAA5+HiDofKn/iW9aKVsKo1XU0cRuzPrQGMfC57fQaA7YaZBWlSS+s4SS2Ay6pchYG
x/h+cJccHMoA/NDtHcWsRl9nmDwzAnuW9WH7nHQAEt95wkj2eMQVBHyj3OSkSoEGFnS8
r4irSIve3V4np1lvbEUgHdJmOEV6gAd8SFzItmgxFImn55qoUX/xt4pdXagPmhBXaYFI
+B/uYLxOYLO4fM1fGpQQ8/1EIpzrbahFjpG8L8kMjhVIWpn2Ut30p2deyjcL47JbSjx7
/RId4XZlSEqXf9SOETcsxCkVhDWTghGYRWKlEe7j32CVrxOkg2lvlQLHB1XBhTpQiLSm
PR0hDIG5dQif0jaIeQJ9zGqq3kgkl9RlFHGbcvfMdrJsYGOgYMzXh1jfF/v21xPfauYh
PIE9LQu/TTiAGDvS+BYrM2xVaPHZPNUpLaVlk/2zIqiWo/Zj2se4FXGOl4LAVKqrbOYN
2YONkSY4CQOZTmNmoO6SBU2NJHBYtVVeIhOLGLoS9/xzPwMdFevvZJFD7iQvL0RDKCBt
z5r7dnFz2WlZsdx0aMsbV3HRdnJtmBh7zA3AaO/4rJtWAQqAID2gAF8YMoNdJJp13Bdr
f34iRpVYbsDxrBNJXtanTgDdNO3xXpyZhQTgOrBDcsv7mD68E1E6bE5ac97/z9R8cLf0
8/X8/ie7tUBcqVThW81DiM4RGh3LSg==
</data>
<key>price</key>
<string>0</string>
<key>pricingParameters</key>
<string>STDRDL</string>
<key>productType</key>
<string>C</string>
<key>salableAdamId</key>
<string>1234806557</string><key>appExtVrsId</key>
<string>839637337</string>
</dict>
</plist>
'''
import sys
import codecs
import frida
import plistlib
import os
import base64
##### Frida Part
procName = os.environ.get('ITUNES_PROCESS_NAME', 'iTunes.exe')
session = frida.attach(procName)
#session = frida.attach('iTunesFucked.exe')
def eprint(*args, **kwargs):
print(*args, file=sys.stderr, **kwargs)
with codecs.open(os.path.dirname(os.path.realpath(__file__)) + '/get_header_rpc.js', 'r', 'utf-8') as f:
source = f.read()
script = session.create_script(source)
script.load()
def on_message(message, data):
if message['type'] == 'send':
eprint("Frida: %s" % message['payload'])
elif message['type'] == 'error':
eprint("Frida error: %s" % message['stack'])
script.on('message', on_message)
rpc = script.exports
##### Flask Part
from flask import Flask, request
from flask import jsonify
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def getHeader():
hdrUrl = request.args.get('url', "https://p46-buy.itunes.apple.com/WebObjects/MZBuy.woa/wa/buyProduct")
retHdrs = rpc.get_header(hdrUrl)
eprint("Got Headers: %s" % retHdrs)
kbsync = retHdrs.pop('kbsync')
guid = retHdrs.pop('X-Guid')
return jsonify({
"headers": retHdrs,
"kbsync": kbsync,
"guid": guid
})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=9000)
session.detach()

View File

@@ -0,0 +1,100 @@
var getHeader = null;
function init() {
var CFURLCreateWithBytes = new NativeFunction(Module.findExportByName('CoreFoundation','CFURLCreateWithBytes'), 'pointer', ['pointer', 'pointer', 'int64', 'int64', 'int64'])
var CFDictionaryGetCount = new NativeFunction(Module.findExportByName('CoreFoundation','CFDictionaryGetCount'), 'int64', ['pointer'])
var CFCopyDescription = new NativeFunction(Module.findExportByName('CoreFoundation','CFCopyDescription'), 'pointer', ['pointer'])
var CFStringGetCStringPtr = new NativeFunction(Module.findExportByName('CoreFoundation','CFStringGetCStringPtr'), 'pointer', ['pointer', 'uint64'])
var CFStringGetCString = new NativeFunction(Module.findExportByName('CoreFoundation','CFStringGetCString'), 'int8', ['pointer', 'pointer', 'int64', 'int64'])
var CFStringGetLength = new NativeFunction(Module.findExportByName('CoreFoundation','CFStringGetLength'), 'int64', ['pointer'])
var CFDictionaryGetCount = new NativeFunction(Module.findExportByName('CoreFoundation','CFDictionaryGetCount'), 'int64', ['pointer'])
var CFDictionaryGetKeysAndValues = new NativeFunction(Module.findExportByName('CoreFoundation','CFDictionaryGetKeysAndValues'), 'void', ['pointer', 'pointer', 'pointer'])
var CFDataGetBytePtr = new NativeFunction(Module.findExportByName('CoreFoundation','CFDataGetBytePtr'), 'pointer', ['pointer'])
var CFDataGetLength = new NativeFunction(Module.findExportByName('CoreFoundation','CFDataGetLength'), 'uint64', ['pointer'])
var readCFStr = (cfs) => {
var cfslen = CFStringGetLength(cfs)
var cfsBuf = Memory.alloc(cfslen + 1)
CFStringGetCString(cfs, cfsBuf, cfslen + 1, 134217984)
return Memory.readUtf8String(cfsBuf, cfslen)
}
var readCFDict = (cfdict) => {
var dictCount = CFDictionaryGetCount(cfdict)
var dictKeys = Memory.alloc(dictCount * 8)
var dictValues = Memory.alloc(dictCount * 8)
CFDictionaryGetKeysAndValues(cfdict, dictKeys, dictValues)
var ret = {}
for (var i = 0; i < dictCount; i++) {
var k = readCFStr(dictKeys.add(8 * i).readPointer())
var v = readCFStr(dictValues.add(8 * i).readPointer())
ret[k] = v
}
return ret
}
var readCFData = (cfd) => {
var cfdlen = CFDataGetLength(cfd)
var cfdbuf = CFDataGetBytePtr(cfd)
return cfdbuf.readByteArray(cfdlen)
}
var itunesBase = Process.enumerateModulesSync()[0].base
var cfAllocator = itunesBase.add(0x22A8448).readPointer()
var kbsyncContext = itunesBase.add(0x22A6BBC).readU32()
var prepareAppleHdrWrap = new NativeFunction(itunesBase.add(0x87FFC0), 'pointer',['pointer','pointer','pointer']);
var get_cookie_val = new NativeFunction(itunesBase.add(0xBDE500), 'pointer',['pointer']);
var get_kbsync = new NativeFunction(itunesBase.add(0x74D210), 'pointer',['uint32', 'uint32', 'pointer']);
getHeader = function (url) {
var GlobalContext = itunesBase.add(0x22A9B18).readPointer()
var otpGlobalContext = GlobalContext
if (!GlobalContext.isNull()) {
otpGlobalContext = GlobalContext.add(62716).readPointer()
}
var otpContext = Memory.alloc(128)
otpContext.writePointer(otpGlobalContext)
otpContext.add(8).writeU64(0)
otpContext.add(16).writeU64(0)
otpContext.add(24).writeU16(1)
otpContext.add(26).writeU8(0)
otpContext.add(27).writeU8(1) // include X-Guid
otpContext.add(28).writeU8(1) // include ADIV1 Hdrs
otpContext.add(32).writeU32(0)
otpContext.add(36).writeU16(0)
otpContext.add(40).writeU32(0)
otpContext.add(48).writeU64(0)
otpContext.add(64).writeU64(0)
otpContext.add(72).writeU64(0)
otpContext.add(84).writeU64(0)
otpContext.add(112).writeU8(1)
var urlBuf = Memory.allocUtf8String(url)
var urlData = CFURLCreateWithBytes(cfAllocator, urlBuf, url.length, 0x8000100, 0)
var kbsync = get_kbsync(kbsyncContext, 1, otpGlobalContext)
var cookieCFStr = get_cookie_val(urlData)
var hdrDict = prepareAppleHdrWrap(NULL, urlData, otpContext)
//hdrdesc = CFCopyDescription(hdrDict)
//console.log(readCFStr(hdrdesc))
var cookieStr = readCFStr(cookieCFStr)
var hdrOutput = readCFDict(hdrDict)
hdrOutput['Cookie'] = cookieStr
var kbSyncData = readCFData(kbsync)
hdrOutput['kbsync'] = [...new Uint8Array(kbSyncData)].map(x => x.toString(16).padStart(2, '0')).join('');
return hdrOutput
};
}
rpc.exports = {
getHeader: (url) => {
if (!getHeader) {
init()
}
return getHeader(url);
},
};

View File

@@ -0,0 +1,13 @@
cd %~dp0
curl -LO https://secure-appldnld.apple.com/itunes12/091-87819-20180912-69177170-B085-11E8-B6AB-C1D03409AD2A6/iTunes64Setup.exe
iTunes64Setup.exe /extract
@REM start /wait msiexec.exe /i AppleApplicationSupport.msi /qn
start /wait msiexec.exe /i AppleApplicationSupport64.msi /qn
@REM start /wait msiexec.exe /i AppleMobileDeviceSupport64.msi /qn
@REM start /wait msiexec.exe /i AppleSoftwareUpdate.msi /qn
@REM start /wait msiexec.exe /i Bonjour64.msi /qn
start /wait msiexec.exe /i iTunes64.msi /qn
python3 patch_itunes.py

View File

@@ -0,0 +1,22 @@
import os
# for debug
P = r"C:\Program Files\iTunes\iTunes1.exe"
if not os.path.exists(P):
# for production
P = r"C:\Program Files\iTunes\iTunes.exe"
with open(P, 'rb') as f:
data = f.read()
data = bytearray(data)
# Patch passwordSettings (actually useless)
data[0x77daf4:0x77daf4+5] = b'\xBF\x03\x00\x00\x00'
data[0x77db7e:0x77db7e+2] = b'\xEB\x0A'
# Patch signIn reason to serverDialog
data[0x7a9ed4:0x7a9ed4+6] = b'\xB9\x23\xFF\xFF\xFF\x90'
with open(P, 'wb') as f:
f.write(data)

View File

@@ -0,0 +1,156 @@
import subprocess
import time
from pywinauto.application import Application
from win32con import *
import sys
ACCOUNT = sys.argv[1]
PASSWORD = sys.argv[2]
print("Launching iTunes...")
def initITunes():
subprocess.call('taskkill /f /im iTunes*', shell=True)
app = Application().start(r"C:\Program Files\iTunes\iTunes.exe")
app.wait_cpu_usage_lower()
time.sleep(8)
def debugTopWin():
topwin = app.top_window().wait('exists')
texts = []
texts += topwin.texts()
for c in topwin.iter_children():
texts += c.texts()
print("-- Cur top win: %s, texts: %s" % (topwin, texts))
def cleanAllDialog():
while True:
topwin = app.top_window().wait('exists')
if 'Dialog' in topwin.class_name():
print(" Closing dialog %s" % topwin.window_text())
app.top_window().Button0.click()
elif 'Tour' in topwin.window_text():
print(" Closing Window %s" % topwin.window_text())
topwin.close()
else:
break
app.wait_cpu_usage_lower()
time.sleep(5)
# Click all first-time dialogs (like License Agreements, missing audios)
cleanAllDialog()
# Calm down a bit before main window operations
app.wait_cpu_usage_lower()
debugTopWin()
# Click main window's first-time question ("No thanks" button)
try:
buttonText = app.iTunes.Button11.wait('ready').window_text()
print('Button11 text is: %s' % buttonText)
if 'Search' not in buttonText:
print("Clicked 'No Thanks' Button!")
app.iTunes.Button11.click_input()
app.wait_cpu_usage_lower()
time.sleep(4)
else:
raise Exception('stub')
except:
print("Not founding 'No Thanks' Button, passing on...")
# Start logging in by clicking toolbar menu "Account"
print("Clicking Account menu...")
app.iTunes.Application.Static3.click()
app.wait_cpu_usage_lower()
time.sleep(3)
debugTopWin()
# Detect whether we have "&S" in popup, which refers to "Sign in"
popup = app.PopupMenu
if '&S' not in popup.menu().item(1).text():
popup.close()
raise Exception("Already logged in!")
print("Signin menu presented, clicking to login!")
# not log in
popup.menu().item(1).click_input()
app.wait_cpu_usage_lower()
time.sleep(8)
debugTopWin()
for i in range(15):
dialog = app.top_window()
dialogWrap = dialog.wait('ready')
assert dialogWrap.friendly_class_name() == 'Dialog'
time.sleep(1.0)
try:
if dialogWrap.window_text() == 'iTunes' \
and dialog.Edit1.wait('ready').window_text() == 'Apple ID' \
and dialog.Edit2.wait('ready').window_text() == 'Password' \
and dialog.Button1.wait('exists').window_text() == '&Sign In':
break
except Exception as e:
continue
else:
raise Exception("Failed to find login window in 15 iterations!")
app.wait_cpu_usage_lower()
print("Setting login dialog edit texts")
appleid_Edit = dialog.Edit1
appleid_Edit.wait('ready')
appleid_Edit.click_input()
appleid_Edit.type_keys(ACCOUNT)
appleid_Edit.set_edit_text(ACCOUNT)
time.sleep(3)
pass_Edit = dialog.Edit2
pass_Edit.wait('ready')
pass_Edit.click_input()
pass_Edit.type_keys(PASSWORD)
pass_Edit.set_edit_text(PASSWORD)
time.sleep(3)
print("Clicking login button!")
loginButton = dialog.Button1
loginButton.wait('ready')
# click multiple times as pywinauto seems to have some bug
loginButton.click()
time.sleep(0.5)
try:
loginButton.click()
time.sleep(0.5)
loginButton.click_input()
except:
pass
print("Waiting login result...")
time.sleep(10)
debugTopWin()
if app.top_window().handle == dialogWrap.handle:
raise Exception("Failed to trigger Login button!")
elif app.top_window().window_text() == 'Verification Failed':
raise Exception("Verification Failed: %s" % app.top_window().Static2.window_text())
# Finish & Cleanup
print("Waiting all dialogs to finish")
cleanAllDialog()
for init_i in range(3):
try:
initITunes()
break
except Exception as e:
print("Init iTunes %d: Failed with %s" % (init_i, e))
import traceback; traceback.print_exc()
time.sleep(8)
print("Init iTunes Successfully!")

View File

@@ -0,0 +1,2 @@
Libimobiledevice compiled for Windows
Updated 18/05/2020. Compiled by iFred09

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

54
src_win/win_server.py Executable file
View File

@@ -0,0 +1,54 @@
#!/usr/bin/env python3
from http.server import BaseHTTPRequestHandler, HTTPServer
from pathlib import Path
import os
import socket
import subprocess
os.chdir(Path(__file__).parent)
INSTALLER = Path('libimobiledevice', 'ideviceinstaller.exe')
PATH_IN = Path('queued')
class IpaServer(BaseHTTPRequestHandler):
def reply(self, status: int, data: str):
self.send_response(status)
self.send_header('Access-Control-Allow-Origin', '*')
self.send_header('Content-type', 'text/plain')
self.end_headers()
self.wfile.write(data.encode('utf8'))
def do_POST(self):
length = self.headers.get('Content-Length', 1)
data = self.rfile.read(int(length)).decode('utf8')
if self.path == '/up':
return self.reply(200, 'YES')
elif self.path == '/install':
fname = PATH_IN / data
if not fname.exists():
return self.reply(404, f'File not found "{fname}"')
subprocess.run([INSTALLER, '-i', fname])
elif self.path == '/uninstall':
bundleId = data
subprocess.run([INSTALLER, '-U', bundleId], timeout=60)
else:
raise ValueError('unsuppoted API path')
return self.reply(200, 'OK')
def getLocalIp():
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.connect(('10.255.255.255', 80))
ip = s.getsockname()[0]
s.close()
return ip
if __name__ == '__main__':
webServer = HTTPServer(('0.0.0.0', 8117), IpaServer)
print('Server started http://%s:%s' % (getLocalIp(), 8117))
try:
webServer.serve_forever()
except KeyboardInterrupt:
pass
webServer.server_close()