├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── TitaniumBackupDecrypt.php └── hidden-input ├── LICENSE.txt ├── README.md ├── build └── hiddeninput.exe └── src ├── hiddeninput.c ├── hiddeninput.cpp └── version.rc /.gitignore: -------------------------------------------------------------------------------- 1 | release -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:latest 2 | 3 | # Really my email ends with zighinetto "organization", but for antispam reasons I am masking 4 | # And no offense for the real Greeks, there is a long story why it is pronunciated ECHELON 5 | LABEL maintainer="/usr/local/ΕΨΗΕΛΩΝ " 6 | 7 | RUN pear channel-discover phpseclib.sourceforge.net && \ 8 | pear install phpseclib/Crypt_AES phpseclib/Crypt_RSA 9 | 10 | WORKDIR /usr/local/php 11 | COPY TitaniumBackupDecrypt.php /usr/local/php 12 | 13 | ENTRYPOINT ["php", "/usr/local/php/TitaniumBackupDecrypt.php"] -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Brian T. Hafer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | TitaniumBackupDecrypt 2 | ===================== 3 | 4 | Decrypts encrypted archive files from Titanium Backup for Android. 5 | 6 | If you use this program, please let me know via a message on Github. Thanks!
7 | https://github.com/bhafer/TitaniumBackupDecrypt 8 | 9 | ===================== 10 | 11 | Dependencies:
12 |      http://phpseclib.sourceforge.net/ :: Install via PEAR with:
13 |          pear channel-discover phpseclib.sourceforge.net
14 |          pear install phpseclib/Crypt_AES phpseclib/Crypt_RSA 15 | 16 | Usage:
17 |      php TitaniumBackupDecrypt <archive-file>
18 |           Will check for TB_ARCHIVE_PASSWORD environment variable or else prompt for password.
19 |             --OR--
20 |      php TitaniumBackupDecrypt <archive-file> <password>
21 | Where archive-file is a file of one of the following types:
22 |      .tar.bz2
23 |      .tar.gz
24 |      .tar.lzop
25 |      .tar
26 |      .xml.bz2
27 |      .xml.gz
28 |      .xml.lzop
29 |      .xml 30 | 31 | Based on the file format specification information from Titanium Backup at:
32 |      https://plus.google.com/+ChristianEgger/posts/MQBmYhKDex5 33 | 34 | ===================== 35 | 36 | Snapshot of https://plus.google.com/+ChristianEgger/posts/MQBmYhKDex5 :: 37 | 38 | So, the file format for encrypted data backups is as follows: 39 | 40 | "TB_ARMOR_V1" '\n' passphraseHmacKey '\n' passphraseHmacResult '\n' publicKey '\n' encryptedPrivateKey '\n' encryptedSessionKey '\n' Data
41 | 42 | Each of the 5 "variables" (passphraseHmacKey, passphraseHmacResult, publicKey, encryptedPrivateKey, encryptedSessionKey) is stored in Base64 format without linewraps (of course) and can be decoded with: Base64.decode(passphraseHmacKey, Base64.NO_WRAP) 43 | 44 | Then the user-supplied passphrase (String) can be verified as follows:
45 | Mac mac = Mac.getInstance("HmacSHA1");
46 | mac.init(new SecretKeySpec(passphraseHmacKey, "HmacSHA1"));
47 | byte[] sigBytes = mac.doFinal(passphrase.getBytes("UTF-8"));
48 | boolean passphraseMatches = Arrays.equals(sigBytes, passphraseHmacResult); 49 | 50 | Then the passphrase is independently hashed with SHA-1. We append [twelve] 0x00 bytes to the 160-bit result to constitute the 256-bit AES key which is used to decrypt "encryptedPrivateKey" (with an IV of [sixteen] 0x00 bytes). [Decrypt using AES-256 in CBC mode and perform PKCS5 unpadding. Note that AES-256 is equivalent to Rijndael-128 using a 256 bit key.] 51 | 52 | Then we build the KeyPair object as follows:
53 | KeyFactory keyFactory = KeyFactory.getInstance("RSA");
54 | PrivateKey privateKey2 = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKey));
55 | PublicKey publicKey2 = keyFactory.generatePublic(new X509EncodedKeySpec(publicKey));
56 | KeyPair keyPair = new KeyPair(publicKey2, privateKey2); 57 | 58 | Then we decrypt the session key as follows:
59 | Cipher rsaDecrypt = Cipher.getInstance("RSA/NONE/PKCS1Padding"); 60 | rsaDecrypt.init(Cipher.DECRYPT_MODE, keyPair.getPrivate());
61 | ByteArrayOutputStream baos = new ByteArrayOutputStream();
62 | CipherOutputStream cos = new CipherOutputStream(baos, rsaDecrypt);
63 | cos.write(encryptedSessionKey);
64 | cos.close();
65 | byte[] sessionKey = baos.toByteArray(); 66 | 67 | And finally, we decrypt the data itself with the session key (which can be either a 128-bit, 192-bit or 256-bit key) and with a 0x00 IV. 68 | 69 | While the "zero" IV is suboptimal from a security standpoint, it allows files to be encoded faster - because every little bit counts, especially when we store backups with LZO compression. 70 | 71 | ===================== 72 | 73 | Docker usage: (until a full image is built) 74 | 75 | First, build your own image with Docker 76 | 77 | docker build -t TitaniumBackupDecrypt . 78 | 79 | Then, run as a standalone container. 80 | 81 | You MUST mount the directory that holds TitaniumBackup files into the container. 82 | 83 | Example for Windows hosts 84 | 85 | docker run --rm -ti -v C:\Users\Example\TitaniumBackup:/app /app/encrypted-backup.tar.gz 86 | 87 | Example for Linux hosts 88 | 89 | docker run --rm -ti -v /home/example/TitaniumBackup:/app /app/encrypted-backup.tar.gz -------------------------------------------------------------------------------- /TitaniumBackupDecrypt.php: -------------------------------------------------------------------------------- 1 | 15 | // Where archive-file is a file of one of the following types: 16 | // .tar.bz2 17 | // .tar.gz 18 | // .tar.lzop 19 | // .tar 20 | // .xml.bz2 21 | // .xml.gz 22 | // .xml.lzop 23 | // .xml 24 | // 25 | // Based on file format specification information from Titanium Backup at: 26 | // https://plus.google.com/+ChristianEgger/posts/MQBmYhKDex5 27 | // 28 | // --------------------------- 29 | // 30 | // The MIT License (MIT) 31 | // 32 | // Copyright (c) 2014 Brian T. Hafer 33 | // 34 | // Permission is hereby granted, free of charge, to any person obtaining a copy 35 | // of this software and associated documentation files (the "Software"), to deal 36 | // in the Software without restriction, including without limitation the rights 37 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 38 | // copies of the Software, and to permit persons to whom the Software is 39 | // furnished to do so, subject to the following conditions: 40 | // 41 | // The above copyright notice and this permission notice shall be included in 42 | // all copies or substantial portions of the Software. 43 | // 44 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 45 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 46 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 47 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 48 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 49 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 50 | // THE SOFTWARE. 51 | // 52 | // --------------------------- 53 | 54 | require_once('Crypt/AES.php'); 55 | require_once('Crypt/RSA.php'); 56 | 57 | define('TB_BUFFER_SIZE', 128 * 2048); 58 | 59 | function pkcs5_unpad($text) { 60 | $pad = ord($text{strlen($text)-1}); 61 | if ($pad > strlen($text)) return false; 62 | if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) return false; 63 | return substr($text, 0, -1 * $pad); 64 | } 65 | 66 | function prompt($message) { 67 | if (PHP_SAPI !== 'cli') { 68 | return false; 69 | } 70 | echo $message; 71 | //$ret = stream_get_line(STDIN, 1024, PHP_EOL); // Echoes the password to the screen 72 | if ((PHP_OS === 'WINNT') || (PHP_OS === 'WIN32')) { 73 | $ret = exec(__DIR__ . '\hidden-input\build\hiddeninput.exe'); 74 | echo PHP_EOL; 75 | } 76 | else { 77 | system('stty -echo'); 78 | $ret = trim(fgets(STDIN)); 79 | system('stty echo'); 80 | echo PHP_EOL; 81 | } 82 | return $ret; 83 | } 84 | 85 | if ((count($argv) != 2) && (count($argv) != 3)) { 86 | echo "No archive file specified.\nUsage:\n php TitaniumBackupDecrypt \n Will check for TB_ARCHIVE_PASSWORD environment variable or else prompt for password.\n --OR--\n php TitaniumBackupDecrypt \n"; 87 | exit(1); 88 | } 89 | 90 | $filenameIn = $argv[1]; 91 | $fileIn = fopen($filenameIn, 'rb'); 92 | if ($fileIn === false) { 93 | echo "File not found: $filenameIn\n"; 94 | exit(1); 95 | } 96 | 97 | // Determine input file type. 98 | $filename = basename($argv[1]); 99 | if (substr($filename, -strlen('.tar.gz')) == '.tar.gz') { 100 | $filenameOut = dirname($argv[1]) . DIRECTORY_SEPARATOR . basename($argv[1], '.tar.gz') . '-decrypted.tar.gz'; 101 | } else if (substr($filename, -strlen('.tar.bz2')) == '.tar.bz2') { 102 | $filenameOut = dirname($argv[1]) . DIRECTORY_SEPARATOR . basename($argv[1], '.tar.bz2') . '-decrypted.tar.bz2'; 103 | } else if (substr($filename, -strlen('.tar.lzop')) == '.tar.lzop') { 104 | $filenameOut = dirname($argv[1]) . DIRECTORY_SEPARATOR . basename($argv[1], '.tar.lzop') . '-decrypted.tar.lzop'; 105 | } else if (substr($filename, -strlen('.tar')) == '.tar') { 106 | $filenameOut = dirname($argv[1]) . DIRECTORY_SEPARATOR . basename($argv[1], '.tar') . '-decrypted.tar'; 107 | } else if (substr($filename, -strlen('.xml.gz')) == '.xml.gz') { 108 | $filenameOut = dirname($argv[1]) . DIRECTORY_SEPARATOR . basename($argv[1], '.xml.gz') . '-decrypted.xml.gz'; 109 | } else if (substr($filename, -strlen('.xml.bz2')) == '.xml.bz2') { 110 | $filenameOut = dirname($argv[1]) . DIRECTORY_SEPARATOR . basename($argv[1], '.xml.bz2') . '-decrypted.xml.bz2'; 111 | } else if (substr($filename, -strlen('.xml.lzop')) == '.xml.lzop') { 112 | $filenameOut = dirname($argv[1]) . DIRECTORY_SEPARATOR . basename($argv[1], '.xml.lzop') . '-decrypted.xml.lzop'; 113 | } else if (substr($filename, -strlen('.xml')) == '.xml') { 114 | $filenameOut = dirname($argv[1]) . DIRECTORY_SEPARATOR . basename($argv[1], '.xml') . '-decrypted.xml'; 115 | } else { 116 | echo "Unknown archive file type.\n"; 117 | exit(1); 118 | } 119 | 120 | $header = fgets($fileIn, 64); 121 | $header = rtrim($header, "\n"); 122 | echo "File type: $header \n"; 123 | if ($header != "TB_ARMOR_V1") { 124 | echo "Unsupported file format. Expected: \"TB_ARMOR_V1\".\n"; 125 | exit(1); 126 | } 127 | 128 | $passphraseHmacKey = fgets($fileIn, 1024); 129 | $passphraseHmacKey = rtrim($passphraseHmacKey, "\n"); 130 | //echo "Passphrase HMAC Key: $passphraseHmacKey \n"; 131 | $passphraseHmacKey = base64_decode($passphraseHmacKey, true); 132 | if ($passphraseHmacKey === false) { 133 | echo "Invalid Passphrase HMAC Key.\n"; 134 | exit(1); 135 | } 136 | 137 | $passphraseHmacResult = fgets($fileIn, 1024); 138 | $passphraseHmacResult = rtrim($passphraseHmacResult, "\n"); 139 | //echo "Passphrase HMAC Result: $passphraseHmacResult \n"; 140 | $passphraseHmacResult = base64_decode($passphraseHmacResult, true); 141 | if ($passphraseHmacResult === false) { 142 | echo "Invalid Passphrase HMAC Result.\n"; 143 | exit(1); 144 | } 145 | 146 | $publicKey = fgets($fileIn, 1024); 147 | $publicKey = rtrim($publicKey, "\n"); 148 | //echo "Public Key: $publicKey \n"; 149 | $publicKey = base64_decode($publicKey, true); 150 | if ($publicKey === false) { 151 | echo "Invalid Public Key.\n"; 152 | exit(1); 153 | } 154 | 155 | $encryptedPrivateKey = fgets($fileIn, 4096); 156 | $encryptedPrivateKey = rtrim($encryptedPrivateKey, "\n"); 157 | //echo "Encrypted Private Key: $encryptedPrivateKey \n"; 158 | $encryptedPrivateKey = base64_decode($encryptedPrivateKey, true); 159 | if ($encryptedPrivateKey === false) { 160 | echo "Invalid Encrypted Private Key.\n"; 161 | exit(1); 162 | } 163 | 164 | $encryptedSessionKey = fgets($fileIn, 1024); 165 | $encryptedSessionKey = rtrim($encryptedSessionKey, "\n"); 166 | //echo "Encrypted Session Key: $encryptedSessionKey \n"; 167 | $encryptedSessionKey = base64_decode($encryptedSessionKey, true); 168 | if ($encryptedSessionKey === false) { 169 | echo "Invalid Encrypted Session Key.\n"; 170 | exit(1); 171 | } 172 | 173 | if (count($argv) == 3) { 174 | $passphrase = $argv[2]; 175 | echo "Using encryption password from arguments.\n"; 176 | } else if (getenv('TB_ARCHIVE_PASSWORD') !== FALSE) { 177 | $passphrase = getenv('TB_ARCHIVE_PASSWORD'); 178 | echo "Using encryption password from environment (TB_ARCHIVE_PASSWORD).\n"; 179 | } else { 180 | $passphrase = prompt("Enter encryption passphrase: ", true); 181 | } 182 | 183 | if ($passphraseHmacResult != hash_hmac('sha1', $passphrase, $passphraseHmacKey, true)) { 184 | echo "Supplied passphrase not valid for encrypted file.\n"; 185 | exit(1); 186 | } 187 | 188 | $aesKey = sha1($passphrase, true) . "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"; 189 | if (strlen($aesKey) * 8 != 256) { 190 | echo "Error generating AES key from supplied passphrase.\n"; 191 | exit(1); 192 | } 193 | 194 | $aesCipher = new Crypt_AES(CRYPT_AES_MODE_CBC); 195 | $aesCipher->setKey($aesKey); 196 | $aesCipher->setIV("\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); 197 | $decryptedPrivateKey = $aesCipher->decrypt($encryptedPrivateKey); 198 | // Other ways to do this: 199 | // $decryptedPrivateKey = openssl_decrypt($encryptedPrivateKey, 'aes-256-cbc', $aesKey, true, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); 200 | // $decryptedPrivateKey = pkcs5_unpad(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $aesKey, $encryptedPrivateKey, MCRYPT_MODE_CBC, "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00")); 201 | //echo "Decrypted Private Key: " . base64_encode($decryptedPrivateKey) . " \n"; 202 | 203 | $rsaCipher = new Crypt_RSA(); 204 | $rsaCipher->setEncryptionMode(CRYPT_RSA_ENCRYPTION_PKCS1); 205 | $rsaCipher->loadKey($decryptedPrivateKey); 206 | $decryptedSessionKey = $rsaCipher->decrypt($encryptedSessionKey); 207 | //echo "Decrypted Session Key: " . base64_encode($decryptedSessionKey) . " \n"; 208 | echo "Session Key Length: " . strlen($decryptedSessionKey) * 8 . " \n"; 209 | 210 | // Create output file. 211 | $fileOut = fopen($filenameOut, 'wb'); 212 | echo "Writing file: $filenameOut\n"; 213 | 214 | // Setup cipher for continuous buffering and copy between files. 215 | $aesCipher->setKey($decryptedSessionKey); 216 | $aesCipher->enableContinuousBuffer(); 217 | $aesCipher->disablePadding(); 218 | while (!feof($fileIn)) { 219 | fwrite($fileOut, $aesCipher->decrypt(fread($fileIn, TB_BUFFER_SIZE))); 220 | } 221 | 222 | fclose($fileIn); 223 | fclose($fileOut); 224 | 225 | echo "Done.\n"; 226 | 227 | ?> 228 | -------------------------------------------------------------------------------- /hidden-input/LICENSE.txt: -------------------------------------------------------------------------------- 1 | https://github.com/Seldaek/hidden-input 2 | 3 | License 4 | 5 | This is in the public domain as far as I am concerned. Should anyone involved in writing the original bits feel particularly sad or offended by my publishing of the sources or binary, drop me a line. -------------------------------------------------------------------------------- /hidden-input/README.md: -------------------------------------------------------------------------------- 1 | Hidden Input 2 | ------------ 3 | 4 | Executing this program allows to prompt users in interactive 5 | CLI apps for passwords without it being printed in the terminal. 6 | 7 | This should be easier, but it's unfortunately not possible to 8 | achieve with most higher level languages that do not offer 9 | an abstraction for it. Therefore instead of relying on popular 10 | cheap tricks using VBScript to fire up a prompt (which was never 11 | even working well since the password is shown in clear while 12 | the user types it in) I decided to build this from C++ sources 13 | I found on [stackoverflow](http://stackoverflow.com/questions/6899025/hide-user-input-on-password-prompt). 14 | Credits go to [guestgulkan](http://www.cplusplus.com/user/E6M4jE8b/) 15 | from cplusplus.com, whoever (s)he may be. 16 | 17 | Usage 18 | ----- 19 | 20 | Get the build/hiddeninput.exe file into your project and execute it 21 | to accept user input. Whatever the user types until a newline will 22 | be output back to the caller but will not show up in the terminal. 23 | 24 | C++ 25 | --- 26 | 27 | hiddeninput.cpp is the original sources modified so that it compiles 28 | with VC2008 which allows the executable to run fine from Win XP onwards. 29 | 30 | C 31 | - 32 | 33 | hiddeninput.c is an alternative version I worked on using the ancestral 34 | [conio](http://en.wikipedia.org/wiki/Conio.h) utilities. It is not 35 | completely functionally equivalent to the C++ version so I chose not to 36 | include the binary in the repo. Should you really have adverse reactions 37 | to running compiled C++ code, you can easily build this one yourself instead. 38 | 39 | License 40 | ------- 41 | 42 | This is in the public domain as far as I am concerned. Should anyone involved 43 | in writing the original bits feel particularly sad or offended by my publishing 44 | of the sources or binary, drop me a line. -------------------------------------------------------------------------------- /hidden-input/build/hiddeninput.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bhafer/TitaniumBackupDecrypt/313d085429c0eb66929eeae77d250dae59e0575c/hidden-input/build/hiddeninput.exe -------------------------------------------------------------------------------- /hidden-input/src/hiddeninput.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include "stdlib.h" 3 | #include "conio.h" 4 | 5 | int main(void) { 6 | int input; 7 | 8 | char text[255] = ""; 9 | int ptr = 0; 10 | 11 | while (++ptr < 255) { 12 | input = _getch(); 13 | if (input == 13 || input == 10) { 14 | break; 15 | } 16 | sprintf(text, "%s%c", text, input); 17 | } 18 | 19 | printf("%s", text); 20 | return 0; 21 | } -------------------------------------------------------------------------------- /hidden-input/src/hiddeninput.cpp: -------------------------------------------------------------------------------- 1 | #define _WIN32_WINNT 0x0500 2 | #define WINVER 0x0500 3 | #define WIN32_LEAN_AND_MEAN 4 | #define NTDDI_VERSION 0x05000000 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | 12 | int main() 13 | { 14 | HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE); 15 | DWORD mode = 0; 16 | GetConsoleMode(hStdin, &mode); 17 | SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT)); 18 | 19 | string s; 20 | getline(cin, s); 21 | 22 | cout << s << endl; 23 | return 0; 24 | } -------------------------------------------------------------------------------- /hidden-input/src/version.rc: -------------------------------------------------------------------------------- 1 | 1 VERSIONINFO 2 | FILEVERSION 1,0,0,0 3 | PRODUCTVERSION 1,0,0,0 4 | FILEFLAGSMASK 0x17L 5 | #ifdef _DEBUG 6 | FILEFLAGS 0x1L 7 | #else 8 | FILEFLAGS 0x0L 9 | #endif 10 | FILEOS 0x4L 11 | FILETYPE 0x1L 12 | FILESUBTYPE 0x0L 13 | BEGIN 14 | BLOCK "StringFileInfo" 15 | BEGIN 16 | BLOCK "040904b0" 17 | BEGIN 18 | VALUE "FileDescription", "Reads from stdin without leaking info to the terminal and outputs back to stdout" 19 | VALUE "FileVersion", "1, 0, 0, 0" 20 | VALUE "InternalName", "hiddeninput" 21 | VALUE "LegalCopyright", "Jordi Boggiano - 2012" 22 | VALUE "OriginalFilename", "hiddeninput.exe" 23 | VALUE "ProductName", "Hidden Input" 24 | VALUE "ProductVersion", "1, 0, 0, 0" 25 | END 26 | END 27 | BLOCK "VarFileInfo" 28 | BEGIN 29 | VALUE "Translation", 0x409, 1200 30 | END 31 | END --------------------------------------------------------------------------------