Lekensteyn / lglaf

LG Download Mode utility and documentation
https://lekensteyn.nl/lglaf/
MIT License
137 stars 74 forks source link

LG Q6: additionnal limitations from Lg ? #24

Open aenglebert opened 6 years ago

aenglebert commented 6 years ago

Hello everybody, It try to use lglaf on my lg q6 but I face a few problems:

  1. Needs to unlock, solved by PR #19
  2. Cannot use commands with lglaf, because sh in not available on the q6. Only a few commands can be used directly: dmesg, unmount, fota, gota, ps, ls, mkdir, getenforce, grep (I found the list by extracting and opening the lafd binary with an hex editor, that seems to correlate with what I see in practice). I made this to be at least able to use the few available commands https://github.com/LG-Q6/lglaf/commit/ee36e156e3322e9173882c2817e587cef3f3d90f .
  3. Related to the previous problem, we cannot obtain the informations about the partitions needed by partition.py (starting block, size) because the cat command is not available. As a workaround I extracted theses informations with adb and changed partition.py to get the informations from files https://github.com/LG-Q6/lglaf/commit/31c1afd0da36633deea09d2681d52eefeaa384c8. I now be able to dump the partitions with "partition.py --dump file.img partition_name", the file image is correctly writen to the disk and is the same that the file from the kdz, even if I get this message:
    ./partitions.py --debug --dump test.img recovery
    2017-11-01 10:02:58,350 LGLAF.py: DEBUG: Using endpoints 83 (IN), 02 (OUT)
    mmcblk0p49
    /sys/class/block/mmcblk0/mmcblk0p49
    2017-11-01 10:02:58,351 partitions: DEBUG: Partition recovery (mmcblk0p49) at offset 310378496 (0x12800000) size 33554432 (0x2000000)
    2017-11-01 10:02:58,755 partitions: DEBUG: Opened fd 42 for disk
    2017-11-01 10:02:58,755 partitions: DEBUG: Will read 33554432 bytes at disk offset 310378496
    2017-11-01 10:03:01,381 partitions: INFO: Wrote 33554432 bytes to test.img
    Traceback (most recent call last):
    File "./partitions.py", line 288, in <module>
    main()
    File "./partitions.py", line 284, in main
    wipe_partition(comm, disk_fd, part_offset, part_size)
    File "/usr/lib64/python2.7/contextlib.py", line 24, in __exit__
    self.gen.next()
    File "./partitions.py", line 86, in laf_open_disk
    comm.call(close_cmd)
    File "/home/alex/lglaf/lglaf.py", line 236, in call
    raise RuntimeError('Command failed with error code %#x (%s)' % (errCode, msg))
    RuntimeError: Command failed with error code 0x8000010a (LAF_ERROR_ACCESS_DENIED)

    But I cannot write to the partitions, for exemple:

    ./partitions.py --debug --restore recovery.img recovery 
    2017-11-01 10:06:03,126 LGLAF.py: DEBUG: Using endpoints 83 (IN), 02 (OUT)
    mmcblk0p49
    /sys/class/block/mmcblk0/mmcblk0p49
    2017-11-01 10:06:03,127 partitions: DEBUG: Partition recovery (mmcblk0p49) at offset 310378496 (0x12800000) size 33554432 (0x2000000)
    2017-11-01 10:06:03,540 partitions: DEBUG: Opened fd 43 for disk
    2017-11-01 10:06:36,562 partitions: INFO: Done after writing 22439936 bytes from recovery.img
    Traceback (most recent call last):
    File "./partitions.py", line 288, in <module>
    main()
    File "./partitions.py", line 284, in main
    wipe_partition(comm, disk_fd, part_offset, part_size)
    File "/usr/lib64/python2.7/contextlib.py", line 24, in __exit__
    self.gen.next()
    File "./partitions.py", line 86, in laf_open_disk
    comm.call(close_cmd)
    File "/home/alex/lglaf/lglaf.py", line 236, in call
    raise RuntimeError('Command failed with error code %#x (%s)' % (errCode, msg))
    RuntimeError: Command failed with error code 0x8000010a (LAF_ERROR_ACCESS_DENIED)

    And if I dump the partition again it's unchanged and nothing changed at reboot. The error message is similare to the message I receive when I try to use a command before using auth.py, but it doesn't change anything for this, only for sending commands.

Does someone have similar problem on an other device ? Do you have any solution or any clue to find how to solve that?

Thank you!

anarcat commented 6 years ago

i have the same issue, documented in #31. i gave up.

joeblowma commented 6 years ago

See pull #28 switch --rawshell, OP confirmed it helped with the issue.

aenglebert commented 6 years ago

Yes, issue 2 is solved by the pull #28 . There is only a limited numbre of commands like I already said but we can at least use them easier. The only remaining issue is the second part of issue 3. By using pull #27 we can now obtain the parittions informations from GPT (we cannot obtain them from cat since the command is not available), but we still cannot write to the partitions (I still receive a "Command failed with error code 0x8000010a (LAF_ERROR_ACCESS_DENIED)" response).

tuxuser commented 6 years ago

I think that I fixed your access denied issue: LGLAF Fork / Proper CR Usage

Please try and report back.

PS: You dont have to provide --cr switch, it's done automatically. Oh.. and DON'T use --skip-hello

aenglebert commented 6 years ago

It solves the error but not everything works. I used list and dump without a problem. restore take a minute or two and complete without an error, but the partition is still unchanged (md5sum of a dump before and after restore a different partition them dump again don't change). wipe seems to work, md5sum changed when dumping again but don't remain after reboot. I tried every command on my recovery partition and rebooting to recovery is still the same (stock recovery) after wipe alone and wipe + restore a test twrp build. Thanks a lot for your efforts !

steadfasterX commented 6 years ago

absolutely the same for the LG G4 when running on MM (v20p). No issues at all when running on LP. on MM i need the KILO ... (which works so far) and on LP I don't.

I can TRIM e.g. abootbak partition without an issue and I can even write without generating an error but the partition is left untouched! In my case I'm able to use the "dd" command which CAN touch the partition. I tried to use dd to TRIM the misc partition but this doesn't worked ?! EDIT: maybe thats the mysterious MISCWRTE command?? I used dd to zero out the misc partition: worked fine..

As I believe that the issue in general is the same for both our devices I decided to participate here instead of a new issue.

To get deeper I flashed with LGup and while doing so I took an usbdump which I wanna share here: -removed as it was useless- (checkout my own issue here: #35 with the corrected dumps)

tuxuser commented 6 years ago

Thx a lot @steadfasterX, that will help for sure :D

runningnak3d commented 6 years ago

Just to add to this...

I have an LG V20 that uses LAF protocol 1.0.0.4. The V20 uses UFS NAND (/dev/block/sda to /dev/block/sdg).

If I send an OPEN with no body / payload then lafd opens /dev/block/sda and I can READ and ERSE. recovery is on /dev/block/sde. If I send an OPEN with /dev/block/sde in the payload, it opens the device for READ (ERSE fails with an invalid parameter).

I patched LG UP so that I could do individual partition downloads.This is the complete packet capture of me flashing laf (on /dev/block/sda) and recovery (on /dev/block/sde) https://drive.google.com/open?id=1Xfa26ZpauKHIKofOv9_VwVuOoL_CZpeX

If you notice, there is a huge payload for each OPEN. I grabbed the payload as-is before the WRTE of recovery. Once I issue an OPEN with that payload, then I can now use ERSE -- so I thought GREAT that opened the device in write mode. Nope.

No matter what, whether I pass the complete payload as sniffed in LG UP, or just the block device (that is the first 16 bytes of the payload -- 14 are actually used), WRTE acts like it is writing, but doesn't actually write. If I look at dmesg after a write, there is an error: fsync failed.

I am guessing that either:

1 - The payload in an OPEN not only determines if the device will be in write mode, but what (some hash?) can be written. or 2 - lafd writes to some temp area, and then fllushes the blocks to the device after some check.

I have tried using OPCM CHEK and CHCK CLER. I have not tried to decode what the payload in the OPEN does, and I have not tried to decode what the payload in a SIGN does.

I have been fighting with this on my own for quite a while now, and didn't realize that there are others out there trying to crack this. It would be great if we could all put our heads together now, because IMHO with the TrustZone is moved on die with the release of the SD845 -- this will all be useless. But then again, at anytime LG could just change the entire LAF protocol, and everything that has been learned will be useless.

-- Brian

runningnak3d commented 6 years ago

Looking at the SIGN opcode, it is a list of the partitions, and there is a 32 byte random number at the end of each partition entry. I will bet a 6 pack of beer (Budweiser -- I am poor :P ) that it is the SHA256 of the partition. Unfortunately, I think it is the SHA256 of the compressed partition. Again, just a guess, but I think lafd writes to a temp area, and then once the entire partition has been sent, it compares the hash, and then moves the blocks to the partition if the hash matches.

What is messed up is that lafd has errors for everything. Why isn't it throwing an error when the hash doesn't match since I am not sending the SIGN opcode.

-- Brian

runningnak3d commented 6 years ago

Sorry for the post flood, but I meant to tell @steadfasterX that the MISC WRTE opcode is for writing to the misc partition. When you get an OTA, misc gets updated so that when the phone boots, it boots to recovery. That is why people that are rooted and take an OTA get stuck in a boot loop until they wipe misc -- which they don't need to do, they just need to wipe the first 56 bytes (or 64 I can't remember). LG UP uses MISC WRTE to do exactly that in case you had an OTA that had been taken but you had booted straight to download mode. If it didn't clear that, then your phone would boot loop and there would be no fixing it since misc otherwise wouldn't be touched.

runningnak3d commented 6 years ago

So I figured I would feed the SIGN opcode in as is, and see what happens.

This is interesting from dmesg:

[LAF] sign version = 0x1000 
[LAF] sign count = 558 
[LAF] signature length = 256
[LAF] decrypted length =32

-- Brian

steadfasterX commented 6 years ago

@runningnak3d yes I assumed the same for MISC WRTE but I was not able to test it.

@Lekensteyn we could create an IRC chan for lglaf no ? ;)

EDIT: #lglaf created .. so whoever wants to join..

Well I encountered one more thing for the G4:

When I write on LP i can do so without an issue even for 4 GB files. When trying the same on MM it crashes with an accessed denied error after ca 268 MB ! When trying the same on N it crashes with an accessed denied error after ca 786 MB !

My idea was that the file descriptor may get kind of "overfilled" so my guess was to close the fd and re-open it then writing further on. This works so far but as said it just looks like it is writing.

so your idea about a temp dir could be totally true .. and that would also means that you need to re-do that "SIGN/sync whatever we need" after a specific amount of data as well ... not only once at the beginning.

runningnak3d commented 6 years ago

@steadfasterX If you look at that packet capture that I posted, there is only one SIGN. If you are getting access denied after 268MB / 768MB, the OPEN may play a role in that, because I am not having that issue -- but I am using the payload from my sniff as my OPEN.

The full sequence is:

OPEN // no args, opts, or payload INFO GPRO // the usual \x08\x0b as payload INFO SPRO // no args, opts but a large payload INFO GPRO // the usual \x08\x0b as payload again SIGN // no arg, opts and unknown payload, but it does contain both partition lists for the two block devices that I was writing to. CR // challenge / response mode 4 CLSE CR / mode 2 OPEN // /dev/block/sda with the rest of the payload unknown READ // with OPT1=0x44. The replay is a READ with the partition table of /dev/block/sda. This is how LG UP gets the partition layout instead of reading it directly CR mode 4 CLSE That repeats for all 7 block devices so LG UP has the partition layouts for every partition On the last partition instead of an immediate CLSE: OPCM CHEK // No args / opts or payload MISC WRTE // ARG1=0x32, Payload is a bunch of ascii 0s INFO GPRO INFO SPRO INFO GPRO CHCK CLER // No args, opts or payload CR mode 4 CLSE CR mode 2 OPEN // /dev/block/sda CR mode 4 CLSE The OPEN / CLSE is repeated for all 7 block devices. I can only guess that it does this to make sure that they are still available. Why OPEN and then immediately CLSE it makes no sense. And then it starts yet again. However this time /dev/block/sda to /dev/block/sdd are just opened and closed, but once it gets to /dev/block/sde: CR mode 2 OPEN // /dev/block/sde WRTE // ARG1=offset, OPT1=0x18 which means the data is compressed and then the data is in the payload of course. 3megs (3072) are written with each WRTE Repeats till done CR mode 4 CLSE

So, the questions are:

OPCM CKEK and CHEK CLER -- don't really need to know what they do or why, just need to send them at the right time since they have no args, opts or payload and nothing is supplied on response.

-- Brian

runningnak3d commented 6 years ago

It doesn't solve our issue of not being able to write, but I have a theory on OPCM CHCK. I think when it is sent, lafd sends the response with the condition of the MISC partition. In the event it is not in a sane state, then LG UP issues a MISC WRTE. I think LG UP then sends CHEK CLER once it is done using MISC WRTE and INFO SPRO to get things in a sane state.

This is purely speculation, but looking at some of my other packet captures lends a lot of credence to this.

-- Brian

runningnak3d commented 6 years ago

Well after sitting here and reloading Hopper after the 30 minute timeout on the demo about 10 times, I can verify a couple of things:

1 - We will not be able to write without figuring out SIGN. lafd checks the data that is sent on the WRTE to the SIGN, and if it doesn't match -- fsync fail 2 - The hash in SIGN isn't a hash (I owe someone a beer), it is a key. It looks like it is generated similarly to the challenge / response, the same fixed key in the DLL is used.

My reverse engineering skills are bad though, and even worse when it comes to arm64 code. I tried to find where the SIGN payload was created in the DLL, but had no luck -- so I went looking in lafd for the decode. I wish I could provide more details, but at least I know this is above my head now.

EDIT:

So the first 16 bytes of the payload:

First 4 - CRC32 of the payload, or the key // [LAF] sign magic code = 0x1010101 Next 2 - signature length (size of the key) 256 in this case Next 4 - sign count (the number of chunks that are signed) userdata and system are broken up into chunks. Next 4 - version Last 2 - unknown -- could be padding, they are always 0x00

The next 512 bytes are the key (AES?), but only 256 are used, so there is room for a 512 byte key. The other 256 bytes are padded with 0xFF.

Starting at byte 529 are the partition entries. Each entry is 80 bytes. The first 32 bytes are the partition name, with the last 4 bytes terminating as 0xFF The next 8 bytes are the start sector of the partition - little endian Next 8 bytes is the size of the partition uncompressed - little endian finally there is the 32 byte hash (I believe this is a SHA256 of the partition that is encrypted with the key. Don't know if it is of the compressed or uncompressed data).

-- Brian

runningnak3d commented 6 years ago

Another update.

I had no luck finding out how the payload was generated in the DLL because it isn't generated in by LG UP. The entire payload of the SIGN is in the KDZ and LG UP just passes it as-is.

So a couple of things:

1 - We can use any RSA key that we want once we figure out how it is used to generate the hashes 2 - We will have to look to lafd to figure out the first 4 bytes. I am 99% positive that it is a CRC32, but of what -- gotta dig deeper.

We solve those two things and we will be able to generate our own SIGN payload...

-- Brian

runningnak3d commented 6 years ago

On lafd version 1.0.0.4 (The version that comes with Nougat) it will never be possible to write data. I was on crack. We need the PRIVATE RSA key in order to generate the sign_hash. The sign_hash is standard RSA signing. SHA1 or SHA256 of the payload, which is then encrypted with the private RSA key. The PUBLIC key is hard coded in lafd, but it will also read a file called 'pub' that is located in the directory that it is launched from, or from the root of the SDcard IF you have a /SWUpdate dir and a valid KDZ in that directory -- but that is outside the scope of this project.

This can be a nice tool to flash official KDZs from Linux, but without the ability to create the sign_hash, lafd will just -- wait for it -- wait for it -- laf at you. Oh yea. ZING! :)

aenglebert commented 6 years ago

Writing directly from lafd will not be possible directly according to runningnak3d findings because it check for the signed hash from lg. But there other binaries available from laf mode that could allow to write (like dd). The last version of lafd still allow to use some command but a very short list, and nothing useful directly to write (ls, ps,...). But it seems it check only that the beginning of the command match with the list to allow the command. For example, it allow ls, but if you try ls -l, lsmod or lsof it also works. ls followed by something invalid will not display anything, in comparison to something completely invalid will show you the "Hello..." message. I tried to chain with others commands without success (like ls && something), but maybe I didn't try everything, do you think there is still a possibility in this way? Or maybe finding a memory overflow somewhere in lafd (I personally don't know enough the arm assembly to find this).

h0nIg commented 6 years ago

would be great if someone is able to find one overflow.

xxorax commented 6 years ago

I'm waiting for it too.