"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.:
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.