Fluke undocumented image format "FBRLE2D" (now understand.) - Page 1
Forum section: electronics / testgear

author: squadchannel
author_rank: Frequent Contributor
timestamp: November 09, 2024, 02:32:06 pm
content: "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 2Das 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: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 detectret_err = 0;}else{ //FBRLE2D detectret = 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 nameint 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 * 0x100 + (uint)rledata_pos;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);for (loop_cnt = 0; loop_cnt < 0x60; loop_cnt = loop_cnt + 1) {*decode_buf = (uint)*rledata_pos * 0x10000 + (uint)rledata_pos * 0x100 +(uint)rledata_pos;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);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_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 flaggedrledata_pos_buf = *rledata_pos;rledata_pos_buf = rledata_pos + 2;rledata_pos = rledata_pos + 2;if ((rledata_pos_buf & 0x7f) < 0x60) {for (; curr_rledata_byte_buf != 0; curr_rledata_byte_buf = curr_rledata_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_pos_buf != 0; rledata_pos_buf = rledata_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.

author: squadchannel
author_rank: Frequent Contributor
timestamp: November 11, 2024, 02:24:25 am
content: hmmm... it is close, but incomplete.definitely a function being called in addition to ImgRleToFv.

author: squadchannel
author_rank: Frequent Contributor
timestamp: November 11, 2024, 07:42:46 am
content: 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.

author: parawizard
author_rank: Regular Contributor
timestamp: November 11, 2024, 09:17:14 am
content: 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?

author: squadchannel
author_rank: Frequent Contributor
timestamp: November 11, 2024, 09:34:17 am
content: using FlukeView 5.4When 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.

author: squadchannel
author_rank: Frequent Contributor
timestamp: November 11, 2024, 09:52:31 am
content: Perfect decoding.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.

author: asis
author_rank: Frequent Contributor
timestamp: November 11, 2024, 12:49:38 pm
content: Hi,I think FlukeView2 will work with the THS3024 if you find the activation key.

author: squadchannel
author_rank: Frequent Contributor
timestamp: November 11, 2024, 01:00:48 pm
content: 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.

author: parawizard
author_rank: Regular Contributor
timestamp: November 13, 2024, 03:14:59 am
content: Quote from: squadchannel on November 11, 2024, 09:34:17 amYes. 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.

author: squadchannel
author_rank: Frequent Contributor
timestamp: November 15, 2024, 04:04:56 am
content: Fully decoded.FBRLE2D to BMP