[
  {
    "path": ".gitmodules",
    "content": "[submodule \"ExAndroidNativeEmu\"]\n\tpath = ExAndroidNativeEmu\n\turl = https://github.com/maiyao1988/ExAndroidNativeEmu.git\n"
  },
  {
    "path": "BooxKeyConvert.py",
    "content": "#!/usr/bin/env python3\ntry:\n    from Cryptodome.Cipher import DES\n    from Cryptodome.Hash import MD5\nexcept ModuleNotFoundError:\n    from Crypto.Cipher import DES\n    from Crypto.Hash import MD5\n    from Crypto import version_info\n    if version_info[0] == 2:\n        raise SystemExit('Need either pycryptodome or pycryptodomex, NOT pycrypto!')\nimport sys\nfrom base64 import b64decode\nfrom base64 import b64encode\n\ndef decryptStr(tmpKey: bytes, string: str) -> str:\n    if len(string) != 44:\n        print('Key is not the correct length of 44')\n        return None\n    try:\n        data: bytes = b64decode(string)\n    except:\n        print('Key is not valid base64 encoding')\n        return None;\n    if len(data) != 33:\n        print('Base64 decoding of key returned length less than 33')\n        return None;\n    cypher = DES.new(tmpKey, DES.MODE_CFB, iv=b'\\xff' * 8, segment_size=64)\n    data = cypher.decrypt(data)\n    try:\n        out: str = data.decode()\n    except:\n        print('Key did not decode correctly for this model')\n        return None\n    if out[32] != '\\n': print('Decrypted key missing standard newline')\n    return out[:32]\n\ndef encryptStr(tmpKey: bytes, string: str) -> str:\n    cypher = DES.new(tmpKey, DES.MODE_CFB, iv=b'\\xff' * 8, segment_size=64)\n    out: str = b64encode(cypher.encrypt(bytearray(string.upper()+'\\n', 'ascii'))).decode()\n    return out\n\nif __name__ == '__main__':\n    if len(sys.argv) >= 3:\n        model = sys.argv[1];\n        tmpKey: bytes = MD5.new((model * 2).encode()).digest()[:8]\n        for i in range(2, len(sys.argv)):\n            if i > 2: print()\n            x = sys.argv[i]\n            print(x)\n            y = decryptStr(tmpKey, x)\n            if y != None:\n                print(y)\n                z = encryptStr(tmpKey, y)\n                if z != x: print('Key does not \"round-trip\" correctly')\n    else: print('Usage: BooxKeyConvert.py <model> <settings> <upgrade>')\n"
  },
  {
    "path": "BooxKeys.csv",
    "content": "Name,Model,Key,IV\nDarwin11-ru,DARWIN11,68954E31C8EA505B646641AF2015B63C,C00286616C61DB326065BE988D1C1F90\nDarwin7,MC_DARWIN7,9272F672593B96AE388849C30EC9272F,7EEF6EF4C34231E4E16A4C688DBF7C4B\nFLOW,FLOW,42F6E69DB0A176DC5FBA04DE2C4C0C7D,9C0F30369B5539A1E0696598638D4731\nFLOWPro,FLOWPro,714C5E4655170DA6774E7E1DFCF78D9E,08A0369CAE896E52AF8694A2EF39C1EE\nFLOWProMax,FLOW.Pro Max,6D0D6E2DBDF3D5D6C71B84E170E1E33A,11DFA3EAC57A65E2F9731DA5F278013C\nGALILEO,GALILEO,F303E7819D87666785B5CEE6C172201D,103040EE4F225D67A607B25337E787B9\nGalileo2-ru,GALILEO2,B510E6FBDCF05596BED85DF4EEA293CB,B5A3B196ABE1310018A45FED291B856A\nGo103,Go103,7BDBEE7DB0CB42F3334C40FA67491C72,951CB9A08616E7E230386931AB090AEF\nGo6,Go6,09743FA75F5B9899787665A8806085A3,D294C0832EACF2EEEE0E944AB5928784\nGo7,Go7,DFEA378BF727024CD7121DCD5856228C,1B534CD3E821247C8BD39D4AF0301DAA\nGoColor7,GoColor7,73AA85E6BAF08452FC583DA8A533642A,4F573D5A9AE3857F8D220C7CF6BE32A8\nGoColor7_2,GoColor7_2,8B2A051FF85FFD266C3639F8AF47C6F8,30A4B381DB0E88188A065AF1B1C78A61\nGulliver-ru,MC_GULLIVER,0CBE6C03F589353182354085D8E20F31,7E34FC7B6E6FD0A39C0E43CBAF32FB52\nJDRead2,JDRead2,E4CEA0CD7CDB7BBC79BC651D3A98F96F,22430CF10E1F604E88BF32F0B683BBF7\nKANT,KANT,13BC5939158355469CE617D7733DF4CB,35EB192AF12EF49206C8D747A49F0EAC\nKonTiki-ru,KON_TIKI,071C5480AFAF91DD0EEA6EA1F4FC8951,7BCF8715C94BDD5FB684E0F66B9F57B5\nKonTiki2-ru,Kon_Tiki2,98AC5EB0D8D7A0A0B2E75B278DB8B288,745555DF150183D4C92AFBC4EDE58ED2\nKonTiki5-ru,Kon_Tiki5,AAE96BDB70457A536D2123DCD301A5A5,4B64E4867EF473E7BEDC652A1B737C6C\nLeaf,Leaf,2FA5F9484C5079FA25B89FD6A926F0FD,0CB5EBC30E284C71108ACC344026DFD6\nLeaf2,Leaf2,4131D58DC6CB5D85BFD23D9F576CAB16,A60D8B8DEE2A494194F1AC6C80E850AC\nLeaf2-cn,Leaf2_P,E95EE8726EC4B63A2241F38829C27FCC,8093A741A8A4C45E11690B72D78C59A1\nLeaf3,Leaf3,6ABE99B43B928F351C83CDD5A2516A6E,1009E8860F53BB40483A83173F844198\nLeaf3C,Leaf3C,59A002BBBF0C9AF21FE068AAB8712E39,ADCD327395970FD0B8AA0159AB057341\nLeaf5,Leaf5,648C324326F16772931171609F1EC0F6,DFA378E2340EF490F5EB6C8EDBDB97B5\nLeaf5C,Leaf5C,66C121409BAF575EBA9F56C6127F5DEF,84EC4607094D49AAAF3A3946000E3669\nLivingstone-ru,LIVINGSTONE,CDA476FB48B341F03C3217EBDFCA1BFA,B642BDF244FCC321E1A62BBE0EE4C15A\nLivingstone2-ru,LIVINGSTONE2,5517687D3994A7EB439DA99F87134B88,509E86C0948605D593A44D70143529C6\nLivingstone3-ru,LIVINGSTONE3,80E2F29AA083CBFBB065DFBA1922F1CC,1D8FBDAE2AB78E1DD0D6008036DE5667\nLomonosov-ru,Lomonosov,D2F7E3354FC07C2521BF609D01FBC90F,0070D3F87818CF23DB5EE9798B8B0657\nMax2,Max2,7B71BD797D7E3721D69F4449E12B4C22,38B2BB851DAF20E1BE6E7E3F349CABA0\nMax2Pro,Max2Pro,8BEAEE4D037DAC8E2DB18458CDFA4A53,706038B3497224CCC2B81572EACAA780\nMax3,Max3,0C3FF83E57334391A2D4171E99EAC1A9,982877D54100A07BBF205F31193B2D3D\nMaxLumi,MaxLumi,3205089AF7E08108730ABFC237D94BE8,09FE14E89ABE38654112885A43352D30\nMaxLumi2,MaxLumi2,25F86689E5FE3DEB2856983CCF21FB6D,14E98D0F357F7087D27D22406D7291E0\nNote,Note,1D7A5202269D384B72F348CE25D55120,A0828A338A06FF2CD3618B219D3E30E2\nNote2,Note2,47363CF9416ADE0231B61D62DD8F4539,462B820F6C4B3BC72EE48FDFF0FA3E39\nNote3,Note3,098C863EB6E1F8DC96720031B32EA200,EFC95EF6B02442EE518F2D2BD5594250\nNote4,MC_Note4,182F38C6C9C292A0E4037730DDA1A509,F5852333D9EF9ADA35BA05824C0EAD65\nNote5,Note5,FEACD5CEDBD266D7E84C560C4BE95511,2EFCE01264CD8DA64307434F887ED737\nNote5Plus,Note5Plus,9377320B7032EDC0194DBA82AD9DDCB0,9B7422F02E481B143030F24F5F3736B0\nNoteAir,NoteAir,AED5E8AE8AB08319EC791849310171C2,F62715B99809212131696C2FD7C6F75D\nNoteAir2,NoteAir2,856C9D4ADCE8015F272357F5DDF08B71,879D69054D57C76152CF272A0C121006\nNoteAir2P,NoteAir2P,1276ECC6636DA6AFCEF5D7F300E5C915,1F6CF46EC279E61EDB7FC150C3471972\nNoteAir3,NoteAir3,54F4360CD7465F72D31957D9DAF017C7,40FE355773181D3B8D0B3E41B96D6998\nNoteAir3C,NoteAir3C,4DEAED7F843CDCEDB384E6FC468C540E,964E8856F922178810AEC06E0D363512\nNoteAir4C,NoteAir4C,CF26B9FD1C5F74A8170C24D389F9A92D,3BC02F669B2C772CC3F9A583F1FC3263\nNoteAir5C,NoteAir5C,3829B3AEBF3600BD28AB88B887E7B1F0,1B16F54416A306113EA5B7B4CB5F65A3\nNoteMax,NoteMax,D999393AEAA81119AC0F8C8C1EA11089,CD7894A509490BAF2BAA129686A083E4\nNotePro,NotePro,F72D46836B5B1A0D1CB5BB27C6895A4F,2652833CDDC4C4CDD3BEC6F8D6AD3914\nNoteS,NoteS,0B82F60DB060E404F752E9B91F0BE28B,363A003F2C54F1DA3412F7A9CC7DFDBE\nNoteX,NoteX,8CC1464D7009076D571D4FF249F47B3D,2E3D8332638FED444BA24D5462C12046\nNoteX2,NoteX2,00D996A8B45734B9E7943C0FF3333646,AE9ACFDF9C871C2B9458B2F2245035E0\nNoteX3,NoteX3,40B1F5E5773AA90DC41ED62AA34EEF6D,C41B0F1CD0E88A0CC6CA22E1429AC4A3\nNoteX3S,NoteX3S,37BD0706BCBCA53265083A292966FD4D,6EB0274D85707BA9729580E49D0890AA\nNoteX3Pro,NoteX3Pro,7002375BDC1F2FE5FD29C3A0D3470883,C5334CAA3B27498621CFB7744F578797\nNoteX5,NoteX5,16640A316BEE96785C799BC419D002AC,8CFF7D62605DE3809106B3136177C5CF\nNoteX5Mini,NoteX5Mini,6E18EA16B323F39B1CE2CA6626843F17,1F709679CD98CDF5E01FC6974928E985\nNote_Lite,Note_Lite,BD78142B675385319A5DD30AB786E743,5898BFC811708BC8DF67EEB6186F8752\nNote_YDT,Note_YDT,A5BEEB144025D73FD90971E4906232BA,18870C4F426A494320868606DA2091BB\nNova,Nova,F03A6BBC306066AB1B3A5BE5AEC1D3F3,558CD7D43E17869A50FFFF6C72C26282\nNova2,Nova2,0AC6B91ABB5988ADE6313FD62F6EDB0B,3B9D6F6DE8D1795D3D265C0EFC9B59C7\nNova3,Nova3,6D89ABF9B380998958D8002897BBF650,7F1224824C0B41DAAE907ADE10828C58\nNova3Color,Nova3Color,796FD580A4BE82B6FEAC8CFDA676585B,AFB0DFEBD0A2D71F37F21231808BD1A9\nNova5,Nova5,96359CD7CCB6F86C38D808E2C4109B70,0F24CA95466CE4F0CE6477CE20EDCB5D\nNovaAir,NovaAir,20A25EB3B9363810BCFFA9A2E1B55B0A,1B5380830BF637ECB7181C3926D5B479\nNovaAir2,NovaAir2,88520228DF831062B6CB7AAED5166156,C0A91A092BC76A46F7AE6FDC4EED5C20\nNovaAirC,NovaAirC,3E37242727DD67DF9D4EC08D936974E2,4DB17A985684B7319A349743B22BF41B\nNovaAirS,NovaAirS,93E8EAF62AFAEA06D735CE5C07ABD390,083BB627BC2DD6290CD972874BC767F7\nNovaPlus,NovaPlus,18C0438BE7CE881BC86BC6C84CEC096D,592328C13A578E053FA565A5B684294D\nNovaPro,NovaPro,619A3C6290FEDA02418091AE2F39592F,C5DCEF361A60239DB4E8648DD9F7847A\nP6,P6,E1C71F36EC30FFCC2FEC7501AD62FEC2,7B457D7E495B0B860755AFE362E9E5BF\nPadMu3,PadMuAP3,A8BAA1F101DB9FC7CA0CD9FB04A97694,16ED3B30C6F251BA4267679433765E5F\nPage,Page,25CC82D3A7B62D4F03321946F135037D,8D2BC33F82F6A14E04085574947687D0\nPalma,Palma,0E729B7296ACCB86AE5A6C15374C4D02,BF694808B49ACAF49E73334E3A4B1163\nPalma2,Palma2,EBF46CA428B21CD4012B17F6E37F132D,CE5D3FD02F712FE0E30F7DC31B0789E9\nPalma2Pro,Palma2Pro,2B818F3CAD5200FB983C3C5867EA5DF5,006AAA200C7E8C905F2BFCE80E0A6156\nPoke2,Poke2,C5E45730FBD665FC3857C6D78FD63E43,528DB4C57394F09D3C00E2D45E5C9641\nPoke2Color,Poke2Color,D314CF54B76931B6309F83B2F037D419,C397190FD2E48874C1CFBE52A782AB59\nPoke3,Poke3,3DC53116D8AE3DCCEAD99F53E08E1E35,42B996AB6E252DCA4EDBC668BA3E5A3A\nPoke4,Poke4,E155E2D73EFC3EB70745B434BE203EFD,C3AC2D3955E32709F10511C3B662B27F\nPoke4Lite,Poke4Lite,6C74952C3EA2E07244D6B3D91C222335,EB037125447814B0F4198E201A966B3D\nPoke4S,Poke4S,CFD60D360BD91B04843F8B3090B2DA08,3595F7FCE8B4F2AD5804DEE46299281C\nPoke5,Poke5,59B93DC6771B6740C266DD2AC851943C,4CF0CCAE9C4201B24D623221B9BA6673\nPoke5P,Poke5P,550504B1DE28C363172040829BFFA408,26C4D62584DA0619AB972B66ACC3689A\nPoke5S,Poke5S,AE0FB835F4A50D35C95FEC7700EEDF1D,321FFD1D14E8A2A7A422C683D152AF3B\nPoke6,Poke6,DFA1B679E1089011FEB9C8455AD7E1D5,83BF5F0F69A76D42D742E044EA1BDACF\nPoke_Pro,Poke_Pro,AA151AD6C888DFB010433F3B1EC676EC,FD83B38456D9C052F22414DF9A2C0FEC\nSavi6,Savi6,C88A656DF7A4A4CD137892693622EFCE,21970EF67B88056928F827D9023C9B15\nSP_NoteSL,SP_NoteS,3992B2A532F9ABDCB953EDAD7EC95FFF,12C928CF2C665AF83B50361C80B88297\nSP_PokeL,SP_PokeL,6E01CB5A77A96F675561AE7CB7CAF6AC,DEAEF764C4290B4039EA7DDC2110A7B2\nT10C,T10C,21B04F8458DCD400D6DD2FEDDF13BC16,C78066550FE7CE6CCAFB692FFFB0E3F5\nTab10,Tab10,C301F33490ABF2C9396E335D3258BD37,2B2B6159E3CA418F71D58966310B764F\nTab10C,Tab10C,3E3F7FB56DD7DF682D9F49070543B4F0,37E49117577D5BF42771F8E0DAAFBA73\nTab10CPro,Tab10CPro,D999393AEAA81119AC0F8C8C1EA11089,CD7894A509490BAF2BAA129686A083E4\nTab13,Tab13,1072DE10B43097036B98B9021F308FB0,F180C655AABF3EB02D9E45B9FD5379B2\nTab8,Tab8,ECC953580D49BAD70D9DBA526EC091D2,46085FF59CCE685B38FB651876E8964B\nTab8C,Tab8C,D7731639E43C1C421F25178D79A07031,A419E9D70B549920F91DD28733A51A46\nTabMiniC,TabMiniC,3B0B382BDA19D0E9F0755C8B01B086DC,A602693F5AF19546BF7F40A523237E54\nTabUltra,TabUltra,A01FF8E0F4D139FC75FAA49D61E12E9C,EC94D4BA3550734EA8C69DD726444DE3\nTabUltraC,TabUltraC,2DB550268389D895AACB781AB3515DCB,13FDC3C394EAB6323BF42428CD275AA2\nTabUltraCPro,TabUltraCPro,A41D77766544408A879E2796FA58FBEF,CE33186077354F08421AB9F0C2648A06\nTabX,TabX,28E3BE85609F0D5CAA3796643512AAA2,8952832CBE65133CF531C978B7AC0696\nViking,Viking,F702F339BD6D31CB7F4252D6071A8C97,F51563B4B58AB1E673590E904F632361\nZYJ101IOE3,ZYJ101IOE3,21EE6EB8FEC5A5D10A2A3F5C73FB954B,F9898DEAC0E5DC9B0FB861D149194BDF\nZYJ103ION2,ZYJ103ION2,455521D97400F49CED0B7894CEF6720B,607C57A6F3333EC90A136263620C5B3D\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# CONTRIBUTING\n\n## New decryption keys\n\nIf you have a device for which no decryption keys are known and listed in [BooxKeys.csv](BooxKyes.csv) you can help by supplying them.\nYou can start an [Issue](https://github.com/Hagb/decryptBooxUpdateUpx/issues), but you'll still need to supply some information for someone else to extract the decryption keys.\nYou may be able to find the decryption keys yourself.\nAs the way these decryption keys are stored has changed over time you may have to use different approaches.\n\n## Preliminaries\n\nFirst you have to determine what the actual model of your device is called.\nYes, you know what you think is the model, but the actual model name usually does not have any spaces in it and may often have a surprising suffix.\nADB is a common and necessary tool for many operations on Android.\nIf you are not familiar with it, just Google \"minimal ADB\".\nThe simple command in an ADB shell `getprop ro.product.model` will tell you the real model name.\nYou can also check the fingerprints with `getprop | grep finger`.\n\n## Decryption keys stored as resources\n\nThis was the oldest approach.\nThe app responsible for decrypting updates was usually in `/system/priv-app/OnyxOtaService/OnyxOtaService.apk`\nYou can download this app by `adb pull /system/priv-app/OnyxOtaService/OnyxOtaService.apk`.\nNow you will have to disassemble the app using [Apktool](https://github.com/iBotPeaches/Apktool).\nIf this is beyond your pay grade, just start an issue and post the apk.\nThe strings that we are looking for are stored in res/values/strings.xml in the disassembled app.\n``` xml\n<string name=\"settings\">j857wYAQcWZgvIEQ/tcQqzxreUJgFHwJl6D2TN9BuSkQ</string>\n<string name=\"upgrade\">+soGw/YVdGIRJiAs5SMmv1ihW37H1Fa9+/1w2Vgt14Ag</string>\n```\nAs these are the \"old style\" strings they need to be converted to \"new style\" strings.\nYou can use [BooxKeyConverter.py](BooxKeyConverter.py) or, as always, just post an issue.\n\n## Decryption keys stored in a library file\n\nThis is the newer approach and may have variants now and in the future.\nYou can easily see if this might be the case by the presence of a library file named `libota_jni.so`.\nYou can download this library by `adb pull /system/lib64/libota_jni.so` (for 64 bit) or `adb pull /system/lib/libota_jni.so` (for 32 bit).\nAs before, you can just start an issue and post this file if that's your limit as it gets a bit more complicated now.\n\n### Decryption keys accessed by JNI method\n\nThe decryption keys may be extracted by calling JNI methods in libota_jni.so.\nThe two JNI methods are:\n```\nJava_com_onyx_android_onyxotaservice_RsaUtil_nativeGetSecretKey\nJava_com_onyx_android_onyxotaservice_RsaUtil_nativeGetIvParameter\n```\nThe Android application [GetBooxUpxKeysApp](https://github.com/Hagb/GetBooxUpxKeysApp) or the emulator utility [ota_jni.py](ota_jni.py) can extract the decryption keys.\nIf the extracted decryption keys are old style you can use [BooxKeyConverter.py](BooxKeyConverter.py) to convert them to new style.\n\n### Decryption keys accessed by C++ call\n\nThe new style decryption keys may be extracted by calling C++ functions in `libota_jni.so`.\nThe two exported functions are:\n```\ngetKeyString(void)         _Z12getKeyStringv\ngetInitVectorString(void)  _Z19getInitVectorStringv\n```\nThere is not yet a released utility to extract the decryption keys.\n\n### Decryption keys not accessible directly from the library file\n\nThere may not be any methods or functions to directly access the encryption keys.\nPlease post your libota_jni.so and we will see what we can do.\n"
  },
  {
    "path": "DeBooxUpx.py",
    "content": "#!/usr/bin/env python3\ntry:\n    from Cryptodome.Cipher import AES\nexcept ModuleNotFoundError:\n    from Crypto.Cipher import AES\n    from Crypto import version_info\n    if version_info[0] == 2:\n        raise SystemExit('Need either `pycryptodome` or `pycryptodomex`,'\\\n                ' NOT `pycrypto`!')\nimport csv\n\nclass DeBooxUpx:\n    blockSize: int = 2**12  # 4KiB\n\n    def __init__(self,\n                 KEY: str,\n                 IV: str):\n        self.key: bytes = bytes.fromhex(KEY)\n        self.iv: bytes = bytes.fromhex(IV)\n\n    def deUpxStream(self, inputFile, outputFile):\n        block: bytes = b'1'\n        cipher = AES.new(self.key, AES.MODE_CFB, iv=self.iv, segment_size=128)\n        header_checked = False\n        while block:\n            block = inputFile.read(self.blockSize)\n            decrypted_block = cipher.decrypt(block)\n            if not header_checked:\n                if decrypted_block[:4] != b'\\x50\\x4b\\x03\\x04':\n                    raise ValueError(\"The decrypted data seems not a zip package, \"\n                                     \"please ensure that the strings or model is correct.\")\n                header_checked = True\n            outputFile.write(decrypted_block)\n\n    def deUpx(self, inputFileName: str, outputFileName: str):\n        inputFile = open(inputFileName, mode='rb', buffering=self.blockSize)\n        outputFile = open(outputFileName, mode='wb', buffering=self.blockSize)\n        self.deUpxStream(inputFile, outputFile)\n        inputFile.close()\n        outputFile.close()\n\ndef findKeyIv(path: str, name: str):\n    try:\n        with open(path) as file:\n            reader = csv.reader(file, delimiter=',')\n            line = 0\n            for row in reader:\n                if line > 0 and (row[0] == name or row[1] == name):\n                    return(row)\n                line += 1\n        return None\n    except:\n        print(f'\"{path}\" not found')\n        sys.exit()\n\nif __name__ == '__main__':\n    import sys\n    import os.path\n    if 2 <= len(sys.argv) <= 4:\n        csvPath = os.path.join(os.path.split(sys.argv[0])[0], 'BooxKeys.csv')\n        device_name = sys.argv[1]\n        updateUpxPath = \"update.upx\" if len(sys.argv) == 2 else sys.argv[2]\n        if len(sys.argv) == 4:\n            decryptedPath = sys.argv[3]\n        else:\n            basename = os.path.basename(updateUpxPath)\n            name, ext = os.path.splitext(basename)\n            decryptedPath = name + '.zip' if ext == '.upx' else basename + '.zip'\n        row = findKeyIv(csvPath, device_name)\n        if row is None:\n            print(f'No model named \"{device_name}\" found')\n            sys.exit()\n        decrypter = DeBooxUpx(row[2], row[3])\n        decrypter.deUpx(updateUpxPath, decryptedPath)\n        print(f\"Saved decrypted file to {decryptedPath}\")\n    else:\n        print('Usage:\\npython DeBooxUpdate.py <device name> [input file name [output file name]]')\n        print('For supported devices see BooxKeys.csv')\n"
  },
  {
    "path": "LICENSE.txt",
    "content": "        DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE \r\n                    Version 2, December 2004 \r\n\r\n Copyright (C) 2004 Sam Hocevar <sam@hocevar.net> \r\n\r\n Everyone is permitted to copy and distribute verbatim or modified \r\n copies of this license document, and changing it is allowed as long \r\n as the name is changed. \r\n\r\n            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE \r\n   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION \r\n\r\n  0. You just DO WHAT THE FUCK YOU WANT TO. \r\n"
  },
  {
    "path": "README.md",
    "content": "# decrypt Boox Update Upx\n\nThis project is aimed to help get the plain firmware packages of Boox devices, from the firmware packages named update.upx which are provided and encrypted by Onyx.\n\nIt has been tested only in [these Boox ereaders](./BooxKeys.csv). A few of models sold in China and most (if not all) of models sold in Russia are marked with suffixes `-cn`/`-ru`, **the firmwares for which don't use the same strings with their international versions!** SP\\_NoteS is SuperStar (chaoxing) verison.\n\nIf your device hasn't been included, you could try to [get its keys](https://github.com/Hagb/decryptBooxUpdateUpx/blob/master/CONTRIBUTING.md). If you found it available for any other Boox ereader, please [submit the keys](CONTRIBUTING.md).\n\nNote 1: There is also [another method to fetch decrypted `update.zip`](https://github.com/Hagb/decryptBooxUpdateUpx/issues/1) from [@shunf4](https://github.com/shunf4).\n\nNote 2: There is [a way to get downloading url of latest firmware](https://github.com/Hagb/decryptBooxUpdateUpx/issues/2#issuecomment-704006389).\n\nNote 3: `payload.bin` can be extracted with <https://github.com/cyxx/extract_android_ota_payload>.\n\nAny other issue and pull request is also welcomed!\n\n## How to run?\n\nPython3 should be installed to run the script, and `pycryptodome` a dependency of the script should also be installed:\n\n(BTW: in some environments, the following `pip` and `python` should be replaced with `pip3` and `python3`)\n\n```bash\npip install pycryptodome\n```\n\nThere are two components to this application: `DeBooxUpd.py` (the program) and `BooxKeys.csv` (the database of decryption keys).\nThese must be placed together in the same directory but the location does not matter.\nFor simplicity `update.upx` (the update to be decrypted) may be put in the same directory.\nBy default, `update.zip` (the decrypted update) will be generated in the current directory.\n\nTo run the application:\n```bash\npython DeBooxUpx.py <device model> [input file name [output file name]]\n```\n\n`<device model>` is required, and `[input file name [output file name]]` is optional. The input file will be `update.upx` and the output file will be saved in the current working directory, if not set in the arguments.\n\nFor a list of the currently supported models please refer to the file [BooxKeys.csv](BooxKeys.csv).\n\n## Keys\n\nPreviously the raw strings extracted from Onyx software were used.\nThese strings were 44 alphanumeric characters long.\nSince the newer Onyx models no longer use this level of obfuscation we've switched to using what the true gist of the keys are, a 32 character hexadecimal string.\nOlder Onyx models continue to be supported, although the key strings may appear to be unfamiliar.\n\n## API\n\nThere is a python class `DeBooxUpx` in [DeBooxUpx.py](DeBooxUpx.py) to decrypt `update.upx`.\n\nFollowing is a example of how to use this class to decrypt the `update.upx` using manual strings:\n\n``` python3\nfrom DeBooxUpx import DeBooxUpx\n\nKey = \"3DC53116D8AE3DCCEAD99F53E08E1E35\" \nIV = \"42B996AB6E252DCA4EDBC668BA3E5A3A\" \nupdateUpxPath = 'update.upx'\ndecryptedPath = 'update.zip'\n\ndecrypter = DeBooxUpx(Key, IV)\nprint('When updating, the device decrypt update package into', decrypter.path)\ndecrypter.deUpx(updateUpxPath, decryptedPath)\n```\n\n## Contributing\n\nRefer to [CONTRIBUTING.md](CONTRIBUTING.md).\n"
  },
  {
    "path": "algorithm-zh_cn.md",
    "content": "# Onyx Boox update.upx 更新包解密算法\n\n## 获取字符串 方式一：资源文件（仅在 Onyx Nova Pro 上测试过，以 Onyx Nova Pro 为例）\n\n先将设备开启 adb 调试。\n\n- MODEL 可将设备接上计算机后使用\n\n    ```\n    adb shell getprop ro.product.model\n    ```\n\n    来获取.（NovaPro 中得到的是`NovaPro`）\n\n- 以下三个字符串：\n\n    先从设备中`pull`出`/system/app/OnyxOtaService/OnyxOtaService.apk`并用`apktool`工具解包，然后在其中查找`id`分别为`0x7f050000`、`0x7f050001`、`0x7f050002`的`name`值，再根据这三个`name`值查找得到三条`string`值，我们分别将它们记作 S1, S2, S3。\n\n    以 NovaPro 为例，以上 `name` 值首先在`res/values/public.xml`中找到，分别为\n\n    - `\"settings\"`\n    - `\"upgrade\"`\n    - `\"local\"`\n\n    接着在`res/values/strings.xml`中可找到其对应的`string`值即 S1、S2、S3 的值，分别为\n\n    - `\"j857wYAQcWZgvIEQ/tcQqzxreUJgFHwJl6D2TN9BuSkQ\"`\n    - `\"+soGw/YVdGIRJiAs5SMmv1ihW37H1Fa9+/1w2Vgt14Ag\"`\n    - `\"lpsj9NJ8Kzv8jHb+OO8A5lxC+9Zhl243bFmDZYaF\"`\n\n- 在 Android 11 以上的设备上，这些字符串被移到了 `libota_jni.so`，在这种情况下，请查看安装该 APP [GetBooxUpxKeys](https://github.com/Hagb/GetBooxUpxKeysApp/releases) 来获得keys。或者参考 [22#issuecomment-964035840](https://github.com/Hagb/decryptBooxUpdateUpx/issues/22#issuecomment-964035840).\n\n\n## 获取字符串 方式二：libota_jni.so\n\n在 Android 11 以上的设备上（例如Poke4），Onyx Boox 将解密用的key移到了 shared library里。获取它的方法有很多，这里提供了一种模拟执行的方式来获取。\n\n先将设备开启 adb 调试。\n\n- MODEL 可将设备接上计算机后使用\n\n    ```\n    adb shell getprop ro.product.model\n    ```\n\n    来获取.（Poke4 中得到的是`Poke4`）\n\n- 获取 libota_jni.so\n\n    32位机使用命令\n\n    ```\n    adb pull /system/lib/libota_jni.so\n    ```\n\n    64位机使用命令\n\n    ```\n    adb pull /system/lib64/libota_jni.so\n    ```\n\n- 准备运行环境\n\n    将 submodule 克隆回来\n\n    ```\n    git submodule init\n    git submodule update\n    ```\n\n    安装python依赖\n\n    ```\n    pip install unicorn capstone\n    ```\n\n- 模拟执行 libota_jni.so\n\n    将拖回来的 libota_jni.so 作为脚本的入参。下方指令使用的是 Poke4 中拖出来的 libota_jni.so，未测试其他机型。\n\n    ```\n    python ota_jni.py test/libota_jni.so\n    ```\n  \n    输出内容示例，最后两行就是结果\n\n    ```\n    python ota_jni.py test/libota_jni.so\n    ERROR:androidemu.internal.modules:=> Undefined external symbol:\n    ERROR:androidemu.internal.modules:=> Undefined external symbol: __cxa_finalize\n    ERROR:androidemu.internal.modules:=> Undefined external symbol: __register_atfork\n    ERROR:androidemu.internal.modules:=> Undefined external symbol: __cxa_atexit\n    ro.kernel.qemu was not found in system_properties dictionary.\n    libc.debug.malloc was not found in system_properties dictionary.\n    WARNING:root:File does not exist '/proc/stat'\n    WARNING:androidemu.internal.modules:libcrypto.so needed by libota_jni.so do not exist in vfs ExAndroidNativeEmu/vfs\n    WARNING:androidemu.internal.modules:libutils.so needed by libota_jni.so do not exist in vfs ExAndroidNativeEmu/vfs\n    ----------------\n    STRING_SETTINGS uTKx3XVyRhlbI7hoHuy/NiYdwlWViPlc4EecZKYTThN/\n    STRING_UPGRADE vzDFqwIEMRfqTPUCV82ahjnz0hXfcZCIxRY8ljuLmTaf\n    ```\n\n## 解密算法\n\n1. 求两份 MODEL 相连（如 MODEL 值为`NovaPro`，则该值为`MovaProNovaPro`）的 md5，（二进制）截取前 8 字节，作为临时密钥 tmpK；\n2. Base64 解码 S1、S2、S3，分别得字符串 s1、s2、s3\n3. 用 DES 算法 CFB 模式\n\n    - 初始化向量（initialization vector）: 8 字节的 0xff \n    - 分段长度（segment size）: 64 bits\n    - 密钥：上述临时密钥 tmpK\n\n    来解密 s1，得密钥 K 的 hex，进一步可求出密钥 K；\n\n4. 用上述 3 的方法解密 s2 得初始化向量 IV 的 hex，从而可进一步得到初始化向量 IV；\n5. 再用同样的方法解密 s3 得路径 P（这步可不做）；\n6. 以 AES 算法 CFB 模式\n\n    - 初始化向量: 上述 IV\n    - 分段长度: 128 bits\n    - 密钥: 上述 K\n\n    解密 update.upx 更新包，即可得到能够被 Recovery 读取的明文 zip 更新包。\n"
  },
  {
    "path": "ota_jni.py",
    "content": "import sys\n\nsys.path.append('ExAndroidNativeEmu')\n\nfrom unicorn import UcError\nfrom unicorn.arm64_const import UC_ARM64_REG_PC\n\nfrom androidemu.const import emu_const\nfrom androidemu.emulator import Emulator\nfrom androidemu.internal import elf_reader\nfrom androidemu.java.classes.types import *\n\n\ndef main(so_path):\n    # Initialize emulator\n    reader = elf_reader.ELFReader(so_path)\n    if reader.is_elf32():\n        emulator = Emulator(\n            vfs_root=\"ExAndroidNativeEmu/vfs\",\n            arch=emu_const.ARCH_ARM32,\n            config_path=\"ExAndroidNativeEmu/emu_cfg/default.json\"\n        )\n    else:\n        emulator = Emulator(\n            vfs_root=\"ExAndroidNativeEmu/vfs\",\n            arch=emu_const.ARCH_ARM64,\n            config_path=\"ExAndroidNativeEmu/emu_cfg/default.json\"\n        )\n    try:\n        libtest = emulator.load_library(so_path)\n        print(\"----------------\")\n        r = emulator.call_symbol(libtest, 'Java_com_onyx_android_onyxotaservice_RsaUtil_nativeGetSecretKey',\n                                 emulator.java_vm.jni_env.address_ptr)\n        pystr = emulator.java_vm.jni_env.get_local_reference(r).value.get_py_string()\n        print(\"STRING_SETTINGS\", pystr)\n        r = emulator.call_symbol(libtest, 'Java_com_onyx_android_onyxotaservice_RsaUtil_nativeGetIvParameter',\n                                 emulator.java_vm.jni_env.address_ptr)\n        pystr = emulator.java_vm.jni_env.get_local_reference(r).value.get_py_string()\n        print(\"STRING_UPGRADE\", pystr)\n    except UcError as e:\n        print(\"Exit at 0x%08X\" % emulator.mu.reg_read(UC_ARM64_REG_PC))\n        emulator.memory.dump_maps(sys.stdout)\n        raise\n\n\nif __name__ == '__main__':\n    if len(sys.argv) < 2:\n        print(\"python %s /path/to/libota_jni.so\" % __file__)\n        exit(0)\n    main(sys.argv[1])\n"
  },
  {
    "path": "update_readme_strings.py",
    "content": "#!/usr/bin/env python3\nfrom DeBooxUpx import boox_strings\nimport re\nmodel_width = 10\n\nwith open('README.md') as readme:\n    readme_text = readme.read()\nmodels = sorted(boox_strings.items(), key=lambda x: x[0])\ntable_header = f\"\"\"|{'': ^{model_width}}|{'MODEL': ^{model_width+2}}|{'STRING_SETTINGS': ^46}|{'STRING_UPGRADE': ^46}|{'STRING_LOCAL': ^42}|\n|{'-'*(model_width)}|{'-'*(model_width+2)}|{'-'*46}|{'-'*46}|{'-'*42}|\n\"\"\"\ntable = table_header + '\\n'.join(\n    f\"|{name: ^{model_width}}|{'`'+string['MODEL']+'`': ^{model_width+2}}|`{string['STRING_SETTINGS']}`|`{string['STRING_UPGRADE']}`|`{string.get('STRING_LOCAL') or ' '*40}`|\"\n    for name, string in models)\nwith open('README.md', 'w') as readme:\n    readme.write(\n        re.sub(\n            r'<!--\\(strings table begin\\)-->.*<!--\\(strings table end\\)-->',\n            f'<!--(strings table begin)-->\\n{table}\\n<!--(strings table end)-->',\n            readme_text,\n            1,\n            flags=re.S))\n"
  }
]