[Android Q&A, Help & Troubleshooting] Vince_02100:
Hey guys :o

I have found a way to root this device and worked for me, and work certainly for all other un-rooted device.

For some curious, I explain below what I have doing to dump recovery partition and boot.

For other, you can find the ZIP link at the end of this post.

With the DirtyCow exploit, it's very easy to dump our boot or recovery partition but need a little time without a custom recovery working...

In a first step, read this : h**ps://github.com/jcadduono/android_external_dirtycow#running

(a big thank's to jcadduono ! ;))

Ok, it's need an Android OS Environnement and it's not for our device, but it's not realy a problem.

A little modification to this file < recowvery-applypatch.c > to dump our recovery or boot partition, like this :

#include <unistd.h>
#include <stdio.h>

#include <stdint.h>
#include <time.h>

#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <fcntl.h>
#include <sys/stat.h>

#define APP_NAME "recowvery"
#define HOST_NAME "applypatch"

#ifdef DEBUG
#include <android/log.h>
#define LOGV(...) { __android_log_print(ANDROID_LOG_INFO, APP_NAME, __VA_ARGS__); printf(__VA_ARGS__); printf("\n"); }
#define LOGE(...) { __android_log_print(ANDROID_LOG_ERROR, APP_NAME, __VA_ARGS__); fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
#else
#define LOGV(...) { printf(__VA_ARGS__); printf("\n"); }
#define LOGE(...) { fprintf(stderr, __VA_ARGS__); fprintf(stderr, "\n"); }
#endif

#define SEP LOGV("------------")

#include "bootimg.h"

/* Time delay in microsecond for next loop (1000 = 1ms)
* 1ms is good for every PC
* (you can try less than 1ms to speed the process, but you can have an <unexpected EOF>)
*/
#define DELAY_T	1000

/* For Medion LifeTab X10302 (May work on same hardware MT6735/MT6753)
* You can define BLOCK_EMMC to other path if needed.
* Use < adb shell mount > for help.
*/
#define BLOCK_EMMC	"/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/boot"
//#define BLOCK_EMMC	"/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/recovery"
//#define BLOCK_EMMC	"/dev/block/platform/any-folder-point-to-by-name-folder/by-name/boot"

void delay(long t)
{
long timens = t * 1000;
nanosleep((const struct timespec[]){{0, timens}}, NULL);
}

int main(int argc, char **argv)
{
int ret = 0;
int i = 0;

LOGV("Welcome to %s! (%s)", APP_NAME, HOST_NAME);

for (i = 30; i > 0; i--)
{
LOGV("The process start in %is", i);
sleep(1);
}

byte rb[32];
char *content = malloc(256);
FILE *filetest;
size_t nread;

filetest = fopen(BLOCK_EMMC, "r");
if (filetest) {
LOGV("*** DUMP START ***");
LOGV("* BLOCK_EMMC = %s", BLOCK_EMMC);
while ((nread = fread(rb, 1, sizeof rb, filetest)) > 0)
{
sprintf(content, "HEXDUMP = [");
for (i = 0; i < (int)nread; i++)
{
if (i == 0)
sprintf(content, "%s%.2x", content, rb[i]);
else
sprintf(content, "%s,%.2x", content, rb[i]);
}
sprintf(content, "%s];", content);
LOGV("%s", content);

/* sleep to prevent any unexpected EOF with with pipe stream
* ex : adb logcat | my-prg.exe
*/
delay(DELAY_T);
}

if (ferror(filetest)) {
ret = 1;
LOGE("*** DUMP ERROR ***");
LOGE("Error while reading the file...");
}

LOGV("*** DUMP END ***");
fclose(filetest);
}
else
{
LOGV("Can't read the file...");
ret = 1;
goto oops;
}

return 0;
oops:
LOGE("*** DUMP ERROR ***");
LOGE("Error %d: %s", ret, strerror(ret));
LOGE("Exiting...");
return ret;
}

(Edit : Added a delay in loop, there is some time an <unexpected EOF> during the process)

What it does ?

When launched by <recowvery-app_process>, it only open "/dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/boot" (in my exemple) in read-only and do like a HexDump, or something else.

It's human readable, and visible from Windows or any Linux distribution via the command adb logcat -s recowvery. But, we need something to do the revert :silly:

To solve this issue, I have made a to do this for me :cool:

C# code version : (not opti)

/*
* Created by SharpDevelop.
* User: vince
* Date: 16/01/2017
* Time: 17:34
*
* To change this template use Tools | Options | Coding | Edit Standard Headers.
*/
using System;
using System.IO;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace HexDumpReader
{
class Program
{
private static string workingDir = Path.GetDirectoryName(System.Reflection.Assembly.GetEntryAssembly().Location) + Path.DirectorySeparatorChar;
private static string outfile = null;
private static List<string> datalist = new List<string>();

public static int Main(string[] args)
{
int val = 0;
int ncrash = 0;

if (args.Length == 0 || Console.In.Peek() == -1)
{
DisplayUsage();
return 1;
}

try
{
// Shorter regex is possible, but I prefer like that.
// Used to start the process
Regex rs = new Regex("\\*\\*\\* DUMP START \\*\\*\\*");
// Used to match all data block, and populate < datalist >
Regex rl = new Regex("^\\d{2}-\\d{2} \\d{2}:\\d{2}:\\d{2}.\\d{3}\\s{1,2}\\d{3,5}\\s{1,2}\\d{3,5} I recowvery: HEXDUMP = \\[([^\\]].*)\\];$");
// Used to break the infinite loop and start parsing all data
Regex rf = new Regex("\\*\\*\\* DUMP END \\*\\*\\*");
// Used to intercept error from < recowvery-applypatch >
Regex re = new Regex("\\*\\*\\* DUMP ERROR \\*\\*\\*");

outfile = args[0];
bool start = false;
bool reboot = false;

/* If we want a reboot while finish */
if (args.Length > 1)
reboot = (args[1] == "-r");

while (true)
{
string line = Console.In.ReadLine();

/*
* If an unexpected EOF from recowvery-applypatch...
* We can't receive a null string, close fsout, set start to false and break the loop after 3 crashs.
*/
if (line == null)
{

Console.WriteLine("* < null > received !");
Console.WriteLine("Try again...");
break;
}

/*
* *** DUMP START ***
* set start = true, and init fsout FileStream
*/
if (rs.IsMatch(line))
start = true;

/*
* Add all matched group from HEXDUMP line to datalist.
* Note :
* It's possible to have matched line before intercept DUMP START,
* If we convert now, it's a good idea to have a broken output file.
* Important :
* Never try to parse all data on the fly, you can have a mysterious "unexpected EOF" error.
*/
if (start && rl.IsMatch(line))
{
datalist.Add(rl.Replace(line, "$1"));
Console.CursorLeft = 0;
Console.Write("Block read : " + datalist.Count);
}
/*
* Display the other lines (for debuging, logging...)
*/
else if (!rl.IsMatch(line) && (!rf.IsMatch(line) && !start) && line.Length > 1)
{
Console.WriteLine(line);
}

/*
* *** DUMP END ***
* Flush and close fsout, inform the user, and break the loop.
*/
if (start && rf.IsMatch(line))
break;

/*
* *** DUMP ERROR ***
* An error intercepted from ADB, close fsout, set start to false.
* < applypatch > will restart every 3 min.
* We break the loop after 3 errors.
*/
if (re.IsMatch(line))
{
Console.WriteLine("* Error received from ADB *");

start = false;
if (ncrash == 3)
{
Console.WriteLine("* Too many tries, please check your < recowvery-applypatch.c > and try again.");
break;
}
Console.WriteLine("* Be patient, recowvery-applypatch will restart in a few minutes.");
ncrash++;
}
}

Console.WriteLine();
Console.WriteLine("Parsing blocks...");

/* recowvery-applypatch read by block of 32 bytes.
* so, we can calculate the final size.
*/
double finalSize = 32 * datalist.Count;
/* used to display the new progression */
int lastprogress = 0;

FileStream fsout = File.OpenWrite(outfile);
fsout.SetLength(0);
foreach(string s in datalist)
{
string[] data = s.Split(',');

for (int c = 0; c < data.Length; c++)
{
try
{
byte[] b = StringToByteArrayFastest(data[c]);
fsout.Write(b, 0, b.Length);

/* Calculate progression */
int progress = (int)(((double)fsout.Position / finalSize) * 100);
/* Display the progression if needed */
if (progress != lastprogress)
{
Console.CursorLeft = 0;
Console.Write("Processing... " + progress + "% ");
lastprogress = progress;
}
}
catch(Exception ex)
{
Console.WriteLine();
Console.WriteLine("** Exception **");
Console.WriteLine(" - When convert : " + data[c]);
Console.WriteLine(" - Message : " + ex.Message);
Console.WriteLine(" - StackTrace : " + ex.StackTrace);
}
}
}
fsout.Flush();
fsout.Close();

Console.WriteLine();
Console.WriteLine("Finish !");

if (reboot)
rebootDevice();
else
Console.WriteLine("You can reboot your device.");
}
catch(Exception e)
{
Console.WriteLine("*** Exception ***");
Console.WriteLine(" - Message : " + e.Message);
Console.WriteLine(" - Source : " + e.Source);
Console.WriteLine(" - StackTrace : " + e.StackTrace);
val = e.HResult;
}

return val;
}

public static void DisplayUsage()
{
Console.WriteLine("Usage :");
Console.WriteLine(" adb logcat -s recowvery | " + System.Diagnostics.Process.GetCurrentProcess().ProcessName + ".exe myImage-output.img [-r]");
Console.WriteLine();
Console.WriteLine(" -r : Reboot your device when finish.");
Console.WriteLine(" (Run \"adb reboot\" for you)");
}

public static void rebootDevice()
{
ProcessStartInfo psi;
Process p;
string strout = "";
string strerr = "";

psi = new ProcessStartInfo("adb.exe", "reboot");
psi.WorkingDirectory = workingDir;
psi.RedirectStandardError = true;
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;

Console.WriteLine("Rebooting your device...");

p = new Process();
p.StartInfo = psi;
p.Start();

if (!p.StandardError.EndOfStream)
strerr = p.StandardError.ReadToEnd();
if (!p.StandardOutput.EndOfStream)
strout = p.StandardOutput.ReadToEnd();

if (strout.Length > 0)
Console.WriteLine(strout);
if (strerr.Length > 0)
{
Console.WriteLine(strerr);
}

p.WaitForExit();
}

public static byte[] StringToByteArrayFastest(string hex) {
if (hex.Length % 2 == 1)
throw new Exception("The binary key cannot have an odd number of digits");

byte[] arr = new byte[hex.Length >> 1];

for (int i = 0; i < hex.Length >> 1; ++i)
{
arr[i] = (byte)((GetHexVal(hex[i << 1]) << 4) + (GetHexVal(hex[(i << 1) + 1])));
}

return arr;
}

public static int GetHexVal(char hex) {
int val = (int)hex;
//For uppercase A-F letters:
//return val - (val < 58 ? 48 : 55);
//For lowercase a-f letters:
return val - (val < 58 ? 48 : 87);
//Or the two combined, but a bit slower:
//return val - (val < 58 ? 48 : (val < 97 ? 55 : 87));
}
}
}

To compile the C# code, you need :

An IDE like SharpDevelop, MonoDevelop, or VisualStudio

Create a <New Project> and select <Console Application>

copy the code below, and replace all to your main.cs or program.cs.

Run it

C++ code version : (Working good on Ubuntu 16.04, Windows too but not tested)

#include <iostream>
#include <regex>
#include <stdio.h>

typedef unsigned char byte;

using namespace std;

void DisplayUsage()
{
cout << string("Usage :") << endl;
cout << string(" adb logcat -s recowvery | ") << string("HexDumpReader") << string(".exe myImage-output.img [-r]") << endl;
cout << endl;
cout << string(" -r : Reboot your device when finish.") << endl;
cout << string(" (Run \"adb reboot\" for you)") << endl;
}

void split(const string &s, char delim, vector<string> &elems) {
stringstream ss;
ss.str(s);
string item;
while (getline(ss, item, delim)) {
elems.push_back(item);
}
}

vector<string> split(const string &s, char delim) {
vector<string> elems;
split(s, delim, elems);
return elems;
}

void string_to_bytearray(std::string str, unsigned char* &array, int& size)
{
int length = str.length();
// make sure the input string has an even digit numbers
if(length%2 == 1)
{
str = "0" + str;
length++;
}

// allocate memory for the output array
array = new unsigned char[length/2];
size = length/2;

std::stringstream sstr(str);
for(int i=0; i < size; i++)
{
char ch1, ch2;
sstr >> ch1 >> ch2;
int dig1, dig2;
if(isdigit(ch1)) dig1 = ch1 - '0';
else if(ch1>='A' && ch1<='F') dig1 = ch1 - 'A' + 10;
else if(ch1>='a' && ch1<='f') dig1 = ch1 - 'a' + 10;
if(isdigit(ch2)) dig2 = ch2 - '0';
else if(ch2>='A' && ch2<='F') dig2 = ch2 - 'A' + 10;
else if(ch2>='a' && ch2<='f') dig2 = ch2 - 'a' + 10;
array[i] = dig1*16 + dig2;
}
}

void rebootDevice()
{
system("adb reboot");
}

int main(int argc, char** argv)
{
/* Display help if no argument */
if (argc == 1)
{
DisplayUsage();
return 1;
}

cout << string("*** HexDumpReader ***") << endl;

int val = 0;
vector<string> datalist;
string outfile = string(argv[1]);
bool reboot = false;
int ncrash = 0;

/* Output image file */
cout << string("* Output file : ") << outfile << endl;
/* If we want a reboot while finish */
if (argc > 2)
{
reboot = (string(argv[2]) == "-r");
}
cout << string("* Reboot device : ") << reboot << endl;

try
{
// Shorter regex is possible, but I prefer like that.
// Used to start the process
regex rs("^.+I recowvery: (\\*\\*\\* DUMP START \\*\\*\\*)\\s+");
// Used to match all data block, and populate < datalist >
regex rl("^.+I recowvery: HEXDUMP = \\[([^\\]]+)\\];\\s+");
// Used to break the infinite loop and start parsing all data
regex rf("^.+I recowvery: (\\*\\*\\* DUMP END \\*\\*\\*)\\s+");
// Used to intercept error from < recowvery-applypatch >
regex re("^.+I recowvery: (\\*\\*\\* DUMP ERROR \\*\\*\\*)\\s+");

/* write or not to output file */
bool start = false;

FILE *fsout;
fsout = fopen(outfile.c_str(), "wb");
int nBlock = 0;
long currentSize = 0;

while (true)
{
string line;
getline(cin, line);

/*
* If an unexpected EOF from recowvery-applypatch or if no <pipe>...
* We can't receive a null string, so break the loop, close fsout, and exit the program.
*/
if (line.empty())
{
cout << string("* < null > received !") << endl;
cout << string("Try again...") << endl;
break;
}

/*
* *** DUMP START ***
* set start = true to write parsed data to fsout
*/
if (regex_match(line, rs))
{
start = true;
}

/*
* Parse all string received if match
* Note :
* It's possible to have matched string before intercept DUMP START,
* If we convert now, it's a good idea to have a broken output file.
*/
if (start && regex_match(line, rl))
{
string s = regex_replace(line, rl, "$1");
vector<string> data = split(s, ',');
for (int c = 0; c < (int)data.size(); c++)
{
try
{
byte *b = NULL;
int sb;
string_to_bytearray(data[c], b, sb);
fwrite(b, 1, sb, fsout);
}
catch (const std::exception &ex)
{
std::cout << std::endl;
std::cout << std::string("** Exception **") << std::endl;
std::cout << std::string(" - When convert : ") << data[c] << std::endl;
std::cout << std::string(" - Message : ") << ex.what() << std::endl;
}
}
nBlock++;
currentSize = nBlock * 32;

std::cout << "\r";
std::cout << std::string("Block read : ") << nBlock << string(" (Size : ") << currentSize << string(")");
}
/*
* Display the other lines (for debuging, logging...)
*/
else if (!regex_match(line, rl) && (!regex_match(line, rf) && !start) && line.length() > 1)
{
std::cout << line << std::endl;
}

/*
* *** DUMP END ***
* Flush and close fsout, inform the user, and break the loop.
*/
if (start && regex_match(line, rf))
{
break;
}

/*
* *** DUMP ERROR ***
* An error intercepted from ADB, close fsout, set start to false.
* < applypatch > will restart every 3 min.
* We break the loop after 3 errors.
*/
if (regex_match(line, re))
{
cout << std::string("* Error received from ADB *") << std::endl;

start = false;
if (ncrash == 3)
{
cout << std::string("* Too many tries, please check your < recowvery-applypatch.c > and try again.") << std::endl;
break;
}
cout << std::string("* Be patient, recowvery-applypatch will restart in a few minutes.") << std::endl;
ncrash++;
}
}

cout << endl;

fclose(fsout);

cout << std::endl;
cout << std::string("Finish !") << std::endl;

if (reboot)
{
rebootDevice();
}
else
{
std::cout << std::string("You can reboot your device.") << std::endl;
}
}
catch (const std::exception &e)
{
std::cout << std::string("*** Exception ***") << std::endl;
std::cout << std::string(" - Message : ") << e.what() << std::endl;
}

return val;
}

To build it, you need these CFLAGS : -m32 -std=C++11 -Wall (maybe -g for debug)

Ex :

g++ -Wall -fexceptions -g -Wall -std=c++11 -m32 -g -c main.cpp -o main.o

g++ -o HexDumpReader main.o -m32

Now, how It work ?

How can I dump my boot or recovery partition and make my own TWRP ? :crying:

It's like jcadduono explain here : h**ps://github.com/jcadduono/android_external_dirtycow

Follow only this steps :

adb push dirtycow /data/local/tmp
adb push recowvery-applypatch /data/local/tmp
adb push recowvery-app_process64 /data/local/tmp
adb push recowvery-run-as /data/local/tmp

adb shell

$ cd /data/local/tmp
$ chmod 0777 *
$ ./dirtycow /system/bin/applypatch recowvery-applypatch
"<wait for completion>"
$ ./dirtycow /system/bin/app_process64 recowvery-app_process64
"<wait for completion, your phone will look like it's crashing>"
$ exit

(It's change here =>)
adb logcat -s recowvery | HexDumpReader %USERPROFILE%\Documents\my-image-output.img -r

Wait a moment, you will see that it's finish, and you may see <my-image-output.img> in <%USERPROFILE%\Documents> folder.

You can try to extract your image from MKTool or any Kitchen tools, it's work, and finally make your TWRP, boot from it, backup your roms (if you have never doing this) and finally root your device.

I know that some of you can't do all these steps, or need simply the final image, you can download it from this url :

ZIP File : h**ps://goo.gl/IT7dpl (Contains original boot, recovery, TWRP & a readme)

(Bug found : battery stuck at 50%)