[DISCUSSION] A thread to collate and share what is known about unlocking fastboot on Oppo devices

Search This thread

melontini

Senior Member
May 14, 2023
70
8
POCO M3
Realme 8 Pro
Good news! I think I have an idea of what oppo's unlock key might be!

Bad news! It's a bunch of garbage hex data (undecodable) which needs to be filled inside opporeserve1, ended by your serial number. The starting address is 0045A000 (if you have an emmc: 00440C00)

This data might not be random, but the last line (0045A100 address) is 100% my serial in hex.
 
Last edited:

User154

Senior Member
So
Good news! I think I have an idea of what oppo's unlock key might be!

Bad news! It's a bunch of garbage hex data (undecodable) which needs to be filled inside opporeserve1, ended by your serial number. The starting address is 0045A000 (if you have an emmc: 00440C00)

This data might not be random, but the last line (0045A100 address) is 100% my serial in hex.

This is great! I have some questions if you don't mind?

Have you been working off files dumped from your own device?

If so, did you get them via edl?

How did you find the key? Is it the key you obtained from realme? That was 16 characters short so appeneding the serial to the end would make it the right length?

Do you think it would be possible to write to opporeserve via edl? (using poke command)
 

melontini

Senior Member
May 14, 2023
70
8
POCO M3
Realme 8 Pro
So


This is great! I have some questions if you don't mind?

Have you been working off files dumped from your own device?

If so, did you get them via edl?

How did you find the key? Is it the key you obtained from realme? That was 16 characters short so appeneding the serial to the end would make it the right length?

Do you think it would be possible to write to opporeserve via edl? (using poke command)

I've already unlocked my phone using turistu's rmx3474-rooting script. https://github.com/turistu/rmx3474-rooting

1. Yes and no, I just downloaded the full OTA package and unpacked system/ product/ system_ext/. vendor/ and odm/ are from device.

2. Just a rooted file manager. Although vendor and odm are shipped in the OTA.

3. As mentioned above. Both the application and the key itself were handled by the script (checkApproveResult returns the key). No, the key from realme already has the serial in hex appended.

I have a feeling that the first part is some sort of SHA hashed value. If you remove the serial at the end, the key becomes exactly 512 chars long.

The DownloadModeKey, get_key methods and readDownloadModeData don't seem to be related to fastbootUnlock.

4. I mean... maybe? But it might be possible to mod DeepTest to generate/consume a custom key.
 
Last edited:

User154

Senior Member
I've already unlocked my phone using turistu's rmx3474-rooting script. https://github.com/turistu/rmx3474-rooting

1. Yes and no, I just downloaded the full OTA package and unpacked system/ product/ system_ext/. vendor/ and odm/ are from device.

2. Just a rooted file manager. Although vendor and odm are shipped in the OTA.

3. As mentioned above. Both the application and the key itself were handled by the script (checkApproveResult returns the key). No, the key from realme already has the serial in hex appended.

I have a feeling that the first part is some sort of SHA hashed value. If you remove the serial at the end, the key becomes exactly 512 chars long.

The DownloadModeKey, get_key methods and readDownloadModeData don't seem to be related to fastbootUnlock.

4. I mean... maybe? But it might be possible to mod DeepTest to generate/consume a custom key.

I see, I am not rooted and have been working from a firmware dump that I found online.

I am able to pull the odm partition but nothing from vendor.

I have considered trying to modify the deeptest app but it will change the signature which will prevent it from even being installed. Any app with coloros in its package name must be signed with the oppo key.

Obviously directly writing data to a location on the flash using edl is fairly dangerous and would be a last resort.

Ps:

I think you may be right about the key being 512 in length. I have the key to unlock engineer mode for my device and coincidentally it is also 512 characters long.

I have viewed a secrecy.cfg file and it contains rc4 key also 512 characters long.

I don't know a huge amount about cryptography but looking up rc4 it seems it's common practice to discard the beggining of the keystream.

If you look at turistu's script it adds a random 8 character string to the begining of the key too.
 
Last edited:

melontini

Senior Member
May 14, 2023
70
8
POCO M3
Realme 8 Pro
I see, I am not rooted and have been working from a firmware dump that I found online.

I am able to pull the odm partition but nothing from vendor.

I have considered trying to modify the deeptest app but it will change the signature which will prevent it from even being installed. Any app with coloros in its package name must be signed with the oppo key.

Obviously directly writing data to a location on the flash using edl is fairly dangerous and would be a last resort.

Ps:

I think you may be right about the key being 512 in length. I have the key to unlock engineer mode for my device and coincidentally it is also 512 characters long.

I have viewed a secrecy.cfg file and it contains rc4 key also 512 characters long.

I don't know a huge amount about cryptography but looking up rc4 it seems it's common practice to discard the beggining of the keystream.

If you look at turistu's script it adds a random 8 character string to the begining of the key too.
If the key is RC4, this means that fastboot can check its validity and it's not random. The key has to be generated from info extractable from the device.

I guess the key doesn't exist on oppo's servers before an application is approved and is generated from info sent by Deeptesting. Since IMEI and Serial are the only static values sent to oppo/realme. (the model is only used to check against a blacklist of model IDs) I believe those values are manipulated in some way to make an RC4 secret.

They have to be a secret because encoding IMEI, serial and that weird key from reserve only ends up with 78 chars. To make an encoded message 512 chars long, you need 256 chars in your original message. nah

One way to check what's going on is reversing fastboot, but that's kind of beyond me.
 

User154

Senior Member
If the key is RC4, this means that fastboot can check its validity and it's not random. The key has to be generated from info extractable from the device.

I guess the key doesn't exist on oppo's servers before an application is approved and is generated from info sent by Deeptesting. Since IMEI and Serial are the only static values sent to oppo/realme. (the model is only used to check against a blacklist of model IDs) I believe those values are manipulated in some way to make an RC4 secret.

They have to be a secret because encoding IMEI, serial and that weird key from reserve only ends up with 78 chars. To make an encoded message 512 chars long, you need 256 chars in your original message. nah

One way to check what's going on is reversing fastboot, but that's kind of beyond me.

I would be shocked if it was rc4 after looking into it, its shockingly easy to crack. I didn't realise that's what WEP is based off, and we all know how easy that is to crack.

I'm not sure where the key comes from but I think if they did use rc4 they it would make sense for a secret to be generated on a per device basis.

I had a little look at abl the other day but it was very hard to read, It didn't look like it had disassembled correctly. I've been meaning to take another look

Edit:

Just checked out dogbolt and I see they have both ghidra and ida (hex-rays). Guess I won't bother with ghidra then lol

What sort of output did you get from using hex rays on dogbolt?
 
Last edited:

melontini

Senior Member
May 14, 2023
70
8
POCO M3
Realme 8 Pro
I would be shocked if it was rc4 after looking into it, its shockingly easy to crack. I didn't realise that's what WEP is based off, and we all know how easy that is to crack.

I'm not sure where the key comes from but I think if they did use rc4 they it would make sense for a secret to be generated on a per device basis.

I had a little look at abl the other day but it was very hard to read, It didn't look like it had disassembled correctly. I've been meaning to take another look
Yeah. I tried using dogbolt.org, and only RetDec had something code-looking.
 

melontini

Senior Member
May 14, 2023
70
8
POCO M3
Realme 8 Pro
I would be shocked if it was rc4 after looking into it, its shockingly easy to crack. I didn't realise that's what WEP is based off, and we all know how easy that is to crack.

I'm not sure where the key comes from but I think if they did use rc4 they it would make sense for a secret to be generated on a per device basis.

I had a little look at abl the other day but it was very hard to read, It didn't look like it had disassembled correctly. I've been meaning to take another look

Edit:

Just checked out dogbolt and I see they have both ghidra and ida (hex-rays). Guess I won't bother with ghidra then lol

What sort of output did you get from using hex rays on dogbolt?

C:
/* This file was generated by the Hex-Rays decompiler version 8.2.0.221215.
   Copyright (c) 2007-2021 Hex-Rays <[email protected]>

   Detected compiler: GNU C++
*/

#include <defs.h>


//-------------------------------------------------------------------------
// Function declarations

void __fastcall __noreturn sub_9FA003B0(int a1, int a2, int a3);
void __fastcall sub_9FA1376A(int a1, unsigned int a2, unsigned int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18, int a19, int a20, int a21, int a22, int a23, int a24, int a25, int a26, int a27, int a28, int a29, int a30, int a31);
void __fastcall sub_9FA1DDD4(int a1);


//----- (9FA003B0) --------------------------------------------------------
void __fastcall __noreturn sub_9FA003B0(int a1, int a2, int a3)
{
  int v3; // r5

  *(_BYTE *)(v3 + 8) = *(_DWORD *)(a2 + 96);
  __asm { LDMDB.W         R2, {R0,R2,R7,R8,R10,PC} }
}
// 9FA003C0: unbalanced stack, ignored a potential tail call
// 9FA003BA: variable 'v3' is possibly undefined

//----- (9FA1376A) --------------------------------------------------------
// positive sp value has been detected, the output may be wrong!
void __fastcall sub_9FA1376A(
        int a1,
        unsigned int a2,
        unsigned int a3,
        int a4,
        int a5,
        int a6,
        int a7,
        int a8,
        int a9,
        int a10,
        int a11,
        int a12,
        int a13,
        int a14,
        int a15,
        int a16,
        int a17,
        int a18,
        int a19,
        int a20,
        int a21,
        int a22,
        int a23,
        int a24,
        int a25,
        int a26,
        int a27,
        int a28,
        int a29,
        int a30,
        int a31)
{
  int v31; // r3
  _DWORD *v35; // r7
  int v36; // r6
  unsigned int v37; // r4
  unsigned int v38; // r2
  char *v40; // r7
  unsigned int v41; // r4
  unsigned int *v42; // r6
  _DWORD *v43; // r7

  STACK[0x2D4] = a3;
  *v43 = a1;
  v43[1] = a2;
  v43[2] = a3;
  v35 = v43 + 3;
  *(_WORD *)(v41 + 14) = a1;
  *(_WORD *)(v41 + 46) = a2;
  *v42 = a2;
  v42[1] = v41;
  v42[2] = (unsigned int)v35;
  v36 = (int)(v42 + 3);
  __asm { STCL            p5, c5, [R0,#-0x318] }
  *((_BYTE *)v35 + 26) = a1;
  *(_DWORD *)(a1 + 36) = v36;
  v37 = v41 - 249;
  v38 = a3 - 221;
  _VF = __OFADD__(v35, 217);
  v40 = (char *)v35 + 217;
  if ( !(v36 >> 30) )
    JUMPOUT(0x9FA13648);
  *(_BYTE *)(v38 + 31) = a2;
  *(_DWORD *)(a2 >> 12) = v38;
  *(_DWORD *)((a2 >> 12) + 4) = v40;
  if ( !_VF && v37 >> 2 != 0 )
  {
    STACK[0x338] = v38;
    JUMPOUT(0x9FA1319C);
  }
  v31 = *(__int16 *)(v37 + a31);
  *((_WORD *)v40 + 3) = a2 + 151;
  *(_WORD *)(v31 + 28) = v38;
  __asm { STC             p2, c6, [LR], {0x2E} ; '.' }
  *(_DWORD *)((v37 >> 2) + a2 + 71) = v38;
  *(_DWORD *)(v38 + v37 + 72) = v38 + v37;
  *(_BYTE *)(v38 + a31 - 53) = v38;
  __asm { BX              R8 }
}
// 9FA1311C: positive sp value 38 has been found
// 9FA13028: control flows out of bounds to 9FA1302A
// 9FA1312E: control flows out of bounds to 9FA13130
// 9FA1319A: control flows out of bounds to 9FA1319C
// 9FA13570: control flows out of bounds to 9FA13572
// 9FA13646: control flows out of bounds to 9FA13648
// 9FA13588: control flows out of bounds to 9F8E227A
// 9FA13622: variable 'v43' is possibly undefined
// 9FA1362C: variable 'v41' is possibly undefined
// 9FA13630: variable 'v42' is possibly undefined
// 9FA13404: using guessed type int dword_9FA13404[27];

//----- (9FA1DDD4) --------------------------------------------------------
void __fastcall sub_9FA1DDD4(int a1)
{
  MEMORY[0x9FC61AE0](a1);
  JUMPOUT(0x9FA1D9AA);
}
// 9FA1D9A8: control flows out of bounds to 9FA1D9AA

// nfuncs=8 queued=3 decompiled=3 lumina nreq=0 worse=0 better=0
// ALL OK, 3 function(s) have been successfully decompiled

Nothing useful, although Snowman spat out 168012 lines of c++ code. It mostly calls ASM

C++:
void fun_9fa20e80() {
    int1_t less1;
    int32_t r12_2;
    int32_t r9_3;
    int1_t c4;
    int1_t less5;
    int1_t c6;
    int1_t less_or_equal7;
    int1_t z8;
    int1_t n9;
    int1_t less10;

    if (less1) {
        *reinterpret_cast<signed char*>(reinterpret_cast<uint32_t>(0x605df178 - r12_2) >> 4) = static_cast<signed char>(r9_3);
    }
    if (!c4) {
        __asm__("ldclo p4, c11, [lr, #0x278]!");
    }
    if (less5) {
        __asm__("teqlt r2, sp, asr #16");
    }
    __asm__("ldmda r3, {r0, r6, r8, sl, fp, sp, lr}");
    if (c6) {
        __asm__("ldmdbhs r0!, {r0, r1, r3, r4, r6, sl, fp, ip, sp, lr}");
    }
    if (less_or_equal7) {
        __asm__("ldrbtle fp, [r5], -sl, lsl #12");
    }
    if (!z8) {
        __asm__("ldrtne ip, [sl], #-0xa8a");
    }
    if (!n9) {
    }
    if (less10) {
        __asm__("mrclt p1, #3, r6, c4, c9, #6");
    }
}
 

User154

Senior Member
C:
/* This file was generated by the Hex-Rays decompiler version 8.2.0.221215.
   Copyright (c) 2007-2021 Hex-Rays <[email protected]>

   Detected compiler: GNU C++
*/

#include <defs.h>


//-------------------------------------------------------------------------
// Function declarations

void __fastcall __noreturn sub_9FA003B0(int a1, int a2, int a3);
void __fastcall sub_9FA1376A(int a1, unsigned int a2, unsigned int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12, int a13, int a14, int a15, int a16, int a17, int a18, int a19, int a20, int a21, int a22, int a23, int a24, int a25, int a26, int a27, int a28, int a29, int a30, int a31);
void __fastcall sub_9FA1DDD4(int a1);


//----- (9FA003B0) --------------------------------------------------------
void __fastcall __noreturn sub_9FA003B0(int a1, int a2, int a3)
{
  int v3; // r5

  *(_BYTE *)(v3 + 8) = *(_DWORD *)(a2 + 96);
  __asm { LDMDB.W         R2, {R0,R2,R7,R8,R10,PC} }
}
// 9FA003C0: unbalanced stack, ignored a potential tail call
// 9FA003BA: variable 'v3' is possibly undefined

//----- (9FA1376A) --------------------------------------------------------
// positive sp value has been detected, the output may be wrong!
void __fastcall sub_9FA1376A(
        int a1,
        unsigned int a2,
        unsigned int a3,
        int a4,
        int a5,
        int a6,
        int a7,
        int a8,
        int a9,
        int a10,
        int a11,
        int a12,
        int a13,
        int a14,
        int a15,
        int a16,
        int a17,
        int a18,
        int a19,
        int a20,
        int a21,
        int a22,
        int a23,
        int a24,
        int a25,
        int a26,
        int a27,
        int a28,
        int a29,
        int a30,
        int a31)
{
  int v31; // r3
  _DWORD *v35; // r7
  int v36; // r6
  unsigned int v37; // r4
  unsigned int v38; // r2
  char *v40; // r7
  unsigned int v41; // r4
  unsigned int *v42; // r6
  _DWORD *v43; // r7

  STACK[0x2D4] = a3;
  *v43 = a1;
  v43[1] = a2;
  v43[2] = a3;
  v35 = v43 + 3;
  *(_WORD *)(v41 + 14) = a1;
  *(_WORD *)(v41 + 46) = a2;
  *v42 = a2;
  v42[1] = v41;
  v42[2] = (unsigned int)v35;
  v36 = (int)(v42 + 3);
  __asm { STCL            p5, c5, [R0,#-0x318] }
  *((_BYTE *)v35 + 26) = a1;
  *(_DWORD *)(a1 + 36) = v36;
  v37 = v41 - 249;
  v38 = a3 - 221;
  _VF = __OFADD__(v35, 217);
  v40 = (char *)v35 + 217;
  if ( !(v36 >> 30) )
    JUMPOUT(0x9FA13648);
  *(_BYTE *)(v38 + 31) = a2;
  *(_DWORD *)(a2 >> 12) = v38;
  *(_DWORD *)((a2 >> 12) + 4) = v40;
  if ( !_VF && v37 >> 2 != 0 )
  {
    STACK[0x338] = v38;
    JUMPOUT(0x9FA1319C);
  }
  v31 = *(__int16 *)(v37 + a31);
  *((_WORD *)v40 + 3) = a2 + 151;
  *(_WORD *)(v31 + 28) = v38;
  __asm { STC             p2, c6, [LR], {0x2E} ; '.' }
  *(_DWORD *)((v37 >> 2) + a2 + 71) = v38;
  *(_DWORD *)(v38 + v37 + 72) = v38 + v37;
  *(_BYTE *)(v38 + a31 - 53) = v38;
  __asm { BX              R8 }
}
// 9FA1311C: positive sp value 38 has been found
// 9FA13028: control flows out of bounds to 9FA1302A
// 9FA1312E: control flows out of bounds to 9FA13130
// 9FA1319A: control flows out of bounds to 9FA1319C
// 9FA13570: control flows out of bounds to 9FA13572
// 9FA13646: control flows out of bounds to 9FA13648
// 9FA13588: control flows out of bounds to 9F8E227A
// 9FA13622: variable 'v43' is possibly undefined
// 9FA1362C: variable 'v41' is possibly undefined
// 9FA13630: variable 'v42' is possibly undefined
// 9FA13404: using guessed type int dword_9FA13404[27];

//----- (9FA1DDD4) --------------------------------------------------------
void __fastcall sub_9FA1DDD4(int a1)
{
  MEMORY[0x9FC61AE0](a1);
  JUMPOUT(0x9FA1D9AA);
}
// 9FA1D9A8: control flows out of bounds to 9FA1D9AA

// nfuncs=8 queued=3 decompiled=3 lumina nreq=0 worse=0 better=0
// ALL OK, 3 function(s) have been successfully decompiled

Nothing useful, although Snowman spat out 168012 lines of c++ code. It mostly calls ASM

C++:
void fun_9fa20e80() {
    int1_t less1;
    int32_t r12_2;
    int32_t r9_3;
    int1_t c4;
    int1_t less5;
    int1_t c6;
    int1_t less_or_equal7;
    int1_t z8;
    int1_t n9;
    int1_t less10;

    if (less1) {
        *reinterpret_cast<signed char*>(reinterpret_cast<uint32_t>(0x605df178 - r12_2) >> 4) = static_cast<signed char>(r9_3);
    }
    if (!c4) {
        __asm__("ldclo p4, c11, [lr, #0x278]!");
    }
    if (less5) {
        __asm__("teqlt r2, sp, asr #16");
    }
    __asm__("ldmda r3, {r0, r6, r8, sl, fp, sp, lr}");
    if (c6) {
        __asm__("ldmdbhs r0!, {r0, r1, r3, r4, r6, sl, fp, ip, sp, lr}");
    }
    if (less_or_equal7) {
        __asm__("ldrbtle fp, [r5], -sl, lsl #12");
    }
    if (!z8) {
        __asm__("ldrtne ip, [sl], #-0xa8a");
    }
    if (!n9) {
    }
    if (less10) {
        __asm__("mrclt p1, #3, r6, c4, c9, #6");
    }
}

Is that from abl.elf or the PE from within?
 

melontini

Senior Member
May 14, 2023
70
8
POCO M3
Realme 8 Pro
C:
v2 = (_QWORD *)sub_32F78(a1);
  v3 = *v2;
  v4 = v2;
  v5 = *v2;
  *v2 -= 96i64;
  v6 = qword_5E528;
  *(_QWORD *)(v5 - 8) = qword_5E528;
  v7 = (_QWORD *)(v5 - 8);
  v8 = ((__int64 (*)(void))loc_47F68)();
  if ( v8 )
  {
    if ( (unsigned __int8)sub_35B48(v8) && (unsigned __int8)sub_11458() )
      sub_356D4(0x80000000i64, "fastboot_unlock_verify error and reboot.");
    v8 = ((__int64 (__fastcall *)(__int64))loc_183E8)(62i64);
  }

I found this sus method (sub_10FBC), but it leads to a billion other smaller methods
 

User154

Senior Member
I think definitley somewhere around here that the check is taking place. I was looking this morning and iirc its somewhere near here in a graph view where it can also split to the branch of a fasboot initialized message. I think it was sub1440 where it branched but I'm not too sure
 

melontini

Senior Member
May 14, 2023
70
8
POCO M3
Realme 8 Pro
So, I found sub_479C0 (names might be different). Some interesting text from it and methods below it: "rsa verify fail, status is 0x%x", "the serial is not match\n". Bootloader also seems to compare model names if "rpmb" is enabled.

There's also sub_F018, which reads the opporeserve1, serial number, some sort of hash. There's also this message which can get logged "RSA: Failed to LocateProtocol gEfiQcomASN1X509ProtocolGuid", so it might be X.509 and not RC4.

Another interesting, unrelated thing is that "flashing avb_custom_key" in sub_14584 wasn't removed.
 
Last edited:

User154

Senior Member
So, I found sub_479C0 (names might be different). Some interesting text from it and methods below it: "rsa verify fail, status is 0x%x", "the serial is not match\n". Bootloader also seems to compare model names if "rpmb" is enabled.

There's also sub_F018, which reads the opporeserve1, serial number, some sort of hash. There's also this message which can get logged "RSA: Failed to LocateProtocol gEfiQcomASN1X509ProtocolGuid", so it might be X.509 and not RC4.

Another interesting, unrelated thing is that "flashing avb_custom_key" in sub_14584 wasn't removed.

That's a nice a find, I really would expect they would use something other than rc4 given the ease at which it can be cracked.

Edit:

Remembered sering something about rpmb in the engineer app. I looked into it and its a secure storage section of the flash protected by a sha256 key

Screenshot_2023-06-01-15-15-39-70_40dbc481ca5b738a325e5182fc08a331.jpg
 
Last edited:

melontini

Senior Member
May 14, 2023
70
8
POCO M3
Realme 8 Pro
No major updates, but Ghidra seems to do a slighty better decompile job.

C:
longlong FUN_000101f8(longlong *param_1)
{
  longlong lVar1;
  char cVar2;
  longlong *plVar3;
  longlong lVar4;
  char *pcVar5;
  char in_w2;
  undefined uVar6;
  longlong *plVar7;
  undefined in_w3;
  undefined in_w4;
  undefined in_w5;
  undefined in_w6;
  undefined in_w7;
  longlong lVar8;
  undefined8 *puVar9;
  longlong lVar10;
  longlong lVar11;
  longlong lVar12;
  undefined8 *puVar13;
  longlong lVar14;
  longlong *plVar15;
  longlong *plVar16;
  longlong lVar17;
  undefined4 in_stack_ffffffffffffff80;
  undefined4 uVar18;
  undefined4 in_stack_ffffffffffffff84;
  
  plVar3 = FUN_00032f78();
  lVar17 = *plVar3;
  *plVar3 = lVar17 + -0x470;
  lVar1 = DAT_0005e528;
  *(longlong *)(lVar17 + -8) = DAT_0005e528;
  if (DAT_00063378 == 0) {
    in_w2 = 'x';
    lVar8 = (**(code **)(DAT_00063310 + 0x140))(&DAT_0005de44,0);
    if (lVar8 != 0) {
      cVar2 = FUN_00035b48();
      if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
        FUN_000356d4(0x80000000,
                     (byte *)"RSA: Failed to LocateProtocol gEfiQcomASN1X509ProtocolGuid %r\n",
                     (char)lVar8,in_w3,in_w4,in_w5,in_w6,in_w7,
                     CONCAT44(in_stack_ffffffffffffff84,in_stack_ffffffffffffff80));
      }
      goto LAB_000107d4;
    }
  }
  lVar8 = -0x7ffffffffffffffe;
  lVar4 = FUN_000475fc();
  lVar11 = DAT_00063378;
  if (lVar4 == 0) {
    lVar8 = -0x7ffffffffffffff7;
    goto LAB_000107d4;
  }
  lVar12 = *param_1;
  lVar14 = param_1[1];
  *(undefined8 *)(lVar17 + -0x58) = 0;
  *(undefined8 *)(lVar17 + -0x50) = 0;
  lVar10 = lVar8;
  if ((lVar11 != 0) && (lVar10 = -0x7ffffffffffffffe, lVar12 != 0)) {
    in_w2 = (char)lVar17 + -0x58;
    lVar10 = (**(code **)(DAT_00063310 + 0x140))(&DAT_0005de24,0);
    if (lVar10 == 0) {
      in_w2 = (char)lVar17 + -0x50;
      lVar10 = (***(code ***)(lVar17 + -0x58))(*(code ***)(lVar17 + -0x58),&DAT_0005de34);
      if (lVar10 == 0) {
        if (*(longlong *)(lVar17 + -0x50) == 0x20) {
          lVar10 = (**(code **)(*(longlong *)(lVar17 + -0x58) + 0x10))
                             (*(longlong *)(lVar17 + -0x58),&DAT_0005de34);
          if (lVar10 == 0) {
            in_w2 = (char)lVar14;
            lVar10 = (**(code **)(*(longlong *)(lVar17 + -0x58) + 0x18))
                               (*(longlong *)(lVar17 + -0x58),lVar12);
            if (lVar10 == 0) {
              lVar10 = (**(code **)(*(longlong *)(lVar17 + -0x58) + 0x20))
                                 (*(longlong *)(lVar17 + -0x58),lVar17 + -0x48);
              if (lVar10 == 0) {
                in_w2 = (char)*(undefined8 *)(lVar17 + -0x50);
                (**(code **)(DAT_00063310 + 0x160))(lVar4,lVar17 + -0x48);
              }
              else {
                cVar2 = FUN_00035b48();
                if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
                  pcVar5 = "GetDataHash: HashFinal unsuccessful! Status: %r\n";
                  goto LAB_00010378;
                }
              }
            }
            else {
              cVar2 = FUN_00035b48();
              if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
                pcVar5 = "GetDataHash: HashUpdate unsuccessful!Status: %r\n";
                goto LAB_00010378;
              }
            }
          }
          else {
            cVar2 = FUN_00035b48();
            if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
              pcVar5 = "GetDataHash: HashInit unsuccessful! Status: %r\n";
              goto LAB_00010378;
            }
          }
        }
        else {
          cVar2 = FUN_00035b48();
          if ((cVar2 == '\0') || (cVar2 = FUN_00035b70(-0x80000000), cVar2 == '\0')) {
            lVar10 = 0x50;
          }
          else {
            in_w2 = ' ';
            in_w3 = (undefined)*(undefined8 *)(lVar17 + -0x50);
            FUN_000356d4(0x80000000,
                         (byte *)"GetDataHash: Invalid size! HashSize: %d, DigestSize: %d\n",0x20,
                         in_w3,in_w4,in_w5,in_w6,in_w7,
                         CONCAT44(in_stack_ffffffffffffff84,in_stack_ffffffffffffff80));
            lVar10 = 0x50;
          }
        }
      }
      else {
        cVar2 = FUN_00035b48();
        if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
          pcVar5 = "GetDataHash: GetHashSize unsuccessful! Status: %r\n";
          goto LAB_00010378;
        }
      }
    }
    else {
      cVar2 = FUN_00035b48();
      if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
        pcVar5 = "GetDataHash: LocateProtocol unsuccessful!Status: %r\n";
LAB_00010378:
        lVar11 = lVar10;
        FUN_000356d4(0x80000000,(byte *)pcVar5,(char)lVar10,in_w3,in_w4,in_w5,in_w6,in_w7,
                     CONCAT44(in_stack_ffffffffffffff84,in_stack_ffffffffffffff80));
        in_w2 = (char)lVar11;
      }
    }
  }
  if (lVar10 == 0) {
    uVar18 = *(undefined4 *)(param_1 + 3);
    puVar13 = (undefined8 *)(lVar17 + -0x460);
    lVar11 = param_1[2];
    FUN_00045ce8((longlong)puVar13,0,0x48);
    plVar15 = (longlong *)(lVar17 + -0x468);
    uVar6 = 0;
    *plVar15 = 0;
    FUN_0000cae4(puVar13,0x48,0);
    if (lVar11 == 0) {
      cVar2 = FUN_00035b48();
      if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
        FUN_000356d4(0x80000000,(byte *)"VerifyHashWithRSASignature:Invalid pointer\n",uVar6,in_w3,
                     in_w4,in_w5,in_w6,in_w7,
                     CONCAT44(in_stack_ffffffffffffff84,in_stack_ffffffffffffff80));
      }
    }
    else {
      lVar8 = FUN_000475fc();
      plVar16 = (longlong *)(lVar17 + -0x448);
      *plVar16 = lVar8;
      if (lVar8 == 0) {
        cVar2 = FUN_00035b48();
        if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
          pcVar5 = "VerifyHashWithRSASignature: mem allocation err for Key.N\n";
LAB_0001053c:
          FUN_000356d4(0x80000000,(byte *)pcVar5,uVar6,in_w3,in_w4,in_w5,in_w6,in_w7,
                       CONCAT44(in_stack_ffffffffffffff84,in_stack_ffffffffffffff80));
        }
LAB_00010544:
        lVar8 = 0x50;
        lVar11 = *plVar16;
      }
      else {
        lVar8 = FUN_000475fc();
        *(longlong *)(lVar17 + -0x458) = lVar8;
        if (lVar8 == 0) {
          cVar2 = FUN_00035b48();
          if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
            pcVar5 = "VerifyHashWithRSASignature: mem allocation err for Key.e\n";
            goto LAB_0001053c;
          }
          goto LAB_00010544;
        }
        plVar7 = plVar15;
        lVar8 = (**(code **)(DAT_00063310 + 0x140))(&DAT_0005de54,0);
        uVar6 = SUB81(plVar7,0);
        if (lVar8 == 0) {
          uVar6 = (undefined)*(undefined4 *)(param_1 + 6);
          puVar9 = (undefined8 *)(lVar17 + -0x210);
          lVar8 = (**(code **)(*plVar15 + 8))(*plVar15,param_1[4]);
          in_w3 = SUB81(puVar9,0);
          if (lVar8 == 0) {
            uVar6 = (undefined)*(undefined4 *)(param_1 + 9);
            puVar9 = (undefined8 *)(lVar17 + -0x418);
            lVar8 = (**(code **)(*plVar15 + 8))(*plVar15,param_1[7]);
            in_w3 = SUB81(puVar9,0);
            if (lVar8 == 0) {
              FUN_00045c34(*(undefined8 **)(lVar17 + -0x448),(undefined8 *)(lVar17 + -0x210),0x208);
              puVar9 = *(undefined8 **)(lVar17 + -0x458);
              FUN_00045c34(puVar9,(undefined8 *)(lVar17 + -0x418),0x208);
              *(undefined4 *)(puVar9 + 0x41) = 0;
              uVar6 = 1;
              *(undefined4 *)puVar13 = 0;
              in_w3 = 0;
              in_w4 = 3;
              in_w6 = 0x20;
              lVar8 = (**(code **)(*plVar15 + 0x10))(*plVar15,puVar13);
              in_w7 = (undefined)lVar11;
              in_w5 = (undefined)lVar4;
              cVar2 = FUN_00035b48();
              if (lVar8 == 0) {
                if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(0x400000), cVar2 != '\0')) {
                  uVar6 = 0;
                  FUN_000356d4(0x400000,(byte *)
                                        "VerifyHashWithRSASignature: SecRSAVerifySig success! Status: %r\n"
                               ,0,in_w3,in_w4,in_w5,in_w6,in_w7,
                               CONCAT44(in_stack_ffffffffffffff84,uVar18));
                }
                lVar8 = 0;
                lVar11 = *plVar16;
                in_stack_ffffffffffffff80 = uVar18;
                goto joined_r0x00010778;
              }
              if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
                pcVar5 = "VerifyHashWithRSASignature: SecRSAVerifySig failed! Status: %r\n";
                goto LAB_00010768;
              }
            }
            else {
              cVar2 = FUN_00035b48();
              uVar18 = in_stack_ffffffffffffff80;
              if ((cVar2 != '\0') &&
                 (cVar2 = FUN_00035b70(-0x80000000), uVar18 = in_stack_ffffffffffffff80,
                 cVar2 != '\0')) {
                pcVar5 = 
                "VerifyHashWithRSASignature: SecRSABigIntReadBin for Modulus failed! Status: %r\n";
LAB_00010768:
                lVar11 = lVar8;
                FUN_000356d4(0x80000000,(byte *)pcVar5,(char)lVar8,in_w3,in_w4,in_w5,in_w6,in_w7,
                             CONCAT44(in_stack_ffffffffffffff84,uVar18));
                uVar6 = (undefined)lVar11;
              }
            }
            goto LAB_00010774;
          }
          cVar2 = FUN_00035b48();
          if (cVar2 != '\0') {
            cVar2 = FUN_00035b70(-0x80000000);
            uVar18 = in_stack_ffffffffffffff80;
            if (cVar2 != '\0') {
              pcVar5 = 
              "VerifyHashWithRSASignature: SecRSABigIntReadBin for Modulus failed!Status: %r\n";
              goto LAB_00010768;
            }
            goto LAB_00010774;
          }
          lVar11 = *plVar16;
        }
        else {
          cVar2 = FUN_00035b48();
          uVar18 = in_stack_ffffffffffffff80;
          if ((cVar2 != '\0') &&
             (cVar2 = FUN_00035b70(-0x80000000), uVar18 = in_stack_ffffffffffffff80, cVar2 != '\0'))
          {
            pcVar5 = "VerifyHashWithRSASignature: LocateProtocol failed, Status: %r\n";
            goto LAB_00010768;
          }
LAB_00010774:
          lVar11 = *plVar16;
          in_stack_ffffffffffffff80 = uVar18;
        }
      }
joined_r0x00010778:
      if (lVar11 != 0) {
        FUN_00047620();
      }
      if (*(longlong *)(lVar17 + -0x458) != 0) {
        FUN_00047620();
      }
    }
    if (lVar8 == 0) {
      lVar8 = 0;
    }
    else {
      cVar2 = FUN_00035b48();
      if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
        FUN_000356d4(0x80000000,(byte *)"Failed to verify hash\n",uVar6,in_w3,in_w4,in_w5,in_w6,
                     in_w7,CONCAT44(in_stack_ffffffffffffff84,in_stack_ffffffffffffff80));
      }
    }
  }
  else {
    cVar2 = FUN_00035b48();
    lVar8 = lVar10;
    if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
      FUN_000356d4(0x80000000,(byte *)"Failed to get hash\n",in_w2,in_w3,in_w4,in_w5,in_w6,in_w7,
                   CONCAT44(in_stack_ffffffffffffff84,in_stack_ffffffffffffff80));
    }
  }
  FUN_00047620();
LAB_000107d4:
  if (lVar1 != *(longlong *)(lVar17 + -8)) {
                    // WARNING: Subroutine does not return
    FUN_000181c4();
  }
  *plVar3 = lVar17;
  return lVar8;
}
 

melontini

Senior Member
May 14, 2023
70
8
POCO M3
Realme 8 Pro
Interesting method:
C:
ulonglong FUN_00047d60(longlong param_1,undefined8 *param_2)

{
  uint uVar1;
  undefined8 *puVar2;
  char cVar3;
  ulonglong uVar4;
  ulonglong uVar5;
  char *pcVar6;
  undefined in_w2;
  undefined in_w3;
  undefined in_w4;
  undefined in_w5;
  undefined in_w6;
  undefined in_w7;
  undefined8 unaff_x30;
 
  if ((*(char *)(param_2 + 0x21) == '0') && (*(char *)((longlong)param_2 + 0x109) == '0')) {
    cVar3 = FUN_00035b48();
    if ((cVar3 != '\0') && (cVar3 = FUN_00035b70(-0x80000000), cVar3 != '\0')) {
      log(0x80000000,(byte *)"use the new struct.\n",in_w2,in_w3,in_w4,in_w5,in_w6,in_w7,
                   unaff_x30);
    }
    if (*(char *)((longlong)param_2 + 0x10c) == '1') {
      uVar4 = FUN_00047bd8((longlong)param_2);
      if ((uVar4 & 0xff) != 0) {
        puVar2 = (undefined8 *)(param_1 + 8);
        thunk_FUN_0000cb98(puVar2,param_2 + 0x20,8);
        *(undefined4 *)(param_1 + 4) = 8;
        thunk_FUN_0000cb98((undefined8 *)(param_1 + 0x10),param_2 + 0x21,4);
        uVar1 = *(int *)(param_1 + 4) + 4;
        *(uint *)(param_1 + 4) = uVar1;
        thunk_FUN_0000cb98((undefined8 *)((longlong)puVar2 + (ulonglong)uVar1),
                           (undefined8 *)((longlong)param_2 + 0x10c),0x20);
        uVar1 = *(int *)(param_1 + 4) + 0x20;
        *(uint *)(param_1 + 4) = uVar1;
        thunk_FUN_0000cb98((undefined8 *)((longlong)puVar2 + (ulonglong)uVar1),
                           (undefined8 *)((longlong)param_2 + 300),0x10);
        *(int *)(param_1 + 4) = *(int *)(param_1 + 4) + 0x10;
        thunk_FUN_0000cb98((undefined8 *)(param_1 + 0x10c),param_2,0x100);
        *(undefined4 *)(param_1 + 0x108) = 0x100;
        thunk_FUN_0000cb98((undefined8 *)&DAT_00079da0,(undefined8 *)((longlong)param_2 + 0x10c),
                           0x20);
        goto LAB_00047eb0;
      }
      cVar3 = FUN_00035b48();
      if ((cVar3 != '\0') && (cVar3 = FUN_00035b70(-0x80000000), cVar3 != '\0')) {
        pcVar6 = "the model name is not match\n";
        goto LAB_00047f44;
      }
    }
    else {
      cVar3 = FUN_00035b48();
      if ((cVar3 != '\0') && (cVar3 = FUN_00035b70(-0x80000000), cVar3 != '\0')) {
        pcVar6 = "the permission is not match\n";
LAB_00047f44:
        log(0x80000000,(byte *)pcVar6,in_w2,in_w3,in_w4,in_w5,in_w6,in_w7,unaff_x30);
      }
    }
    uVar4 = 0xffffffff;
  }
  else {
    thunk_FUN_0000cb98((undefined8 *)(param_1 + 8),param_2 + 0x20,8);
    *(undefined4 *)(param_1 + 4) = 8;
    thunk_FUN_0000cb98((undefined8 *)(param_1 + 0x10c),param_2,0x100);
    *(undefined4 *)(param_1 + 0x108) = 0x100;
LAB_00047eb0:
    uVar5 = FUN_00048538(param_1);
    uVar4 = uVar5 & 0xffffffff;
    if ((((int)uVar5 != 0) && (cVar3 = FUN_00035b48(), cVar3 != '\0')) &&
       (cVar3 = FUN_00035b70(-0x80000000), cVar3 != '\0')) {
      log(0x80000000,(byte *)"rsa verify fail, status is 0x%x \n",(char)uVar4,in_w3,in_w4,
                   in_w5,in_w6,in_w7,unaff_x30);
    }
  }
  return uVar4;
}
 

DRS_Frizzy

Member
Nov 2, 2022
15
0
OPPO A74 5G
I thought this thread got dead but no.
It's nice to hear some news but sadly I can't help much, me and user154(of course you made the most hard work user154) worked hard but still didn't found anything so guys don't give up.
And about this codes sorry but I don't understand in coding too much it's very hard for me XD.
 

melontini

Senior Member
May 14, 2023
70
8
POCO M3
Realme 8 Pro
So, I think I want to recap everything I know about fastboot verification based on Realme 8 pro:

The two structs:

The "old struct": If your key is 528 chars long, your phone will use the "old struct" which verifies the key using an RSA (I believe X.509) key and the serial key encoded at the end and.

The "new struct": Everything from the "old struct" still seems to apply, but now the key has your model name + RPMB key attached (I think). The model name from the Realme key is checked against the one stored in RPMB.

The fastboot order, but I made up all the method names:

Before loading the fastboot app `launchFastbootApp` calls `verifyFastbootAccess`, where if it fails, "fastboot_unlock_verify error and reboot." is logged and your phone reboots.

Inside `verifyFastbootAccess` your phone first checks if Secure Boot is enabled, if not, the fastboot check is skipped completely.
I'm not sure which Secure Boot the method is talking about, but I assume it's this https://www.qualcomm.com/content/da...ot-and-image-authentication-version_final.pdf

proof or smth:
C:
uVar8 = checkSecurebootStatus(uVar7,uVar10,(int)uVar12);
  uVar10 = (undefined)uVar12;
  if (((uint)uVar8 & 0xff) != 1) {
    cVar4 = shouldPringMsg();
    if ((cVar4 != '\0') && (cVar4 = FUN_00035b70(-0x80000000), cVar4 != '\0')) {
      log(0x80000000,(byte *)"secure boot is not enable \n",uVar10,in_w3,in_w4,in_w5,in_w6,
                   in_w7,unaff_x30);
    }
    if ((uVar8 & 0xff) == 0) {
      cVar4 = shouldPringMsg();
      if ((cVar4 != '\0') && (cVar4 = FUN_00035b70(-0x80000000), cVar4 != '\0')) {
        log(0x80000000,(byte *)"skip fastboot verify when secureboot is off!\n",uVar10,
                     in_w3,in_w4,in_w5,in_w6,in_w7,unaff_x30);
      }
      uVar8 = 0;
      DAT_00079da1 = 0x31;
      goto LAB_000480f0;
    }
  }

If Secure Boot is enabled, the good old opporeserve1/oplusreserve1 is read.

First off, the serial in the key is checked against your real one.
Immediately after checking your serial, `verifyFastbootKey` is called, where this is used:
C:
if ((*(char *)(param_2 + 0x21) == '0') && (*(char *)((longlong)param_2 + 0x109) == '0'))
to decide which struct to use, where if == true → new struct, if == false → old struct.
If new struct is on, your model name is checked first. (Also "permission", but I'm not sure what that means)

Now your key is verified with RSA.
Funnily enough, fastboot seems to keep track of the amount of times `verify` succeeded/failed
C:
log(0x80000000,(byte *)"The %d time fastboot unlock rsa verify fail\n",1,in_w3,in_w4,in_w5,in_w6,in_w7,unaff_x30);

Anyway, inside` verifyFastbootUnlockRsa` `verifyRsa` is called, even twice.
This is where X.509 comes in:
C:
if (DAT_00063378 == 0) {
    in_w2 = 'x';
    lVar8 = (**(code **)(DAT_00063310 + 0x140))(&DAT_0005de44,0);
    if (lVar8 != 0) {
      cVar2 = shouldPringMsg();
      if ((cVar2 != '\0') && (cVar2 = FUN_00035b70(-0x80000000), cVar2 != '\0')) {
        log(0x80000000,
                     (byte *)"RSA: Failed to LocateProtocol gEfiQcomASN1X509ProtocolGuid %r\n",
                     (char)lVar8,in_w3,in_w4,in_w5,in_w6,in_w7,
                     CONCAT44(in_stack_ffffffffffffff84,in_stack_ffffffffffffff80));
      }
      goto LAB_000107d4;
    }
  }

After getting the protocol, the check is performed. Here are the messages:

"GetDataHash: HashFinal unsuccessful! Status: %r\n"
"GetDataHash: HashUpdate unsuccessful!Status: %r\n"
"GetDataHash: HashInit unsuccessful! Status: %r\n"
"GetDataHash: Invalid size! HashSize: %d, DigestSize: %d\n"
"GetDataHash: GetHashSize unsuccessful! Status: %r\n"
"GetDataHash: LocateProtocol unsuccessful!Status: %r\n"
"VerifyHashWithRSASignature:Invalid pointer\n"
"VerifyHashWithRSASignature: mem allocation err for Key.N\n"
"VerifyHashWithRSASignature: mem allocation err for Key.e\n"
"VerifyHashWithRSASignature: SecRSAVerifySig success! Status: %r\n"
"VerifyHashWithRSASignature: SecRSAVerifySig failed! Status: %r\n"
"VerifyHashWithRSASignature: SecRSABigIntReadBin for Modulus failed! Status: %r\n"
"VerifyHashWithRSASignature: SecRSABigIntReadBin for Modulus failed!Status: %r\n"
"VerifyHashWithRSASignature: LocateProtocol failed, Status: %r\n"
"Failed to verify hash\n"
"Failed to get hash\n"

As you can tell by the messages, the method is pretty big.

Update:
I forgot that I shared the full method here: https://forum.xda-developers.com/t/...astboot-on-oppo-devices.4490041/post-88602875
 
Last edited:

Top Liked Posts

  • There are no posts matching your filters.
  • 3
    Admin: Please move/delete this thread if it is in the wrong place or against the rules.

    I wanted to create a thread to discuss unlocking fastboot mode on Oppo devices in general, rather than discussing it in terms of any one device in particular. The reason being is that there are currently little bits of information scattered here and there across various different device forums. I think it would be useful to have somewhere where we could pool our information on the subject.

    I will say at this point, I'm not sure what progress can be made, but I do think we could answer some of each other's questions and build a bigger picture of what is going on.

    One question I have for example, that I know someone out there will know, or be able to test, is if you have enabled engineer mode with the sec5 app, or otherwise, are you then able to invoke methods from the 'android.engineer.OplusEngineerManager' class without getting an selinux error?

    I have decompiled the deeptesting app and looked through the sources a little and found the method that unlocks fastboot mode. Its signature is as follows:

    fastbootUnlock(byte[ ] bArr, int i)

    The byte array is essentially formed as followed:

    a string is split into pairs of characters,
    each pair is a hex code that is converted to an int,
    the byte value of each of these integers is then stored in a byte array.

    The int that the fastbootUnlock method takes is simply the length of the byte array.

    I have a find x3 pro and have hit a bit of a brick wall in testing in that I cannot invoke methods from the 'android.engineer.OplusEngineerManager' class however I do suspect that with engineer mode enabled it may be possible to invoke methods from this class.

    If you have any information you feel may be relevant, any questions, or even just want to say hello, do not hesitate to post 🙂
    1
    So, no major breakthroughs to report but some stuff that may be of use to people.

    After hitting a bit of a brick wall disassembling the deeptesting and engineermode apks I have turn my attention to the system.

    Both these apks rely on custom services implemented by oppo (Although most files relating to them have oplus in the name)

    After loading up one of the service files '[email protected]' in ida I think I can see that the key required to unlock fastboot mode is stored on the odm partition in /odm/etc/DownloadModeKey/ (This is a little over my head but I can see multiple references to this)

    Also I have found an xml with a list of mmi codes, I don't know how much use it will be to anyone, but there are a couple in there that I dont believe have been documented elsewhere, so I will upload it here
    1
    I did some very cursory reverse-engineering of the deeptest app and basically came to the conclusion that it depends on the response from Oppo's servers. In a properly designed system (which the original Danger hiptop/T-Mobile Sidekick implemented) there's an unlock entitlement cryptographically signed by the OEM. I *assume* that's the case here, but I don't know for sure.
    1
    Hi! I'm trying a different approach, to spoof the device model so that the deep test.apk will do its thing.
    I have the realme gt 2 (EU) RMX3311 that can't be unlocked but the Indian version (RMX3312) can be unlocked. Some guys managed to change the region of the RMX3311 to India and the deep test apk allowed the bootloader to be unlocked. So, from what I've read, deep test reads build.prop and if it finds the right model it communicates with realme backend to receive the unlock code, and everything works...
    Now I'm trying to find in the deep test apk when it reads the device model and change that code so it accepts whatever it finds :) The problem is I can't understand smali source code :))
    1
    Small update:

    Decided to try and pull the DownloadModeKey from my Find X3 pro via adb, the operation was a success but sadly it just looks like an RSA public key so not much use.