注:本文所有图片保存EverNote 印象笔记(www.yinxiang.com)当中, 如果有其帐号登录后即可查看全部图片。 如没有帐号可用e-mail >>点击注册免费印象笔记
Linux 下解包华为固件包UPDATE.APP
一.关于华为固件包
最近因为需要分析一个特殊的Apk,需要从华为固件升级包抽取出来。
下载后发现华为固件包有两种,一种标准的update.zip卡刷包,这个用标准recovery 升级。解压后从system/app 直接拷贝出相应的apk即可。
另外一种是华为自家包格式dlload格式,每个升级包含配置文件和单个数据文件UPDATE.APP
华为官方的固件全部用这个格式发布,但是这种格式没有公开,因为很简单,很早就被人破解了。
这里三篇是讲如何在Windows下使用perl脚本解出system.img进而抽出其中文件来,思路都是一样的.
《[GUIDE] How to extract Huawei firmware (update.app)》
http://forum.xda-developers.com/showthread.php?t=2315547
《华为mate官方固件update.app专用解包工具及教程 (转)》
http://cn.club.vmall.com/thread-78177-1-1.html
《华为官方固件解包教程...提前官方系统程序》
http://tieba.baidu.com/p/2734923332
二.解析脚本
这几个都是使用split_updata.pl这个perl脚本来解开包。实践发现update.app 实际上是多个img文件合并成的。一般有10个文件。
对比其中不同产品spl_update.pl中的system.img 在不同位置上,这也是造成同一个脚本解析华为不同产品的system.img不对的原因, 在第一篇教程中有一个识别分区image文件脚本
HuaweiFinder https://docs.google.com/file/d/0B5LJgOGBjYOBLWlKcC1sOHkzRzA/edit?usp=sharing ,但是我无法下载,实际操作发现,不用这么复杂,通常尺寸最大是userdata.img,华为改名为cust.img ,第二大就是system.img 这两个通常有50M上下,1-3M大小是boot.img 即内核格式。
这个可以用工具快速测试。
use strict;
use warnings;
# Turn on print flushing.
$|++;
# Unsigned integers are 4 bytes.
use constant UINT_SIZE => 4;
# If a filename wasn't specified on the commmand line then
# assume the file to be unpacked is called "UPDATA.APP".
my $FILENAME = undef;
if ($#ARGV == -1) {
$FILENAME = "UPDATE.APP";
}
else {
$FILENAME = $ARGV[0];
}
open(INFILE, $FILENAME) or die "Cannot open $FILENAME: $!\n";
binmode INFILE;
# Skip the first 92 bytes, they're blank.
seek(INFILE, 92, 0);
# We'll dump the files into a folder called "output".
my $BASEPATH = "output/";
mkdir $BASEPATH;
# These filenames are guessed. Feel free to correct.
&dump_file($BASEPATH."1.img");
&dump_file($BASEPATH."2.img");
&dump_file($BASEPATH."3.img");
&dump_file($BASEPATH."4.img");
&dump_file($BASEPATH."5.img");
&dump_file($BASEPATH."6.img");
&dump_file($BASEPATH."7.img"); # 40byte header
&dump_file($BASEPATH."8.img");
&dump_file($BASEPATH."9.img");
&dump_file($BASEPATH."10.img");
&dump_file($BASEPATH."11.img");
&dump_file($BASEPATH."12.img"); # 40byte header
&dump_file($BASEPATH."13.img");
&dump_file($BASEPATH."14.img"); # 40byte header
close INFILE;
# Unpack a file block and output the payload to a file.
sub dump_file {
my ($outfilename) = @_;
my $buffer = undef;
# Verify the identifier matches.
read(INFILE, $buffer, UINT_SIZE);
unless ($buffer eq "\x55\xAA\x5A\xA5") {
die "Unrecognised file format. Wrong identifier.\n";
}
# Extract the packet length.
read(INFILE, $buffer, UINT_SIZE);
my ($packetLength) = unpack("V", $buffer);
# Ignore the next 16 bytes.
read(INFILE, $buffer, 16);
# Extract the length of the data file.
read(INFILE, $buffer, UINT_SIZE);
my ($dataLength) = unpack("V", $buffer);
# Ignore the rest of the packet. We've already read the first 28 bytes.
read(INFILE, $buffer, $packetLength-28);
# Dump the payload.
read(INFILE, $buffer, $dataLength);
open(OUTFILE, ">$outfilename") or die "Unable to create $outfilename: $!\n";
binmode OUTFILE;
print OUTFILE $buffer;
close OUTFILE;
# Ensure we finish on a 4 byte boundary alignment.
my $remainder = UINT_SIZE - (tell(INFILE) % UINT_SIZE);
if ($remainder < UINT_SIZE) {
# We can ignore the remaining padding.
read(INFILE, $buffer, $remainder);
}
print STDOUT "Extracted $outfilename\n";
}
不过我发现有一个C程序编译后也能达到同样效果
http://www.cnblogs.com/GentlemanMod/p/3272580.html
这里我修改一下源码,把itoa 改成snprintf,以便在linux可以编译通过
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define MAXLEN 10240
void usage();
int main(int argc,char *argv[])
{
int count,packetLength,dataLength,olddataLength,datasum,line,remainder,*Length;
char *FILENAME,*OUTNAME;
char int2char[10];
unsigned char buffer[MAXLEN];
FILE *INFILE,*OUTFILE;
//获取输入的参数
if(argc == 1)
FILENAME="UPDATE.APP";
else
FILENAME=argv[1];
//用二进制打开输入文件
if((INFILE = fopen(FILENAME, "rb")) == NULL) usage();
//创建文件夹并进入目录
mkdir("output");
chdir("output");
//跳过92空字节
fseek(INFILE, 92, 0);
for(count=1;INFILE != NULL;count++)
{
//判断是否为华为固件索引头
fread(buffer, 4, 1, INFILE);
if(buffer[0] != 0x55) break;
if(buffer[1] != 0XAA) break;
if(buffer[2] != 0x5A) break;
if(buffer[3] != 0xA5) break;
//获取头文件长度
fread(buffer, 4, 1, INFILE);
Length = (int *)buffer;
packetLength=*Length;
//跳过16字节
fseek(INFILE, 16, 1);
//获取内容长度
fread(buffer, 4, 1, INFILE);
Length = (int *) buffer;
dataLength=*Length;
//把整数和字符串连接并复制给文件名
#itoa(count, int2char, 10);
snprintf(int2char,sizeof(int2char),"%d",count);
OUTNAME=strcat(int2char, ".img");
//跳到头文件末尾
fseek(INFILE, packetLength-28, 1);
//创建文件
if((OUTFILE = fopen(OUTNAME, "wb")) == NULL)
{
printf("Unrecognised file format. Wrong identifier.\n");
return -1;
} else printf("Extracted output/%s\n",OUTNAME);
//把内容数据分成多个部分
datasum=dataLength/MAXLEN;
for(line=0;line <= datasum;line++)
{
//获取内容数据
if(datasum == line)
fread(buffer, dataLength % MAXLEN, 1, INFILE);
else
fread(buffer, MAXLEN, 1, INFILE);
//输出文件
if(datasum == line)
fwrite(buffer, dataLength % MAXLEN, 1, OUTFILE);
else
fwrite(buffer, MAXLEN, 1, OUTFILE);
}
//关闭输出文件
fclose(OUTFILE);
//指针取整,4的倍数
remainder = 4 - (ftell(INFILE) % 4);
if (remainder < 4)
{
//进行填充剩余的字节
fseek(INFILE, remainder, 1);
}
}
//关闭输入文件
fclose(INFILE);
return 0;
}
void usage()
{
//帮助函数
printf("uasge: unpack_update.exe [UPDATE.APP|UPDATA.APP]\n");
exit(0);
}
三.Linux下解包
前面教程都是因为windows下缺少必要工具perl解析器以及mount工具,才会如此大费周章,实际上Linux操作相当简单,只要传spl_update.pl 脚本即可。而且如果是一个专业的Android内核开发人员,用Linux比用windows机会更多吧。
-
解包
把spl_update.pl和UDPATE.APP放在一个目录下,执行
perl spl_update.pl UPDATE.APP -
挂载操作system.img
Linux下是可以用mount 直接把一个文件当成文件系统挂载的,假设3.img 是system.img格式,
直接用mount 命令
mount -t ext4 -o loop 3.img /mnt
以上命令把system.img 挂载到/mnt目录,打开这个目录/system的各种结构就是展示在你眼前!