MTK Logo 逆向解析之 raw 转 bmp

相关文章

MTK Logo 逆向解析之 bin 转 rawx

MTK Logo 逆向解析之 rawx 全解压

终于来到最后一步啦,将 raw 转换为原始 bmp 文件,也就是我们在 alps\vendor\mediatek\proprietary\bootable\bootloader\lk\dev\logo

文件夹中对应的原始文件,掌握了这个就可以提取设备中的开机logo文件解析

话说在最后这个阶段卡了两星期吧,毕竟我的 c++ 很菜,都是边查边粘贴,总是卡在数据旋转那块,

最终还是通过群友的 AI 大法搞定了,还提供了三种版本的转换方式。话不多说,先来看看源码

1、分析源码

源码路径

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;
        }
    }
}

以下代码中宽、高值都需要替换为你自己的正确值,不然会转换不正常

2、c++ 版本源码

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;  
}

2.1、编译指令

g++ -m32 rawmain.cpp -o raw_to_bmp

raw_to_bmp下载

2.2、回转 bmp 文件

使用方法

[Usage] raw_to_bmp rawfile bmpfile

Example: raw_to_bmp logo.raw logo.bmp

3、python 版本源码

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('<IiiHHIIIIII', INFO_HEADER_SIZE, width, height, 1, 24, 0, data_size, 0, 0, 0, 0)

    return file_header + info_header

def convert_argb8888_to_rgb888(data):
    # 从ARGB8888格式提取RGB数据
    rgb_data = bytearray()
    for y in range(HEIGHT-1, -1, -1):  # 从最后一行开始,向上遍历
        row_start = y * WIDTH * 4
        for x in range(WIDTH):  # 从每行的第一个像素开始,向右遍历
            pixel_pos = row_start + x * 4
            a, r, g, b = data[pixel_pos:pixel_pos+4]
            rgb_data.extend([b, r, g])  # 注意这里的顺序是BRG, 不然颜色不对!!
    return rgb_data



def main():
    # 读取原始数据
    with open('input.raw', 'rb') as f:
        raw_data = f.read()

    # 转换数据
    rgb_data = convert_argb8888_to_rgb888(raw_data)

    # 创建BMP头
    bmp_header = create_bmp_header(WIDTH, HEIGHT)

    # 写入BMP文件
    with open('output.bmp', 'wb') as f:
        f.write(bmp_header)
        f.write(rgb_data)

if __name__ == '__main__':
    main()

3.1、回转 bmp 文件

使用方法

python raw_to_bmp.py input.raw

4、java 版本源码

RawToBitMap.java

/**
 * Created by cczheng on 2023/9/14.
 * <p>
 * https://www.cnblogs.com/chenrui7/p/4561020.html
 * <p>
 * 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
     * <p>
     * 图像宽度必须能被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
     * <p>
     * 图像宽度必须能被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();
        }
    }
}

4.1、回转 bmp 文件

使用方法

try {
Bitmap bitmap = RawToBitMap.convert24bit(getAssets().open(“input.raw”), 800, 1280);
imageView.setImageBitmap(bitmap);
} catch (IOException e) {
e.printStackTrace();
}

猜你喜欢

转载自blog.csdn.net/u012932409/article/details/133950056