Author Topic: Fluke undocumented image format "FBRLE2D" (now understand.)  (Read 947 times)

0 Members and 1 Guest are viewing this topic.

Online squadchannelTopic starter

  • Frequent Contributor
  • **
  • Posts: 423
  • Country: jp
  • deepl translate user
Fluke undocumented image format "FBRLE2D" (now understand.)
« on: November 09, 2024, 02:32:06 pm »
"FBRLE2D" is an image format that can be get in hardcopy.
It is used in the Fluke 190 Series II and Tek THS3000.

Interpreting the name:
Fluke Bitmap Run-Length Encoding 2D
as i think.

trying to understand this format.

I am trying to decompile and interpret the Fluke Scopemeter software (SW90W), but I don't have much experience with decompiling, interpret it to some extent in Ghidra, but when I rewrite it to work, and run it in C it does not result in a meaningful image file.

the software has a menu that allows you to retrieve the image remotely. After retrieving it, it seems to display it in a form in the GUI.

program itself seems to be native VB6 code. The DLL being called seems to be compiled in VB5.

I believe that ImgRleToFv in fv90img.dll is the function related to RLE decoding.
Is something being done in the function before this function is called,
or is it possible to make it into image data with the function after this,
or am I not understanding it well enough?

If you know, please help me.

Decompiled function. Edited to be understandable. Not sure if it is correct.:
Code: [Select]
int ImgRleToFv(int rledata, undefined4 rletofv_param_2, double *image_area_global_handler, undefined4 *global_var_handler, double *rletofv_param_5)
{
    int ret;
    int ret_err;
    float fVar1;
   
    ret = checkFBRLE2Dheader(rledata, 0, 8);
    if (ret == 0) {                                     // if FBRLE2D isnt detect
        ret_err = 0;
    }else{                                              //FBRLE2D detect
        ret = decode_header_area(rledata, 0xf, 0x120, global_var_handler);
        if (ret == 0) {
            ret_err = 0;
        }else{
            ret = decode_image_area?(rledata,0x24f,image_area_global_handler);
            if (ret == 0) {
                ret_err = 0;
            }else{
                /* float value return.but what? */
                fVar1 = (float10)rlecall#4(rledata);
                *rletofv_param_5 = (double)fVar1;
                ret_err = 1;
            }
        }
    }
  return ret_err;
}

int checkFBRLE2Dheader(int rledata, int start_addr, int header_size)
{
    char *rledata_pos;
    char *header_name;
   
    rledata_pos = (char *)(rledata + start_addr);
    header_name = "FBRLE2D";                        //header name
   
    int break_flag = 1;
    int loop_counter = 0;
    while((loop_counter < header_size && (break_flag == 1))){
        char rledata_pos_buf = *rledata_pos;
        char header_name_buf = *header_name;
       
        rledata_pos_buf = bin_curr_pos + 1;
        header_name_buf = header_name + 1;
       
        if (rledata_pos_buf != header_name_buf) {
            break_flag = 0;
        }
       
        loop_counter = loop_counter + 1;
    }
    return break_flag;
}

int decode_header_area(int rledata,int headersize?, undefined4 imagestart_addr?, undefined4 *global_var_handler)
{
    short ret_err;
    undefined4 ret;
    byte *rledata_pos;
    int loop_cnt;
    int *p_decode_buf;
   
    /* GetElemsAlloc(dynamic_mem_handler, alloc_block_size, block_bytes_size,mode_flag?) */
    ret_err = GenElemsAlloc(global_var_handler, 0x100, 4, 0);
    if (ret_err == 0) {
        ret_err = GenElemsAlloc(global_var_handler + 1,0x100,4,0);
        if (ret_err == 0) {
            rledata_pos = (byte *)(rledata + headersize?);
                /* GenElemsLock = GlobalLock */
            p_decode_buf = (int *)GenElemsLock(*global_var_handler);
            for (loop_cnt = 0; loop_cnt < 0x60; loop_cnt = loop_cnt + 1) {
                *p_decode_buf = (uint)*rledata_pos * 0x10000 + (uint)rledata_pos[1] * 0x100 + (uint)rledata_pos[2];
                p_decode_buf = p_decode_buf + 1;            //move 4byte(int)
                rledata_pos = rledata_pos + 3;          //move 1byte(char)
            }
            for (loop_cnt = 0x60; loop_cnt < 0x100; loop_cnt = loop_cnt + 1) {
                *decode_buf = 0;
                decode_buf = decode_buf + 1;
            }
            GenElemsUnlock(*global_var_handler);
            decode_buf = (int *)GenElemsLock(global_var_handler[1]);
            for (loop_cnt = 0; loop_cnt < 0x60; loop_cnt = loop_cnt + 1) {
                *decode_buf = (uint)*rledata_pos * 0x10000 + (uint)rledata_pos[1] * 0x100 +
                (uint)rledata_pos[2];
                decode_buf = decode_buf + 1;
                rledata_pos = rledata_pos + 3;
            }
            for (loop_cnt = 0x60; loop_cnt < 0x100; loop_cnt = loop_cnt + 1) {
                *decode_buf = 0;
                decode_buf = decode_buf + 1;
            }
            GenElemsUnlock(global_var_handler[1]);
            ret = 1;
        }else{
            GenElemsFree(global_var_handler);
            ret = 0;
        }
    }else{
        /* return */
        ret = 0;
    }
    return ret;
}


int decode_image_area?(int rledata,int img_data_start_addr,double *global_var_handler)
{
    int ret_err;
    int ret;
    char *buf?;
   
    char *rledata_pos;
    char *p_decode_buf;
   
    int rledata_pos_buf;
    int rledata[1]_byte_buf;
   
    int horiz_cnt;
    int vert_cnt;
 
    ret_err = GenElemsAlloc(global_var_handler,0x12c00,1,0);
    if (ret_err == 0) {
       
        rledata_pos = (byte *)(rledata + img_data_start_addr);
        p_decode_buf = (byte *)GenElemsLock(global_var_handler);
       
        for (vert_cnt = 0; vert_cnt < 0xf0; vert_cnt = vert_cnt + 1) {
           
            horiz_cnt = 0;
           
            while (horiz_cnt < 0x140) {
               
                if ((*rledata_pos & 0x80) == 0) {       //bit 8 not flagged
                   
                    *p_decode_buf = *rledata_pos;
                    p_decode_buf = p_decode_buf + 1;    //move 1byte(char)
                    rledata_pos = rledata_pos + 1;      //move 1byte(char)
                    horiz_cnt = horiz_cnt + 1;
                   
                }else{                                  //bit 8 is flagged
                   
                    rledata_pos_buf = *rledata_pos;
                    rledata[1]_pos_buf = rledata_pos[1] + 2;
                    rledata_pos = rledata_pos + 2;
                   
                    if ((rledata_pos_buf & 0x7f) < 0x60) {
                       
                        for (; curr_rledata[1]_byte_buf != 0; curr_rledata[1]_byte_buf = curr_rledata[1]_byte_buf + -1){
                           
                            *encode_buf = curr_rledata_byte_buf & 0x7f;
                            encode_buf = encode_buf + 1;
                            horiz_cnt = horiz_cnt + 1;
                           
                        }
                    }else{
                       
                        buf? = p_decode_buf + (0x80 - (rledata_pos_buf & 0x7f)) * -0x140;
                       
                        for (; rledata[1]_pos_buf != 0; rledata[1]_pos_buf = rledata[1]_pos_buf + -1){
                           
                            *p_decode_buf = *buf?;
                            p_decode_buf = p_decode_buf + 1;
                            buf? = buf? + 1;
                            horiz_cnt = horiz_cnt + 1;
                           
                        }
                    }
                }
            }
        }
        GenElemsUnlock(global_var_handler);
        ret = 1;
    }else{
        ret = 0;
    }
    return ret;
}


int GenElemsAlloc(int *dynamic_mem_handler,int allocation_block,short block_size_byte?,short mode_flag?)
{
    HGLOBAL allocd_mem_handle;
    int alloc_mem_size_bytes;
    int ret;
    int dynamic_mem_size;
   
    if (allocation_block < 1) {
        allocation_block = 1;
    }
   
    ret = allocation_block * block_size_byte?;
    dynamic_mem_size = ret;
   
    if ((ret - 1 & ret) != 0) {
        for (dynamic_mem_size = 1;
            ((int)dynamic_mem_size < (int)ret && ((int)dynamic_mem_size < 0x40000000));
            dynamic_mem_size = dynamic_mem_size << 1) {
        }
        if ((int)dynamic_mem_size < (int)ret) {
            return CONCAT22((short)(dynamic_mem_size >> 0x10),0xffff);
        }
    }
    if ((mode_flag? == 0) || (*dynamic_mem_handler == 0)) {
        if (*dynamic_mem_handler != 0) {
            alloc_mem_size_bytes = GlobalSize((HGLOBAL)*dynamic_mem_handler);
            if (alloc_mem_size_bytes == dynamic_mem_size) {
                return alloc_mem_size_bytes & 0xffff0000;
            }
            GenElemsFree(dynamic_mem_handler);
        }
        /* GlobalAlloc(GMEM_MOVEABLE, dwBytes) */
        allocd_mem_handle = GlobalAlloc(2,dynamic_mem_size);
    }else{
        allocd_mem_handle = GlobalReAlloc((HGLOBAL)*dynamic_mem_handler,dynamic_mem_size,2);
    }
   
    if (allocd_mem_handle == (HGLOBAL)0x0) {
        /* error cant allcation mem. return 65535 */
        ret = 0xffff;
    }else{
    /* sucsessfull allocation mem. set handle pointer and return */
        *dynamic_mem_handler = (int)allocd_mem_handle;
        ret = (uint)allocd_mem_handle & 0xffff0000;
    }
    return ret;
}
void GenElemsLock(undefined4 *param_1)
{
    /* 0x437c  49  GenElemsLock */
    GlobalLock((HGLOBAL)*param_1);
    return;
}


void GenElemsUnlock(undefined4 *param_1)
{
    /* 0x438f  52  GenElemsUnlock */
    GlobalUnlock((HGLOBAL)*param_1);
    return;
}


void GenElemsFree(int *param_1)
{
    /* 0x4358  48  GenElemsFree */
    if (*param_1 != 0) {
        GlobalFree((HGLOBAL)*param_1);
        *param_1 = 0;
    }
    return;
}

1)checking to see if “FBRLE2D” is present.
2)number specified in hex is a meaningful number compared to the RLE-encoded file.
3)values for horiz_cnt and vert_cnt are exactly 320x240.

Also, there are arguments that are not used in the "decode_image_area?" function.
I am wondering if ghidra is not decompiling enough.

attached bmp is saved using the scope's USB save function.
I believe it is the same image as the RLE encoded file because I saved it under the same conditions and with the same screen display content.
(Maybe it is not...)

Can't ghidra project data be shared?

I have been thinking about it for the past few days, until midnight as if I was being chased, and decided to ask someone.
« Last Edit: November 15, 2024, 04:11:57 am by squadchannel »
 

Online squadchannelTopic starter

  • Frequent Contributor
  • **
  • Posts: 423
  • Country: jp
  • deepl translate user
Re: Fluke undocumented image format "FBRLE2D": please help decompiling
« Reply #1 on: November 11, 2024, 02:24:25 am »
hmmm... it is close, but incomplete. :palm:
definitely a function being called in addition to ImgRleToFv.
« Last Edit: November 15, 2024, 04:12:19 am by squadchannel »
 

Online squadchannelTopic starter

  • Frequent Contributor
  • **
  • Posts: 423
  • Country: jp
  • deepl translate user
Re: Fluke undocumented image format "FBRLE2D": please help decompiling
« Reply #2 on: November 11, 2024, 07:42:46 am »
ImgRleToFv function is apparently a project file, data used inside the software.
modding the project file to what I decoded image.
close! very close! At least the decoding seems to be working. Still had bug, though. :phew:

 

Online parawizard

  • Contributor
  • Posts: 41
  • Country: ca
Re: Fluke undocumented image format "FBRLE2D": please help decompiling
« Reply #3 on: November 11, 2024, 09:17:14 am »
Very interesting. I figured out FV SW860 which is just a 1-bit bit map. Monochrome screen of course so it's much simpler. Is this a screen capture? I found that QUERY WAVEFORM actually sends enough information to recreate the screen with each point having a resolution of 0-256ish. Can that version of Flukeview mirror the screen from the meter? Or is it a save screen BMP?
 

Online squadchannelTopic starter

  • Frequent Contributor
  • **
  • Posts: 423
  • Country: jp
  • deepl translate user
Re: Fluke undocumented image format "FBRLE2D": please help decompiling
« Reply #4 on: November 11, 2024, 09:34:17 am »
using FlukeView 5.4
When I do a screen capture, the QP command is executed and the image data is retrieved.
Not sure about other models, but the 190II and THS3000 are encoded in FBRLE2D and sent to the software.

190 supports Epson Compatible(ESC/P?), LaserJet, DeskJet, PostScript.
190C supports Epson Compatible(ESC/P?), PNG, and FBRLE2D.
190II supports Epson Compatible(ESC/P?), and FBRLE2D.
THS3000 is 190II based hardware, but for some reason is limited to only FBRLE2D.

i am using THS3024.
of cource, does not work THS3024 with Flukeview, flashed the 190II firmware.

perfect image on the right was captured by the scope's capture function and saved to USB.
« Last Edit: November 11, 2024, 10:03:02 am by squadchannel »
 

Online squadchannelTopic starter

  • Frequent Contributor
  • **
  • Posts: 423
  • Country: jp
  • deepl translate user
Re: Fluke undocumented image format "FBRLE2D": please help decompiling
« Reply #5 on: November 11, 2024, 09:52:31 am »
Perfect decoding. :popcorn: :popcorn: :popcorn:
But it is not yet BMP or RAW format data. This is the data format used inside the software.
There are still other functions to be called.

Still need to confront ghidra. >:D

« Last Edit: November 11, 2024, 09:56:09 am by squadchannel »
 
The following users thanked this post: tv84, asis

Offline asis

  • Frequent Contributor
  • **
  • Posts: 285
  • Country: ru
Re: Fluke undocumented image format "FBRLE2D": please help decompiling
« Reply #6 on: November 11, 2024, 12:49:38 pm »
Hi,

I think FlukeView2 will work with the THS3024 if you find the activation key.
 

Online squadchannelTopic starter

  • Frequent Contributor
  • **
  • Posts: 423
  • Country: jp
  • deepl translate user
Re: Fluke undocumented image format "FBRLE2D": please help decompiling
« Reply #7 on: November 11, 2024, 01:00:48 pm »
I thought the FlukeView2 was for the 190III.

However, i think it probably uses the FTDI driver instead of a COM port to communicate with the scope.
Therefore, the software recognizes it with the FTDI device name.
I think it can be recognized by editing the registry, but i haven't used it because it is troublesome.

also, a kind russian guy put a FlukeView 5.4 key on another forum, so i am grateful to use it.

once the FBRLE2D decoder is complete, the GUI is next. :box:
 

Online parawizard

  • Contributor
  • Posts: 41
  • Country: ca
Re: Fluke undocumented image format "FBRLE2D": please help decompiling
« Reply #8 on: November 13, 2024, 03:14:59 am »
using FlukeView 5.4
When I do a screen capture, the QP command is executed and the image data is retrieved.
Not sure about other models, but the 190II and THS3000 are encoded in FBRLE2D and sent to the software.

190 supports Epson Compatible(ESC/P?), LaserJet, DeskJet, PostScript.
190C supports Epson Compatible(ESC/P?), PNG, and FBRLE2D.
190II supports Epson Compatible(ESC/P?), and FBRLE2D.
THS3000 is 190II based hardware, but for some reason is limited to only FBRLE2D.

i am using THS3024.
of cource, does not work THS3024 with Flukeview, flashed the 190II firmware.

perfect image on the right was captured by the scope's capture function and saved to USB.

Yes. Same as on my 867B it's just that yours is color and not monochrome. Additional bits per pixel.

What I found is that QM returns enough information to recreate the entire screen but is way faster as it is way less data.
 

Online squadchannelTopic starter

  • Frequent Contributor
  • **
  • Posts: 423
  • Country: jp
  • deepl translate user
Re: Fluke undocumented image format "FBRLE2D": (now understand.)
« Reply #9 on: November 15, 2024, 04:04:56 am »
Fully decoded. :phew:
FBRLE2D to BMP
« Last Edit: November 15, 2024, 04:12:54 am by squadchannel »
 
The following users thanked this post: PA0PBZ


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf