1

I am reversing a raw bare-metal binary firmware for a tricore processor (TC1762/TC1766). In many functions i'm currently reversing there is access to offsets of the address stored in the a0 register. Therefore a0 points to a definable stucture, that i analysed an defined in the Data Type Manager.

Examples of access:

Reading from the structure:

result = functionCall(*(int *)(a0 + 0x1478),1);

Writing to the structure:

*(undefined4 *)(a0 + 0x147c) = timeOfLastRequest;

The structure I setup to use: Defined the offsets and names as they are accessed

Now I want to tell ghidra, to handle a0 as a pointer to this struct. If a0 was just a pointer variable this would work as intented and would make "a0 + 0x1478" something like instanceOfCounter->currentNumber.

Trying to set a type to a0 will result in an UnsupportedOperationException: Database supports global memory variables only java.lang.UnsupportedOperationException: Database supports global memory variables only

I am sure there is a way to do this correctly since it would improve the readability of the disassembly so much and would just feel right. I just can't find a way to do this.

I understand that the context, that a0 points to this struct is probably limited, so this might need to be assigned only for the context of a defined function or something. Appreciate any hint

1 Answer 1

2

In Windows Assuming code as below is compiled without debugging info (no PDB) and loaded in ghidra.

disabled analysis on loading.

followed label entry to find the address of function main

went to main (0x140001000) and hit D to Disassemble and hit F to create a Function

#include <windows.h>
#include <winternl.h>
#include <stdio.h>
int main(void)
{
    PPEB mypeb = (PPEB)__readgsqword(0x60);
    PRTL_USER_PROCESS_PARAMETERS proparm = mypeb->ProcessParameters;
    printf("%S\n", proparm->ImagePathName.Buffer);
}

the decompiled function main would look as below with fuzzy pointer arithmetics and ugly casts

   undefined8
FUN_140001000(undefined8 param_1,undefined8 param_2,undefined8 param_3,undefined8 param_4)

{
  longlong unaff_GS_OFFSET;
  
  FUN_1400010a0((longlong)&DAT_1400132e0,
                *(undefined8 *)(*(longlong *)(*(longlong *)(unaff_GS_OFFSET + 0x60) + 0x20) + 0x68),
                param_3,param_4);
  return 0;
}

there are three pointer arithmetic

  1. (unaff_GS_OFFSET + 0x60)
  2. (unaff_GS_OFFSET + 0x60) + 0x20)
  3. (unaff_GS_OFFSET + 0x60) + 0x20) + 0x68)

so there must be three unknown structs/artifacts/something/anything...

lets start making this ugliness go away by adding structures

In DataTypeManager Select the binary -> right click ->new ->structure

enter image description here

create a new structure name it as unknown_struct_1 for now
under Description add a comment using only offset 0x60
enter 0x100 arbitrarily as size

create 2 more structs the same way unknown_struct_2
using offset 0x20 only

unknown_struct_3
using offset 0x68 only

edit unknown_struct_1 go to offset 0x60 and embed a pointer to unknown_struct_2 and give a Name say "Needs Name 1"

edit unknown_struct_2 go to offset 0x20 and embed a pointer to unknown_struct_3 and give a Name say "Needs Name 2"

after this select the longlong type of GS_OFFSET right click and retype the variable as pointer to unknown_struct_1
the decompilation will improve to

{
  unknown_struct_1 *unaff_GS_OFFSET;
  
  FUN_1400010a0((longlong)&DAT_1400132e0,
                *(undefined8 *)&unaff_GS_OFFSET->NeedsName_1->NeedsName2->field_0x68,param_3,param_4
               );
  return 0;
}

now following 1400132e0 you get a tell tale sign of printf format string

                        DAT_1400132e0                                   XREF[1]:     FUN_140001000:140001029(*)  
   1400132e0 25                     ??         25h    %
   1400132e1 53                     ??         53h    S
   1400132e2 0a                     ??         0Ah

create a terminated c string at that address right click -> data -> terminatedcstring

                         s_%S_1400132e0                                  XREF[1]:     FUN_140001000:140001029(*)  
   1400132e0 25 53 0a 00            ds         "%S\n"
   1400132e4 00                     ??         00h

the decompilation will further improve to

{
  unknown_struct_1 *unaff_GS_OFFSET;
  
  FUN_1400010a0((longlong)"%S\n",
                *(undefined8 *)&unaff_GS_OFFSET->NeedsName_1->NeedsName2->field_0x68,param_3,param_4
               );
  return 0;
}

change the function signature of 0x1400010a0 to int __cdecl printf(char * formtstring,...) a vararg function

you will have a still better decompilation

{
  unknown_struct_1 *unaff_GS_OFFSET;
  
  printf("%S\n",*(undefined8 *)&unaff_GS_OFFSET->NeedsName_1->NeedsName2->field_0x68);
  return 0;
}

enter image description here

the format string is %S so it must be printing a wide string (wchar_t *) or probably the buffer in a UNICODE_STRING structure

lets now go to the unknown_struct_3 at offset 0x68 embed a UNICODE_STRING * and give it a name

enter image description here

now the decompilation is almost nearer to the final shape

{
  unknown_struct_1 *unaff_GS_OFFSET;
  
  printf("%S\n",unaff_GS_OFFSET->NeedsName_1->NeedsName2->Some_wide_string);
  return 0;

now after further reverse engineering you can infer unaff_GS_OFFSET is pointer to _TEB ->
whose 0ffset 0x60 Contains pointer to _PEB (Process Environemnt Block) ->
whose offset 0x20 contains a pointer to RTL_USER_PROCESS_PARAMETER ->
whose offset 0x68 contains a pointer to Unicode_string ImagePathName -> whose offset 0x8 contains a pointer to wchar_t

so we need to shift the UNICODE_STRING * to 0x60 as UNICODE_STRING struct so that the member Buffer a wchar_t* will be at offset 0x68.

0:000> dt ntdll!_TEB ProcessEnvironmentBlock->ProcessParameters->ImagePathName.Buffer
   +0x060 ProcessEnvironmentBlock                                          :
      +0x020 ProcessParameters                                                :
         +0x060 ImagePathName                                                    :
            +0x008 Buffer                                                           : Ptr64 Wchar
0:000>

just rename the unknowns and need names

enter image description hereenter image description here

and you have a clean decompilation after changing the function signature of main to int main (void)

int main(void)

{
  TEB *MyTeb;
  
  printf("%S\n",(MyTeb->ProcessEnvironmentBlock->ProcessParameters->ImagePathName).Buffer);
  return 0;
}
2
  • This is a great description of a nice workflow including the definition and assigning of structures. I think I am so far aware of how this works. My requirement is, that I need to assign a defined struct to a register used directly (a0 in this case). Its not a variable on stack or heap or alike.
    – user103675
    Aug 26, 2023 at 23:16
  • You mean you dont want to have a clean decompilation but disassembly showing. [foo+ blah.bar] instead of [rbp+0x68]. No ghidra doesnt do it at the moment there is an open issue in github iirc requesting such a feature
    – blabb
    Aug 27, 2023 at 4:33

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.