CracksMan
CracksMan

Reputation: 39

Finding a stringtable resource ID

Where is a string-table resource ID stored? I am able to list the strings in a table but there doesn't appear to be any kind of identifier the "raw resource" it is just an (array of) USHORT (length) followed by wide chars (the string), there is no identifier.

PIMAGE_RESOURCE_DIR_STRING_U = ^TIMAGE_RESOURCE_DIR_STRING_U;
TIMAGE_RESOURCE_DIR_STRING_U = Record
  Count : USHORT;//Word;
  Name  : Array [0..0] of WideChar;
End;

PIMAGE_RESOURCE_DATA =^TIMAGE_RESOURCE_DATA;
TIMAGE_RESOURCE_DATA = Record
  rt_type :  DWORD; //RT_STRING
  lpName  :  ShortString; //tables name
  Address :  PDWORD; //address of the table
  dwSize  :  DWORD; //size of the data
end;

procedure GUIDataToString(IRD: TIMAGE_RESOURCE_DATA);
Type
  TStringArray = Array of String;


  Function SplitString(P: PByte; dwplen: Int32): TStringArray;
  //P is a Pointer to the string table, dwPLen is the size of the table
  Var
    Index : Int32;
    offset: Int32;
    dwLen : WORD;
    ST_ID : NativeUInt;
    rt_str: PIMAGE_RESOURCE_DIR_STRING_U;
  Begin
    Index := 0; //String Index
    offset:= 0;
    while (offset <= dwplen) do
    Begin
      SetLength(Result, Length(Result)+1);

      rt_str        := PIMAGE_RESOURCE_DIR_STRING_U(@P[offset]);
      Result[Index] := NameToStr(rt_str);
      //
      Inc(offset, (rt_str.Count * sizeof(WideChar) )+ sizeof(WORD));
      Inc(Index);
    End;
  End;

Var
  Table     : TStringArray;
  dwStrings : DWORD;
  I         : Int32;
  //d         :
begin
  Table   := SplitString(PByte(IRD.Address), IRD.dwSize);
  dwStrings := Length(Table);
  Memo1.Lines.Add('STRINGTABLE');
  Memo1.Lines.Add('{');

  for I := 0 to dwStrings-1 do
  Begin
    Memo1.Lines.Add(#9+Table[I]); //#9 is TAB
  End;
   Memo1.Lines.Add('}');
end;

I read resourcestring(type) can be cast to a PResStringRec whos .Identifier field will give an identifier but, I tried with my "raw strings" and it's a random large number (compared IDs resedit gives), any suggestions on how to find the IDs?

image

Upvotes: 0

Views: 1318

Answers (2)

CracksMan
CracksMan

Reputation: 39

Solved, where
dwGroups is the number of groups,
dwGroup is the group index containing the string,
Index is the index of the string (0..15) in the group.

Function MakeRtStringId(dwGroups, dwGroup, Index: DWORD): DWORD;
Var
  dwIndex : DWORD;
Begin
  dwIndex := 4096 - (dwGroups - dwGroup);
  Result  := (dwIndex shl 4) or Index;
End;

There is a max of 65535 strings, divided in groups of 16 gives a maximum of 4096 groups. string IDs "start" at, 65535 and decrements, so if you have 15 strings there ID are 65519, 65520, 65521, [...], 65535. and so on, because the last id is always 65535. The last digit is the string is its "index" in the group becausea single hex digit is 0-F (0-15). hints "groups of 16". Thanks Remy Lebeau for the info.

Upvotes: -1

Remy Lebeau
Remy Lebeau

Reputation: 596407

There are no IDs stored in the stringtable itself. String resources are organized in bundles of 16. A string ID is actually a 16bit integer where the high 12 bits identify the index of the bundle within the table, and the low 4 bits identify the index of the string within the bundle. Raymond Chen discusses this on his MSDN blog:

The format of string resources

What is the highest numerical resource ID permitted by Win32?

Upvotes: 2

Related Questions