1

I'm a newbie here but I'm stuck with this problem.

I'm trying to decompile a function that in turns call another function (named charTranslateToHex).

This is the charTranslateToHex assembly code

                     *************************************************************
                     *                           FUNCTION                          
                     *************************************************************
                     undefined  __stdcall  charTranslateToHex (uint  param_1 , ui
     undefined         r0:1           <RETURN>
     uint              r0:4           param_1
     uint              r1:4           param_2
     undefined4        r2:4           param_3
     undefined2        Stack[-0xc]:2  local_c                                 XREF[2]:     000c1284 (W) , 
                                                                                           000c128c (W)   
                     charTranslateToHex                              XREF[3]:     Entry Point (*) , 
                                                                                  charTranslateToHex:0002a230 (T) , 
                                                                                  charTranslateToHex:0002a238 (c) , 
                                                                                  000f389c (*)   
000c1278 07  40  2d  e9    stmdb      sp!,{param_1  param_2  param_3  lr}
000c127c 00  10  a0  e3    mov        param_2 ,#0x0
000c1280 08  30  8d  e2    add        r3,sp,#0x8
000c1284 b4  10  cd  e1    strh       param_2 ,[sp,#local_c ]
000c1288 10  20  a0  e3    mov        param_3 ,#0x10
000c128c 04  00  63  e5    strb       param_1 ,[r3,#local_c ]!
000c1290 03  00  a0  e1    cpy        param_1 ,r3
000c1294 25  a6  fd  eb    bl         strtoul                                          ulong strtoul(char * __nptr, cha
000c1298 0e  80  bd  e8    ldmia      sp!,{param_2  param_3  r3 pc}

and this is how Ghidra decompile it:

void charTranslateToHex(uint param_1,uint param_2,undefined4 param_3)

{
  uint local_c;
  undefined4 uStack8;
  
  local_c = param_2 & 0xffff0000;
  local_c = local_c | param_1 & 0xff;
  uStack8 = param_3;
  strtoul((char *)&local_c,(char **)0x0,0x10);
  return;
}

Now back to the main function:

......
                             LAB_000c1410                                    XREF[1]:     000c13c4 (j)   
        000c1410 00  00  6a  e0    rsb        param_1 ,r10 ,param_1
        000c1414 48  10  8d  e2    add        param_2 ,sp,#0x48
        000c1418 00  30  81  e0    add        param_4 ,param_2 ,param_1
        000c141c 08  00  8d  e5    str        param_1 ,[sp,#local_60 ]
        000c1420 1d  00  53  e5    ldrb       param_1 ,[r3,#local_3d ]
        000c1424 04  90  a0  e1    cpy        r9,r4
        000c1428 80  a3  fd  eb    bl         charTranslateToHex                               undefined charTranslateToHex(uin
        000c142c 24  31  9f  e5    ldr        param_4 ,[DAT_000c1558 ]                          = 0001529Dh
        000c1430 00  60  a0  e3    mov        r6,#0x0
        000c1434 10  00  8d  e5    str        param_1 ,[sp,#local_58 ]
        000c1438 03  30  8f  e0    add        param_4 ,pc,param_4
        000c143c 14  30  8d  e5    str        param_4 =>DAT_000d66dd ,[sp,#local_54 ]            = 25h    %
......

and this is the decompiled version:

iVar3 = sVar5 - uVar8;
uVar12 = charTranslateToHex((uint)local_3d[iVar3],(uint)&stack0xffffffe0,uVar10);
uVar9 = (uint)uVar12;
uVar11 = 0;
__s = param_4;

I'm not understanding why the charTranslateToHex requires three parameters and why it returns void despite the main function using it's return value.

Can you help me please?

2
  • 2
    Decompilation is not an exact science. Sometimes, you must manually assist with the process. Commented Dec 17, 2020 at 22:08
  • Hi @multithr3at3d thank you for your comment. I attached the code, can you help me understand how to assist the decompiler in this case?
    – Suxsem
    Commented Dec 18, 2020 at 12:54

1 Answer 1

3

Theory

The first important thing to understand is that the decompiler always decompiles only a single function at once and that the results of this analysis are not propagated by default. So the decompiler will happily analyse a function like charTranslateToHex and come to the conclusion that it uses 3 parameters and returns none, but decompile a function that calls charTranslateToHex, e.g. main and conclude that based on only the code in main that charTranslateToHex takes 3 argument and returns something. The important step now is deciding which interpretation to trust and "commiting" to it.

Commit Parameter ID

This can be done manually via Commit Parameter ID in the context menu of the decompiler listing, or by using the analysis pass that does this for all functions.

What changes after commiting?

Now the decompiler will assume that charTranslateToHex has the function signature you commited, e.g. void charTranslateToHex(uint param_1,uint param_2,undefined4 param_3) instead of trying to infer it from the context in main. But in this case this would actually be wrong and lead to worse results!

Why is it wrong?

Because the decompiler doesn't know that call to strtoul inside charTranslateToHex actually returns something, that will then be automatically returned as the result of charTranslateToHex. This is the same problem as your question, now just with charTranslateToHex as the caller, and strtoul as the callee, instead of main as the caller and charTranslateToHex. There is no function signature available for strtoul, so it is unknown if it returns something.

You can either manually apply the correct signature unsigned long int strtoul (const char* str, char** endptr, int base); or look for the appropriate Ghidra Data Type Archive that includes this library function. Then the decompiler will correctly infer that charTranslateToHex returns something and change the signature of charTranslateToHex.

4
  • wow this was gold! Just one point remaining: it seems that this function is trying to return more then one value (I think it has to do with the ldmia instruction). Just out of curiosity, how could a C program have been compiled to this instruction? Maybe this code was processed by some sort of code obfuscator?
    – Suxsem
    Commented Dec 18, 2020 at 15:42
  • umh, actually the strtoul signature was correct (ulong strtoul (char * __nptr, char * * __endptr, int __base)). I still think it's something related to the ldmia instruction
    – Suxsem
    Commented Dec 18, 2020 at 15:45
  • is the ldmia really the last instruction of the function? The code doesn't look like it is obfuscated on purpose, probably just some optimization that Ghidra doesn't handle properly by default Commented Dec 18, 2020 at 17:07
  • yes, it's really the last instruction...
    – Suxsem
    Commented Dec 18, 2020 at 18:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.