lab313ru / ghidra_psx_ldr

Sony Playstation PSX executables loader for GHIDRA
236 stars 31 forks source link

PsyQ Object Files Loader have buggy imports #30

Closed nicolasnoble closed 3 years ago

nicolasnoble commented 4 years ago

Try loading GS_001.OBJ from LIBGS.LIB. Under IDA, you'll get the following:

COMM:00000040
COMM:00000040  # Segment type: Communal definitions
COMM:00000040                 .comm PSDIDX             # DATA XREF: GsInitGraph+40↓w
COMM:00000040                                          # sub_3F8+80↓w ...
COMM:00000044                 .comm GsDRAWENV:0x17     # DATA XREF: sub_274+58↓o
COMM:00000044                                          # sub_274+60↓o ...
COMM:00000048                 .comm GsDISPENV:5        # DATA XREF: sub_274+90↓o
COMM:00000048                                          # sub_274+9C↓w ...
COMM:0000004C                 .comm PSDGPU             # DATA XREF: sub_274+68↓w
COMM:0000004C                                          # sub_274+A4↓w ...
COMM:00000050                 .comm PSDBASEX:2         # DATA XREF: sub_274+78↓w
COMM:00000050                                          # sub_274+98↓o ...
COMM:00000054                 .comm PSDBASEY:2         # DATA XREF: sub_274+70↓w
COMM:00000054                                          # sub_274+B4↓w ...
COMM:00000058                 .comm POSITION:2         # DATA XREF: sub_274+7C↓w
COMM:00000058                                          # sub_274+E0↓w ...
COMM:0000005C                 .comm CLIP2:4            # DATA XREF: sub_274+8C↓w
COMM:0000005C                                          # GsInitGraph2+28↓w ...
COMM:00000060                 .comm PSDCNT:2           # DATA XREF: sub_3F8+20C↓w
COMM:00000060                                          # sub_3F8+214↓w ...
COMM:00000064                 .comm PSDOFSX:2          # DATA XREF: GsSortClear+6C↓r
COMM:00000068                 .comm PSDOFSY:2          # DATA XREF: GsSortClear+8C↓r
COMM:0000006C                 .comm GsORGOFSX:2
COMM:00000070                 .comm GsORGOFSY:2

Which are data import relocations. Even though each symbol is 4 bytes apart, their actual sizes can be much larger, like the GsDRAWENV symbol which is of type DRAWENV:

/*
 * Rectangle:
 */
typedef struct {
        short x, y;             /* offset point on VRAM */
        short w, h;             /* width and height */
} RECT;

/*
 * Environment
 */
typedef struct {
        u_long  tag;
        u_long  code[15];
} DR_ENV;                               /* Packed Drawing Environment */

typedef struct {
        RECT    clip;           /* clip area */
        short   ofs[2];         /* drawing offset */
        RECT    tw;             /* texture window */
        u_short tpage;          /* texture page */
        u_char  dtd;            /* dither flag (0:off, 1:on) */
        u_char  dfe;            /* flag to draw on display area (0:off 1:on) */
        u_char  isbg;           /* enable to auto-clear */
        u_char  r0, g0, b0;     /* initital background color */
        DR_ENV  dr_env;         /* reserved */
} DRAWENV;

But ghidra treats as if the data WAS in the relocation segment instead, which result in really wrong code:

void FUN_000001ac(undefined2 param_1,undefined2 param_2,uint param_3,byte param_4,undefined param_5)

{
  int iVar1;
  undefined4 uVar2;

  uVar2 = 0;
  if ((param_3 >> 4 & 3) == 3) {
    uVar2 = 3;
  }
  ResetGraph(uVar2);
  GsDRAWENV._10_2_ = 0;
  GsDRAWENV._8_2_ = 0;
  GsDRAWENV._18_2_ = 0;
  GsDRAWENV._16_2_ = 0;
  GsDRAWENV._14_2_ = 0;
  GsDRAWENV._12_2_ = 0;
  GsDRAWENV._20_2_ = 0;
  GsDRAWENV.field_0x17 = 0;
  GsDRAWENV.field_0x18 = 0;
  GsDRAWENV.field_0x16 = param_4;
  PutDrawEnv((undefined *)&GsDRAWENV);
  GsDRAWENV._0_2_ = 0;
  GsDRAWENV._2_2_ = 0;
  GsDRAWENV._8_2_ = 0;
  GsDRAWENV._10_2_ = 0;
  GsDRAWENV._12_2_ = 0;
  GsDRAWENV._14_2_ = 0;
  GsDRAWENV._4_2_ = param_1;
  GsDRAWENV._6_2_ = param_2;
  iVar1 = GetVideoMode();
  if (iVar1 == 1) {
    GsDRAWENV._10_2_ = 0x18;
    GsDRAWENV._18_2_ = GsDRAWENV._18_2_ & 0xff00 | 1;
  }
  GsDRAWENV._0_2_ = (ushort)param_3 & 4;
  GsDRAWENV._16_2_ = CONCAT11(param_5,(char)param_3) & 0xff01;
  PutDispEnv((undefined *)&GsDRAWENV);
  return;
}

In this case, the PutDrawEnv is being called on the GsDRAWENV location, but PutDispEnv is supposed to be called on the GsDISPENV location, which isn't what the import currently is doing here. It's using GsDISPENV again.

It's also visible in the disassembly window:

IDA:

.text:00000304                 la      $s0, GsDISPENV
.text:0000030C                 addiu   $v0, $s0, (PSDBASEX - 0x48)
.text:00000310                 sh      $zero, (GsDISPENV - 0x48)($s0)

ghidra:

      00000240 9c 06 10 26          addiu               s0,s0,0x69c
      00000244 08 00 02 26          addiu               v0,s0,0x8
      00000248 00 00 00 a6          sh                  zero,0x0(s0)=>GsDRAWENV                    = 

Even though the IDA output is a bit more confusing, it's properly referencing GsDISPENV instead of GsDRAWENV.

lab313ru commented 3 years ago

Fixed in: 42d7047fcd34c01ebccd1a65ac444de257cc0a8d