Starting point for Nokia 1

As promised, I’m starting the “Hack The One” cycle. And the first chapter consists of the analysis of what people already have found out about its internals.

So, after over half a year of non-usage I’ve factory-reset my Nokia 1 (TA-1047) and run the full cycle of loads of firmware updates to finally get the most recent stock system. Meanwhile, I collected quite a bit of information from different forums.

Most promising aspects are:

  1. Unlockable fastboot (see below).
  2. Allegedly working TWRP build installable or bootable via unlocked fastboot.

Most visible obstacles are:

  1. Inability to install stock updates with modified system/recovery images.
  2. Faildows-only SP FlashTool and NVRAM manipulation tooling like MauiMETA.
  3. No engineering menu app in the firmware.
  4. Stock recovery is just missing (open android image with “No command” string).

Known codes (besides *#06#, of course):

  • *#*#4636#*#* (*#*#INFO#*#*) - standard Android secret menu;
  • *#*#372#*#*, *#*#372733#*#* - FQC test menus.

Further material in this article and other posts of this series assumes that you have ADB, Fastboot and brain.

Unlocking fastboot

The procedure to unlock fastboot (which is your only interface for partition manipulation if you are not on Faildows) is a bit harder than for other MediaTeks. But the way to do this is pretty much official. The only things to beware are that it:

  • wipes your data (performs pretty much another factory reset);
  • adds a nagging bootscreen message like this:
1
2
3
4
Orange State

Your device has been unlocked and can't be trusted
Your device will boot in 5 seconds

Anyway, the process consists of the following steps:

  1. Enable developer menu by going to Settings - System - About phone and tap 7 times on the “Build number” menu item. Now go one level back and “Developer options” menu should appear in Settings - System.
  2. In the “Developer options” menu, enable the “OEM unlocking” switch. Tap “Enable” on the warning popup. This will enable fastboot oem unlock command.
  3. In the “Developer options” menu, enable the “USB debugging” switch. Tap “OK” on the warning popup. This will enable ADB. You can also uncheck the “Verify apps over USB” switch.
  4. Connect the phone in the unlocked state and run adb devices. Accept the authorization. The adb devices output should show a string starting with FRTBA.... That’s your device serial number you’ll need to use in the next step.
  5. Calculate the OEM key by taking the MD5 hash of the serial number. That can be done in different ways and produce different results, so here’s the correct command to calculate it: echo -n [your_serial] | md5sum | cut -d ' ' -f1. E.g. the command echo -n FRTBA81106006136 | md5sum | cut -d ' ' -f1 would give a0f0f9456ec031d39fcc9d47600128f8.
  6. Reboot the phone to fastboot mode by running adb reboot bootloader.
  7. Enter the key calculated on step 4 by running fastboot oem key [your_key], e.g. fastboot oem key a0f0f9456ec031d39fcc9d47600128f8.
  8. Finally, run fastboot oem unlock. When it prompts you to press Volume Up, just press it. If no prompt appears, replug the cable and try this step again.

On the PC side, both commands from the steps 6 and 7 should respond with something like OKAY. Then you should reboot with fastboot reboot and complete the initial system setup after this factory reset. Then you’ll have to re-enable developer menu and ADB as described in steps 0 and 2.

But afterwards, when you reboot into the bootloader with adb reboot bootloader, you’ll be able to use commands like fastboot boot and fastboot flash.

Unfortunately, the TWRP build I shared is not bootable with fastboot boot twrp323.img, as it just leads to crash and reboot to normal mode. I’ll probably have to try this with an SD card inserted and see if the behavior is of any difference.

AT command interface

This is something that needs to be researched on in the next chapter. From the general information I managed to collect, there are two primary interfaces: /dev/radio/atci1 for SIM1 and /dev/radio/atci2 for SIM2. Of course, these interfaces are only available from the root shell, and we don’t have any ways to achieve temporary root on Nokia 1 yet. However, MTK Engineer mode in the OEM handsets somehow manages to work without root permissions. And this leak just proves the way really exists, we just need to highlight the essential code, which, in my humble and honest opinion, is:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static final int EVENT_AT_CMD_DONE                 = 1003;
//...
if (mUserMode && (atCmdLine.toUpperCase()).startsWith("AT+EGMR=1")) {
showInfo("This command is not allowed in UserBuild");
return;
} else if (mIsSecurity && (atCmdLine.toUpperCase()).startsWith("AT+EGMR=1")) {
showInfo("This command is not allowed in A1 project");
return;
} else if (mMultiSimVariants == MultiSimVariants.DSDA &&
(atCmdLine.toUpperCase()).startsWith("AT+EGMR=1,10")) {
showInfo("IMEI_WARNING_MSG");
return;
}

try {
byte[] rawData = atCmdLine.getBytes();
byte[] cmdByte = new byte[rawData.length + 1];
System.arraycopy(rawData, 0, cmdByte, 0, rawData.length);
cmdByte[cmdByte.length - 1] = 0;
mGsmPhone.invokeOemRilRequestRaw(cmdByte,
mHandler.obtainMessage(EVENT_AT_CMD_DONE));
} catch (NullPointerException ee) {
ee.printStackTrace();
}

This means several things to us:

  1. CdsInfo has the restrictions to run the very command we’re interested in (starting with AT+EGMR=1) in the user build. For our case, this means we are lucky that this restriction is imposed on such a high level.
  2. The necessary call is the invokeOemRilRequestRaw method of a Phone instance (com.android.internal.telephony.Phone) which can be obtained by calling a PhoneFactory method (com.android.internal.telephony.PhoneFactory). The method accepts null-terminated bytestring.

So, here’s the proposed concept of a cleaned-up way to access AT commands in the “fire-and-forget” mode (which should be enough for our purposes) and a basic IMEI changer API on top of this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import android.telephony.TelephonyManager;
import android.telephony.TelephonyManager.MultiSimVariants;
import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;
import android.os.Handler;
import android.os.AsyncResult;
import android.app.AlertDialog;
import android.util.Log;

public class HackTheOne {

private static final int EVENT_AT_CMD_DONE = 1003;
private static final String TAG = "HackTheOne/AT";
private static final String INFO_TITLE = "HackTheOne";
private MultiSimVariants variant = TelephonyManager.getMultiSimConfiguration();

private void runAT(int phoneId, String atCmd) {
if(phoneId < 0 || phoneId >=TelephonyManager.getDefault().getPhoneCount())
phoneId = 0;
Phone phoneInstance = PhoneFactory.getPhone(phoneId);
byte[] rawData = atCmd.getBytes();
byte[] cmdByte = new byte[rawData.length + 1];
System.arraycopy(rawData, 0, cmdByte, 0, rawData.length);
cmdByte[cmdByte.length - 1] = 0;
phoneInstance.invokeOemRilRequestRaw(cmdByte, atHandler.obtainMessage(EVENT_AT_CMD_DONE));
}

public void writeIMEI(int simId, String newImei) { //simId can be 0 or 1 or 2, no 4-SIM setups are known but included here
if(simId < 0 || simId > 3) simId = 0;
int[] egmrs = {7,10,11,12};
int phoneId = 0;
String egmrId = Integer.toString(egmrs[simId]);
if(variant == MultiSimVariants.DSDA) {
egmrId = "7";
phoneId = simId;
}
String atCmd = "AT+EGMR=1," + egmrId + ",\"" + newImei + "\"";
runAT(phoneId, atCmd);
}

private Handler atHandler = new Handler() {
public void handleMessage(Message msg) {
AsyncResult ar;
if(msg.what == EVENT_AT_CMD_DONE) {
ar = (AsyncResult) msg.obj;
if (ar.exception != null) {
Log.i(TAG, "The response of command is failed");
showInfo("Failed to send the command");
} else {
try {
byte[] rawData = (byte[]) ar.result;
Log.i(TAG, "HexDump:" + HexDump.dumpHexString(rawData));
String txt = new String(rawData, "UTF-8");
Log.i(TAG, "The response is " + txt);
showInfo("AT command is sent:" + txt);
} catch (NullPointerException e) {
showInfo("Something is wrong");
e.printStackTrace();
} catch (UnsupportedEncodingException ee) {
ee.printStackTrace();
}
}
}
}
};

private void showInfo(String info) {
if (isFinishing()) return;
AlertDialog.Builder infoDialog = new AlertDialog.Builder(this);
infoDialog.setTitle(INFO_TITLE);
infoDialog.setMessage(info);
infoDialog.setIcon(android.R.drawable.ic_dialog_alert);
infoDialog.show();
}

}

As you can see, nothing except standard Android classlibs was used for this snippet.
But I haven’t tested it at all yet. So, whether it works or not, we’ll find out in the next chapter. Stay tuned!

_