More protocol details, add property parser

Example output for parse-props.py:

    download cable             = 'USER'
    battery level              = 100
    download type              = ' '
    download speed             = 0
    usb version                = ' '
    hardware revision          = 'rev_10'
    download sw version        = ' '
    device sw version          = 'D85520t'
    secure device              = 'S'
    laf sw version             = '1.1'
    device factory version     = 'LGD855AT-00-V20t-EUR-XX-AUG-18-2015-16G+0'
    device factory out version = 'LGD855AT-00-V20t-GLOBAL-COM-AUG-18-2015-16G+0'
    pid                        = 'YE01S151002002500'
    imei                       = (redacted)
    model name                 = 'LG-D855'
    device build type          = 'U'
    chipset platform           = 'msm8974'
    target_operator            = 'GLOBAL'
    target_country             = 'COM'
    ap_factory_reset_status    = 3
    cp_factory_reset_status    = 0
    isDownloadNotFinish        = 0
    qem                        = 0
    cupss swfv                 = 'A1439892730-M1439892730-C1439940554-U1439940554-0'
    is one binary dual plan    = 0
    memory size                = 30777344
    memory_id                  = 'SDW16G\n'
    bootloader_ver             = 'MiniOS 3.0'
This commit is contained in:
Peter Wu 2015-12-25 15:08:26 +01:00
parent eed04da13f
commit 8f35f2f5b5
2 changed files with 154 additions and 18 deletions

View file

@ -34,12 +34,26 @@ error code and the original request header as body.
## Commands
### OPEN - Open
Arguments: none
Request body: at most 276 (0x114) bytes (?)
### OPEN - Open File
Opens a file path.
### CLSE - Close
Arguments: none
Arguments:
- arg1 (response): DWORD file descriptor.
Request body: NUL-terminated file path that should be opened for reading or an
empty string to open `/dev/block/mmcblk0` in read/write mode.
(at most 276 (0x114) bytes?)
Non-existing files result in FAIL with error code 0x80000001.
### CLSE - Close File
Closes a file descriptor which was returned by the `OPEN` command.
Arguments:
- arg1: DWORD file descriptor (same in request and response).
Note: this allows you to close any file descriptor that are in use by the `lafd`
process, not just the one returned by `OPEN`. You can discover the current file
descriptors via `ls -l /proc/$pid/fd` where `$pid` is found by `ps | grep lafd`.
### HELO - Hello
Arguments:
@ -53,19 +67,28 @@ Arguments:
Note: `CTRL(RSET)` with no body is sent by the `Send_Command.exe` utility for
the `LEAVE` command.
### WRTE - Write
Arguments:
- arg1: ?
- arg2: ?
### WRTE - Write File
Writes to a file descriptor.
### READ - Read
Arguments:
- arg1: ?
- arg2: ?
- arg3: ?
Response body: present.
- arg1: file descriptor (must be open for writing!)
- arg2: offset in **blocks** (multiple of 512 bytes).
Request body: the data to be written. Can be of any size (including 1 or 513).
(Arguments probably encode read offset, length)
Note: writing to a file descriptor which was opened for reading results in FAIL
with code 0x82000002. This command is likely used for writing to partitions.
### READ - Read File
Reads from a file descriptor.
Arguments:
- arg1: file descriptor
- arg2: offset in **blocks** (multiple of 512 bytes).
- arg3: requested length in bytes.
Response body: data in file at given offset and requested length.
Note: be sure not to read past the end of the file (512 * offset + length), this
will hang the communication, requiring a reset (pull out battery)!
### ERSE - Erase
Arguments:
@ -83,11 +106,14 @@ see standard error, use variables and globbing, use a command such as:
sh -c "$@" -- eval 2>&1 echo $PATH
### INFO - Get Info
### INFO
Arguments:
- arg1: action (`GPRO` - Get Properties, `SPRO` - Set Properties)
Request body: fixed size 2824 (0xb08)
Response body: present for `GPRO`
Request body: must begin with two bytes (`08 0b`).
Response body: 2824 (0xb08) bytes of binary info.
See [scripts/parse-props.py](scripts/parse-props.py) for the structure of the
property body.
### UNLK - Unlock
Arguments: none

110
scripts/parse-props.py Executable file
View file

@ -0,0 +1,110 @@
#!/usr/bin/env python
# Parse property file.
#
# Usage:
#
# lglaf.py -c '!INFO GPRO \x08\x0b' > props.bin
# scripts/parse-props.py props.bin
import argparse, sys, struct
def stringify(resp):
if not isinstance(resp, str):
try: resp = resp.decode('ascii')
except: pass
return resp
def get_str(data, shadow, offset):
resp = b''
#while True:
while offset < len(data):
b = data[offset:offset+1]
shadow[offset] = 's'
if b == b'\0':
break
resp += b
offset += 1
return stringify(resp)
def get_chr(data, shadow, offset):
b = data[offset:offset+1]
shadow[offset] = 'c'
return stringify(b)
def get_int(data, shadow, offset):
d = struct.unpack_from('<I', data, offset)[0]
for off in range(offset, offset+4):
shadow[off] = 'd'
return d
# Description of the contents
keys = [
(0x3f9, get_str, "download cable"),
(0x42b, get_int, "battery level"),
(0x010, get_chr, "download type"),
(0x021, get_int, "download speed"),
(0x403, get_str, "usb version"),
(0x417, get_str, "hardware revision"),
(0x029, get_str, "download sw version"),
(0x14f, get_str, "device sw version"),
(0x42f, get_chr, "secure device"),
(0x4e8, get_str, "laf sw version"),
(0x24f, get_str, "device factory version"),
(0x528, get_str, "device factory out version"),
(0x3db, get_str, "pid"),
(0x3c7, get_str, "imei"),
(0x131, get_str, "model name"),
(0x430, get_str, "device build type"),
(0x43a, get_str, "chipset platform"),
(0x44e, get_str, "target_operator"),
(0x462, get_str, "target_country"),
(0x4fc, get_int, "ap_factory_reset_status"),
(0x500, get_int, "cp_factory_reset_status"),
(0x504, get_int, "isDownloadNotFinish"),
(0x508, get_int, "qem"),
(0x628, get_str, "cupss swfv"),
(0x728, get_int, "is one binary dual plan"),
(0x72c, get_int, "memory size"),
(0x730, get_str, "memory_id"),
(0x39f, get_str, "bootloader_ver"),
]
def debug_other(data, shadow):
for offset, shadow_type in enumerate(shadow):
data_byte = data[offset:offset+1]
if not shadow_type and data_byte != b'\0':
print("Unprocessed byte at 0x%03x: %r" % (offset, data_byte))
shadow[offset] = '*'
def print_shadow(shadow):
for offset in range(0, len(shadow), 32):
line1 = ''.join(c or '.' for c in shadow[offset:offset+16])
line2 = ''.join(c or '.' for c in shadow[offset+16:offset+32])
print("%03x: %-16s %-16s" % (offset, line1, line2))
def parse_data(data):
assert data[0:2] == b'\x08\x0b'
assert len(data) == 0xb08
# Set to non-None when processed
shadow = [None] * len(data)
for offset, getter, description in keys:
resp = getter(data, shadow, offset)
print("%-26s = %r" % (description, resp))
return data, shadow
parser = argparse.ArgumentParser()
parser.add_argument("--debug", action='store_true')
parser.add_argument("file", help="2824 byte properties dump file")
def main():
args = parser.parse_args()
data = open(args.file, "rb").read()
data, shadow = parse_data(data)
if args.debug:
debug_other(data, shadow)
print_shadow(shadow)
if __name__ == '__main__':
main()