相关文章
MTK Logo 逆向解析之 bin 转 rawx
MTK Logo 逆向解析之 rawx 全解压
终于来到最后一步啦,将 raw 转换为原始 bmp 文件,也就是我们在 alps\vendor\mediatek\proprietary\bootable\bootloader\lk\dev\logo
文件夹中对应的原始文件,掌握了这个就可以提取设备中的开机logo文件解析
话说在最后这个阶段卡了两星期吧,毕竟我的 c++ 很菜,都是边查边粘贴,总是卡在数据旋转那块,
最终还是通过群友的 AI 大法搞定了,还提供了三种版本的转换方式。话不多说,先来看看源码
源码路径
vendor\mediatek\proprietary\bootable\bootloader\lk\dev\logo\tool\bmp_to_raw_dithering\
main.cpp 运行入口 仅关注这个
bmpdecoderhelper.cpp 标准 bmp 解析工具类,安装源码中默认也有
bmpdecoderhelper.h 头文件
Dither.c 没用到
整体代码很简单,就是读取 bmp 文件大小,解析 bmp 数据,按照 ARGB8888 格式存储到 raw 文件中
反解析只需要将流程逆过来就行
#include <iostream>
#include <fstream>
#include <vector>
#include "bmpdecoderhelper.h"
using namespace std;
using namespace image_codec;
//g++ -m32 main.cpp bmpdecoderhelper.cpp -o bmp_to_raw
// -------------------------------------------------------------------------------------
class BmpToLogo : public BmpDecoderCallback
{
public:
BmpToLogo() : BmpDecoderCallback(), m_width(0), m_height(0) {}
bool DoBmpToLogo(const char *bmpFilename, const char *logoFilename, bool append)
{
// Open input BMP file
ifstream bmpFile;
bmpFile.open(bmpFilename, ios::in | ios::binary);
if (!bmpFile.is_open()) {
cout << "open " << bmpFilename << " failed!" << endl;
return false;
}
// Open output LOGO file
ofstream logoFile;
ios_base::openmode outMode = (ofstream::out | ofstream::binary);
if (append) outMode |= ofstream::app;
logoFile.open(logoFilename, outMode);
if (!logoFile.is_open()) {
cout << "open " << logoFilename << " failed!" << endl;
return false;
}
// Read in BMP file content
int bmpFileSize = GetFileSize(bmpFile);
m_bitstream.resize(bmpFileSize);
bmpFile.read(&m_bitstream[0], m_bitstream.size());
// Decode BMP file to destination buffer
BmpDecoderHelper bmpDec;
bmpDec.DecodeImage(&m_bitstream[0], m_bitstream.size(), INT_MAX, this);
// ConvertToRGB565();
// ConvertToRGB565Dither();
// Convert RGB888 buffer to RGB565
ConvertToARGB8888();
// Write to LOGO file
// logoFile.write(&m_rgb565Buffer[0], m_rgb565Buffer.size());
logoFile.write(&m_argb8888Buffer[0], m_argb8888Buffer.size());
// Close Files
bmpFile.close();
logoFile.close();
return true;
}
virtual uint8* SetSize(int width, int height)
{
m_width = (uint32)width;
m_height = (uint32)height;
m_rgb888Buffer.resize(width * height * 3);
m_rgb565Buffer.resize(width * height * 2);
m_argb8888Buffer.resize(width * height * 4);
return (uint8 *) &m_argb8888Buffer[0];
}
private:
int GetFileSize(ifstream& file)
{
ios::pos_type backup = file.tellg();
file.seekg(0, std::ios::end);
ios::pos_type size = file.tellg();
file.seekg(backup, std::ios::beg);
return (int)size;
}
void ConvertToRGB565()
{
uint8 *rgb888 = (uint8*) &m_rgb888Buffer[0];
uint16 *rgb565 = (uint16*) &m_rgb565Buffer[0];
uint32 R, G, B;
for(uint32 i = 0; i < m_width * m_height; ++ i) {
R = rgb888[0]; G = rgb888[1]; B = rgb888[2];
*rgb565 = ((R & 0xF8) << 8) | ((G & 0xFC) << 3) | ((B & 0xF8) >> 3);
++ rgb565;
rgb888 += 3;
}
}
void ConvertToARGB8888()
{
uint8 *rgb888 = (uint8*) &m_rgb888Buffer[0];
uint32 *argb8888 = (uint32*) &m_argb8888Buffer[0];
uint32 R, G, B;
for(uint32 i = 0; i < m_width * m_height; ++ i) {
R = rgb888[0];
G = rgb888[1];
B = rgb888[2];
*argb8888 = (0xFF << 24) | (R << 16) | (G << 8) | (B);
++argb8888;
rgb888 += 3;
}
}
#define CLIP_255(x) ((x)>255?255:(x))
void ConvertToRGB565Dither(void)
{
unsigned short DitherMatrix_3Bit_16[4] = {0x5140, 0x3726, 0x4051, 0x2637};
unsigned int count = 0;
unsigned int sr,sg,sb;
unsigned int x, y;
unsigned short dither_scan = 0;
uint8 *src= (uint8*) &m_rgb888Buffer[0];
uint16 *dst= (uint16*) &m_rgb565Buffer[0];
for(count = 0;count < m_width*m_height;count++)
{
x = count % m_width;
y = count / m_width;
dither_scan = DitherMatrix_3Bit_16[(y) & 3];
unsigned short dither = ((dither_scan >> (((x) & 3) << 2)) & 0xF);
sr = ((CLIP_255(((*(src+0) + dither - (*(src+0) >> 5)))) >> (8 - 5)));
sg = ((CLIP_255(((*(src+1) + (dither >> 1) - (*(src+1) >> 6)))) >> (8 - 6)));
sb = ((CLIP_255(((*(src+2) + dither - (*(src+2) >> 5)))) >> (8 - 5)));
printf("%3d,%3d,%3d|%3d,%3d,%3d|%3d,%3d,%3d|%d,%d|%d,%d\n",
(sr<<3)-*(src+0), (sg<<2)-*(src+1),(sb<<3)-*(src+2),
sr<<3,sg<<2,sb<<3,
*(src+0), *(src+1),*(src+2),
x,y,
dither_scan, dither);
src += 3;
*dst++ = ((uint16_t)((sr << (5 + 6)) | (sg << (5)) | (sb << 0)));
}
}
private:
vector<char> m_bitstream;
vector<char> m_rgb888Buffer;
vector<char> m_argb8888Buffer;
vector<char> m_rgb565Buffer;
uint32 m_width, m_height;
};
// -------------------------------------------------------------------------------------
int main(int argc, const char* argv[])
{
if (argc < 3) {
cout << endl << "[Usage] bmp_to_logo logofile bmpfile1 [bmpfile2] ..." << endl << endl;
cout << "Example: bmp_to_logo fhd.raw fhd_uboot.bmp fhd_kernel.bmp ..." << endl << endl;
return -1;
}
const char *logo_filename = argv[1];
const char *bmp_filename = argv[2];
BmpToLogo bmpToLogo;
if (!bmpToLogo.DoBmpToLogo(bmp_filename, logo_filename, false)) {
return -2;
}
for (int i = 3; i < argc; ++ i) {
bmp_filename = argv[i];
if (!bmpToLogo.DoBmpToLogo(bmp_filename, logo_filename, true)) {
return -2;
}
}
}
以下代码中宽、高值都需要替换为你自己的正确值,不然会转换不正常
rawmain.cpp
#include <iostream>
#include <fstream>
#include <vector>
using namespace std;
#pragma pack(push, 1)
typedef struct BITMAPFILEHEADER {
unsigned short bfType;
unsigned int bfSize;
unsigned short bfReserved1;
unsigned short bfReserved2;
unsigned int bfOffBits;
} BITMAPFILEHEADER;
typedef struct BITMAPINFOHEADER {
unsigned int biSize;
int biWidth;
int biHeight;
unsigned short biPlanes;
unsigned short biBitCount;
unsigned int biCompression;
unsigned int biSizeImage;
int biXPelsPerMeter;
int biYPelsPerMeter;
unsigned int biClrUsed;
unsigned int biClrImportant;
} BITMAPINFOHEADER;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef uint16_t uint16;
typedef uint8_t uint8;
typedef uint32_t uint32;
vector<char> m_bitstream;
vector<char> m_rgb888Buffer;
uint32 m_width = 800;
uint32 m_height = 1280;
#pragma pack(pop)
int GetFileSize(ifstream& file)
{
ios::pos_type backup = file.tellg();
file.seekg(0, std::ios::end);
ios::pos_type size = file.tellg();
file.seekg(backup, std::ios::beg);
return (int)size;
}
/*void ConvertToARGB8888()
{
uint8 *rgb888 = (uint8*) &m_rgb888Buffer[0];
uint32 *argb8888 = (uint32*) &m_argb8888Buffer[0];
uint32 R, G, B;
for(uint32 i = 0; i < m_width * m_height; ++ i) {
R = rgb888[0];
G = rgb888[1];
B = rgb888[2];
*argb8888 = (0xFF << 24) | (R << 16) | (G << 8) | (B);
++argb8888;
rgb888 += 3;
}
}*/
void ConvertFromARGB8888() {
// uint32 *argb8888 = (uint32*) &m_argb8888Buffer[0];
uint32 *argb8888 = (uint32*) &m_bitstream[0];
uint8 *rgb888 = (uint8*) &m_rgb888Buffer[0];
uint32 R, G, B, A;
//数据左右翻转了
for(uint32 i = m_width * m_height - 1; i > 0; --i) {
// A = argb8888[i] >> 24;
R = (argb8888[i] >> 16);
G = (argb8888[i] >> 8);
B = argb8888[i];
rgb888[0] = B;
rgb888[1] = G;
rgb888[2] = R;
rgb888 += 3;
}
}
void ConvertFromARGB88882() {
uint32 *argb8888 = (uint32*) &m_bitstream[0];
uint8 *rgb888 = (uint8*) &m_rgb888Buffer[0];
uint32 R, G, B;
//bmp格式的位图区存储顺序是从下到上,从左到右
for(int y = m_height - 1; y >= 0; --y) { // 从最后一行开始,向上遍历
for(uint32 x = 0; x < m_width; ++x) { // 从每行的第一个像素开始,向右遍历
uint32 i = y * m_width + x;
R = (argb8888[i] >> 16) & 0xFF;
G = (argb8888[i] >> 8) & 0xFF;
B = argb8888[i] & 0xFF;
rgb888[0] = B;
rgb888[1] = G;
rgb888[2] = R;
rgb888 += 3;
}
}
}
int main(int argc, const char* argv[]) {
if (argc < 3) {
cout << endl << "[Usage] raw_to_bmp rawfile bmpfile " << endl << endl;
cout << "Example: raw_to_bmp logo.raw logo.bmp " << endl << endl;
return -1;
}
const char *rawFilename = argv[1];
ifstream rawFile;
rawFile.open(rawFilename, ios::in | ios::binary);
if (!rawFile.is_open()) {
cout << "open " << rawFilename << " failed!" << endl;
return -1;
}
int rawFileSize = GetFileSize(rawFile);
m_bitstream.resize(rawFileSize);
rawFile.read(&m_bitstream[0], m_bitstream.size());
m_rgb888Buffer.resize(m_width * m_height * 3);
// m_argb8888Buffer.resize(width * height * 4);
ConvertFromARGB88882();
// 创建BMP文件头和信息头
BITMAPFILEHEADER fileHeader = {0x4D42, 54 + m_bitstream.size(), 0, 0, 54};
BITMAPINFOHEADER infoHeader = {40, m_width, m_height, 1, 24, 0, m_bitstream.size(), 0, 0, 0, 0};
// 创建BMP文件并写入头和数据
const char *bmpFilename = argv[2];
ofstream outputFile(bmpFilename, ios::binary);
if (!outputFile) {
cout << "open " << bmpFilename << " failed!" << endl;
return -1;
}
outputFile.write(reinterpret_cast<char*>(&fileHeader), sizeof(fileHeader));
outputFile.write(reinterpret_cast<char*>(&infoHeader), sizeof(infoHeader));
// outputFile.write(data.data(), data.size());
// outputFile.write(&m_bitstream[0], m_bitstream.size());
outputFile.write(&m_rgb888Buffer[0], m_rgb888Buffer.size());
outputFile.close();
return 0;
}
g++ -m32 rawmain.cpp -o raw_to_bmp
raw_to_bmp下载
使用方法
[Usage] raw_to_bmp rawfile bmpfile
Example: raw_to_bmp logo.raw logo.bmp
raw_to_bmp.py
# coding=UTF-8
import struct
#使用方法
#python raw_to_bmp.py input.raw
# 图像的宽度和高度
WIDTH = 800
HEIGHT = 1280
# BMP文件头和信息头的大小
FILE_HEADER_SIZE = 14
INFO_HEADER_SIZE = 40
def create_bmp_header(width, height):
# 计算图像数据大小
data_size = width * height * 3 # RGB888
# 创建BMP文件头
file_header = struct.pack('<2sIHHI', b'BM', FILE_HEADER_SIZE + INFO_HEADER_SIZE + data_size, 0, 0, FILE_HEADER_SIZE + INFO_HEADER_SIZE)
# 创建BMP信息头
info_header = struct.pack('
使用方法
python raw_to_bmp.py input.raw
RawToBitMap.java
/**
* Created by cczheng on 2023/9/14.
*
* https://www.cnblogs.com/chenrui7/p/4561020.html
*
* https://www.bilibili.com/read/cv15459784/
*/
public class RawToBitMap {
/**
* 从流中读取数组
*
* @param stream 输入流
* @return
*/
public static byte[] readByteArrayFormStream(InputStream stream) {
try {
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
int len = 0;
byte[] tmp = new byte[1024];
while ((len = stream.read(tmp)) != -1) {
outStream.write(tmp, 0, len);
}
byte[] data = outStream.toByteArray();
Log.d("bmp", "data length=" + data.length);
return data;
} catch (IOException e) {
e.printStackTrace();
return new byte[0];
}
}
/**
* 8位灰度转Bitmap
*
* 图像宽度必须能被4整除
*
* @param data 裸数据
* @param width 图像宽度
* @param height 图像高度
* @return
*/
public static Bitmap convert8bit(byte[] data, int width, int height) {
byte[] Bits = new byte[data.length * 4]; //RGBA 数组
int i;
for (i = 0; i < data.length; i++) {
// 原理:4个字节表示一个灰度,则RGB = 灰度值,最后一个Alpha = 0xff;
Bits[i * 4] = Bits[i * 4 + 1] = Bits[i * 4 + 2] = data[i];
Bits[i * 4 + 3] = -1; //0xff
}
// Bitmap.Config.ARGB_8888 表示:图像模式为8位
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bmp.copyPixelsFromBuffer(ByteBuffer.wrap(Bits));
return bmp;
}
/**
* 24位灰度转Bitmap
*
* 图像宽度必须能被4整除
*
* @param data 裸数据
* @param width 图像宽度
* @param height 图像高度
* @return
*/
public static Bitmap convert24bit(byte[] data, int width, int height) {
int i;
/*void ConvertToARGB8888()
{
uint8 *rgb888 = (uint8*) &m_rgb888Buffer[0];
uint32 *argb8888 = (uint32*) &m_argb8888Buffer[0];
uint32 R, G, B;
for(uint32 i = 0; i < m_width * m_height; ++ i) {
R = rgb888[0];
G = rgb888[1];
B = rgb888[2];
*argb8888 = (0xFF << 24) | (R << 16) | (G << 8) | (B);
++argb8888;
rgb888 += 3;
}
}*/
/*int[] iData = new int[data.length / 3]; //RGBA 数组// data.length / 3 表示 3位为一组
for (i = 0; i < data.length / 3; i++) {// 原理:24位是有彩色的,所以要复制3位,最后一位Alpha = 0xff;
// iData[i] = (((int) data[i * 3]) << 16) + (((int) data[i * 3 + 1]) << 8) + data[i * 3 + 2] + 0xff000000;
// iData[i] = (((int) data[i * 3]) >> 16) + (((int) data[i * 3 + 1]) >> 8) + data[i * 3 + 2] + 0xff000000;
iData[i] = data[i * 3] | 0xff + (((int) data[i * 3 + 1] | 0xff) >> 8) + (((int) data[i * 3 + 2] | 0xff ) >> 16) +0xff;
}
Bitmap bmp = Bitmap.createBitmap(iData, width, height, Bitmap.Config.ARGB_8888);*/
byte[] Bits = new byte[data.length * 3];
byte A, R, G, B;
for (i = 0; i < data.length / 3; i++) {
R = data[i * 3];
G = data[i * 3 + 1];
B = data[i * 3 + 2];
// A = -1;
//int color = (A & 0xff) << 24 | (B & 0xff) << 16 | (G & 0xff) << 8 | (R & 0xff);
Bits[i * 3] = R;
Bits[i * 3 + 1] = G;
Bits[i * 3 + 2] = B;
// Bits[i * 3 + 3] = A;
}
Bitmap bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
bmp.copyPixelsFromBuffer(ByteBuffer.wrap(Bits));
return bmp;
}
private static byte[] parseRGBByte(byte argb8888) {
byte[] rdata = new byte[4];
rdata[0] = (byte) ((argb8888 >> 24) & 0xFF); // 获取A通道的值
rdata[1] = (byte) ((argb8888 >> 16) & 0xFF); // 获取R通道的值
rdata[2] = (byte) ((argb8888 >> 8) & 0xFF); // 获取G通道的值
rdata[3] = (byte) ((argb8888) & 0xFF); // 获取B通道的值
return rdata;
}
/**
* 8位灰度转Bitmap
*
* @param stream 输入流
* @param width 图像宽度
* @param height 图像高度
* @return
*/
public static Bitmap convert8bit(InputStream stream, int width, int height) {
return convert8bit(readByteArrayFormStream(stream), width, height);
}
/**
* 24位灰度转Bitmap
*
* @param stream 输入流
* @param width 图像宽度
* @param height 图像高度
* @return
*/
public static Bitmap convert24bit(InputStream stream, int width, int height) {
Bitmap bitmap = convert24bit(readByteArrayFormStream(stream), width, height);
saveBmp2Sdcard(bitmap);
return bitmap;
}
private static void saveBmp2Sdcard(Bitmap bitmap) {
File mSubFolder = new File( "/sdcard/DCIM/");
if (!mSubFolder.exists()) {
mSubFolder.mkdir();
}
String s = "test.png";
File f = new File(mSubFolder.getAbsolutePath(), s);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(f);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用方法
try {
Bitmap bitmap = RawToBitMap.convert24bit(getAssets().open(“input.raw”), 800, 1280);
imageView.setImageBitmap(bitmap);
} catch (IOException e) {
e.printStackTrace();
}