mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-26 19:33:44 +08:00
3807: fix DLGTEMPLATEEX parsing
Parsing of Dialog resources using the DLGTEMPLATEEX structure failed when a `DS_FONT` style option was present, as the font information was not recognized or parsed. This change corrects detection of this option and adds tests for both DLGTEMPLATE and DLGTEMPLATEEX structures in Dialog resources.
This commit is contained in:
+25
-9
@@ -92,10 +92,10 @@ public class DialogResourceDataType extends DynamicDataType {
|
||||
tempOffset = addDialogTitleArray(memBuffer, comps, tempOffset);
|
||||
|
||||
//Check to see if extra font size and array info after three dialog items
|
||||
//will only be there if DS_SETFONT mask is set at offset 0 of DLGTEMPLATE
|
||||
byte getStyle = memBuffer.getByte(0);
|
||||
//will only be there if DS_SETFONT mask is set in the style field
|
||||
byte getStyle = memBuffer.getByte(ex ? 12 : 0);
|
||||
if ((getStyle & DS_SETFONT) > 0) {
|
||||
tempOffset = addDialogFontSizeAndArray(memBuffer, comps, tempOffset);
|
||||
tempOffset = addDialogFontComponents(memBuffer, comps, tempOffset, ex);
|
||||
}
|
||||
|
||||
//get cdit value at offset 8 of DLGTEMPLATE or offset 16 of DLGTEMPLATEEX
|
||||
@@ -199,13 +199,29 @@ public class DialogResourceDataType extends DynamicDataType {
|
||||
return tempOffset;
|
||||
}
|
||||
|
||||
//adds Dialog font size and font array - the OPTIONAL 4th and 5th components after the DLGTEMPLATE structure
|
||||
private int addDialogFontSizeAndArray(MemBuffer memBuffer, List<DataTypeComponent> comps,
|
||||
int tempOffset) {
|
||||
//adds Dialog font components
|
||||
//for DLGITEMTEMPLATE structures this is the font size and typeface array
|
||||
//for DLGITEMTEMPLATEEX structures three additional fields (weight, italic, and
|
||||
//charset) are added in between the font size and typeface
|
||||
private int addDialogFontComponents(MemBuffer memBuffer, List<DataTypeComponent> comps, int tempOffset,
|
||||
boolean ex) {
|
||||
//add Dialog Font size
|
||||
tempOffset =
|
||||
addComp(new ShortDataType(), 2, "Dialog Font Size",
|
||||
memBuffer.getAddress().add(tempOffset), comps, tempOffset);
|
||||
tempOffset = addComp(new ShortDataType(), 2, "Dialog Font Size", memBuffer.getAddress().add(tempOffset), comps,
|
||||
tempOffset);
|
||||
|
||||
if (ex) {
|
||||
//add Dialog Font weight
|
||||
tempOffset = addComp(new WordDataType(), 2, "Dialog Font Weight", memBuffer.getAddress().add(tempOffset),
|
||||
comps, tempOffset);
|
||||
|
||||
//add Dialog Font Italic
|
||||
tempOffset = addComp(new ByteDataType(), 1, "Dialog Font Italic", memBuffer.getAddress().add(tempOffset),
|
||||
comps, tempOffset);
|
||||
|
||||
//add Dialog Font Charset
|
||||
tempOffset = addComp(new ByteDataType(), 1, "Dialog Font Charset", memBuffer.getAddress().add(tempOffset),
|
||||
comps, tempOffset);
|
||||
}
|
||||
|
||||
//add Dialog Font Style array
|
||||
tempOffset = addUnicodeString(memBuffer, comps, tempOffset, "Dialog Font Typeface");
|
||||
|
||||
+210
@@ -0,0 +1,210 @@
|
||||
/* ###
|
||||
* IP: GHIDRA
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package ghidra.program.model.data;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.nio.charset.Charset;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import ghidra.program.model.address.AddressSpace;
|
||||
import ghidra.program.model.address.GenericAddressSpace;
|
||||
import ghidra.program.model.mem.ByteMemBufferImpl;
|
||||
import ghidra.program.model.mem.MemBuffer;
|
||||
import ghidra.program.model.mem.MemoryAccessException;
|
||||
import ghidra.util.LittleEndianDataConverter;
|
||||
import ghidra.util.UniversalIdGenerator;
|
||||
|
||||
public class DialogResourceDataTypeTest {
|
||||
|
||||
private GenericAddressSpace addressSpace;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
UniversalIdGenerator.initialize();
|
||||
addressSpace = new GenericAddressSpace("Test Address Space", 32, AddressSpace.TYPE_RAM, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDlgTemplate() throws MemoryAccessException {
|
||||
byte[] dialogTemplate = getDlgTemplateResource();
|
||||
MemBuffer mb = new ByteMemBufferImpl(addressSpace.getAddress(0), dialogTemplate, false);
|
||||
DialogResourceDataType dr = new DialogResourceDataType();
|
||||
DataTypeComponent[] components = dr.getAllComponents(mb);
|
||||
assertEquals(6, components.length);
|
||||
|
||||
DataTypeComponent titleComponent = components[3];
|
||||
assertEquals("Dialog Title", titleComponent.getFieldName());
|
||||
byte[] titleBytes = new byte[titleComponent.getLength() - 2]; // cut off the null terminator
|
||||
mb.getBytes(titleBytes, titleComponent.getOffset());
|
||||
String title = new String(titleBytes, Charset.forName("UTF-16LE"));
|
||||
assertEquals("Test Dialog", title);
|
||||
|
||||
DataTypeComponent sizeComponent = components[4];
|
||||
assertEquals("Dialog Font Size", sizeComponent.getFieldName());
|
||||
assertEquals(8, mb.getShort(sizeComponent.getOffset()));
|
||||
|
||||
DataTypeComponent typefaceComponent = components[5];
|
||||
assertEquals("Dialog Font Typeface", typefaceComponent.getFieldName());
|
||||
byte[] typefaceBytes = new byte[typefaceComponent.getLength() - 2]; // cut off the null terminator
|
||||
mb.getBytes(typefaceBytes, typefaceComponent.getOffset());
|
||||
String typeface = new String(typefaceBytes, Charset.forName("UTF-16LE"));
|
||||
assertEquals("Test Typeface", typeface);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDlgTemplateEx() throws MemoryAccessException {
|
||||
byte[] dialogTemplateEx = getDlgTemplateExResource();
|
||||
MemBuffer mb = new ByteMemBufferImpl(addressSpace.getAddress(0), dialogTemplateEx, false);
|
||||
DialogResourceDataType dr = new DialogResourceDataType();
|
||||
DataTypeComponent[] components = dr.getAllComponents(mb);
|
||||
assertEquals(9, components.length);
|
||||
|
||||
DataTypeComponent titleComponent = components[3];
|
||||
assertEquals("Dialog Title", titleComponent.getFieldName());
|
||||
byte[] titleBytes = new byte[titleComponent.getLength() - 2]; // cut off the null terminator
|
||||
mb.getBytes(titleBytes, titleComponent.getOffset());
|
||||
String title = new String(titleBytes, Charset.forName("UTF-16LE"));
|
||||
assertEquals("Test Dialog", title);
|
||||
|
||||
DataTypeComponent sizeComponent = components[4];
|
||||
assertEquals("Dialog Font Size", sizeComponent.getFieldName());
|
||||
assertEquals(8, mb.getShort(sizeComponent.getOffset()));
|
||||
|
||||
DataTypeComponent weightComponent = components[5];
|
||||
assertEquals("Dialog Font Weight", weightComponent.getFieldName());
|
||||
assertEquals(400, mb.getShort(weightComponent.getOffset()));
|
||||
|
||||
DataTypeComponent italicComponent = components[6];
|
||||
assertEquals("Dialog Font Italic", italicComponent.getFieldName());
|
||||
assertEquals(0, mb.getByte(italicComponent.getOffset()));
|
||||
|
||||
DataTypeComponent charsetComponent = components[7];
|
||||
assertEquals("Dialog Font Charset", charsetComponent.getFieldName());
|
||||
assertEquals(1, mb.getByte(charsetComponent.getOffset()));
|
||||
|
||||
DataTypeComponent typefaceComponent = components[8];
|
||||
assertEquals("Dialog Font Typeface", typefaceComponent.getFieldName());
|
||||
byte[] typefaceBytes = new byte[typefaceComponent.getLength() - 2]; // cut off the null terminator
|
||||
mb.getBytes(typefaceBytes, typefaceComponent.getOffset());
|
||||
String typeface = new String(typefaceBytes, Charset.forName("UTF-16LE"));
|
||||
assertEquals("Test Typeface", typeface);
|
||||
}
|
||||
|
||||
private byte[] getDlgTemplateExResource() {
|
||||
byte[] resourceBytes = new byte[88];
|
||||
LittleEndianDataConverter leConverter = LittleEndianDataConverter.INSTANCE;
|
||||
|
||||
// @formatter:off
|
||||
leConverter.putShort(resourceBytes, 0, (short) 1); // dlgVer, must be 1
|
||||
leConverter.putShort(resourceBytes, 2, (short) -1); // signature, must be 0xffff
|
||||
leConverter.putInt( resourceBytes, 4, 0); // helpID
|
||||
leConverter.putInt( resourceBytes, 8, 0); // exStyle
|
||||
leConverter.putInt( resourceBytes, 12, 0x40); // style: DS_SETFONT
|
||||
leConverter.putShort(resourceBytes, 16, (short) 0); // cDlgItems (none)
|
||||
leConverter.putShort(resourceBytes, 18, (short) 0); // x
|
||||
leConverter.putShort(resourceBytes, 20, (short) 0); // y
|
||||
leConverter.putShort(resourceBytes, 22, (short) 0xd0); // cx
|
||||
leConverter.putShort(resourceBytes, 24, (short) 0xd0); // cy
|
||||
leConverter.putShort(resourceBytes, 26, (short) 0); // menu: (none)
|
||||
leConverter.putShort(resourceBytes, 28, (short) 0); // windowClass: (predefined dialog box class)
|
||||
leConverter.putShort(resourceBytes, 30, (short) 0x54); // title: "Test Dialog"
|
||||
leConverter.putShort(resourceBytes, 32, (short) 0x65);
|
||||
leConverter.putShort(resourceBytes, 34, (short) 0x73);
|
||||
leConverter.putShort(resourceBytes, 36, (short) 0x74);
|
||||
leConverter.putShort(resourceBytes, 38, (short) 0x20);
|
||||
leConverter.putShort(resourceBytes, 40, (short) 0x44);
|
||||
leConverter.putShort(resourceBytes, 42, (short) 0x69);
|
||||
leConverter.putShort(resourceBytes, 44, (short) 0x61);
|
||||
leConverter.putShort(resourceBytes, 46, (short) 0x6c);
|
||||
leConverter.putShort(resourceBytes, 48, (short) 0x6f);
|
||||
leConverter.putShort(resourceBytes, 50, (short) 0x67);
|
||||
leConverter.putShort(resourceBytes, 52, (short) 0x0);
|
||||
|
||||
// optional font fields, present here because DS_SETFONT was used
|
||||
leConverter.putShort(resourceBytes, 54, (short) 8); // pointsize
|
||||
leConverter.putShort(resourceBytes, 56, (short) 400); // weight: FW_NORMAL
|
||||
resourceBytes[58] = 0; // italic: FALSE
|
||||
resourceBytes[59] = (byte) 1; // charset: DEFAULT_CHARSET
|
||||
leConverter.putShort(resourceBytes, 60, (short) 0x54); // typeface: "Test Typeface"
|
||||
leConverter.putShort(resourceBytes, 62, (short) 0x65);
|
||||
leConverter.putShort(resourceBytes, 64, (short) 0x73);
|
||||
leConverter.putShort(resourceBytes, 66, (short) 0x74);
|
||||
leConverter.putShort(resourceBytes, 68, (short) 0x20);
|
||||
leConverter.putShort(resourceBytes, 70, (short) 0x54);
|
||||
leConverter.putShort(resourceBytes, 72, (short) 0x79);
|
||||
leConverter.putShort(resourceBytes, 74, (short) 0x70);
|
||||
leConverter.putShort(resourceBytes, 76, (short) 0x65);
|
||||
leConverter.putShort(resourceBytes, 78, (short) 0x66);
|
||||
leConverter.putShort(resourceBytes, 80, (short) 0x61);
|
||||
leConverter.putShort(resourceBytes, 82, (short) 0x63);
|
||||
leConverter.putShort(resourceBytes, 84, (short) 0x65);
|
||||
leConverter.putShort(resourceBytes, 86, (short) 0x0);
|
||||
// @formatter:on
|
||||
|
||||
return resourceBytes;
|
||||
}
|
||||
|
||||
private byte[] getDlgTemplateResource() {
|
||||
byte[] resourceBytes = new byte[76];
|
||||
LittleEndianDataConverter leConverter = LittleEndianDataConverter.INSTANCE;
|
||||
|
||||
// @formatter:off
|
||||
leConverter.putInt( resourceBytes, 0, 0x40); // style: DS_SETFONT
|
||||
leConverter.putInt( resourceBytes, 4, 0); // exStyle
|
||||
leConverter.putShort(resourceBytes, 8, (short) 0); // cdit (none)
|
||||
leConverter.putShort(resourceBytes, 10, (short) 0); // x
|
||||
leConverter.putShort(resourceBytes, 12, (short) 0); // y
|
||||
leConverter.putShort(resourceBytes, 14, (short) 0xd0); // cx
|
||||
leConverter.putShort(resourceBytes, 16, (short) 0xd0); // cy
|
||||
leConverter.putShort(resourceBytes, 18, (short) 0); // menu: (none)
|
||||
leConverter.putShort(resourceBytes, 20, (short) 0); // windowClass: (predefined dialog box class)
|
||||
leConverter.putShort(resourceBytes, 22, (short) 0x54); // title: "Test Dialog"
|
||||
leConverter.putShort(resourceBytes, 24, (short) 0x65);
|
||||
leConverter.putShort(resourceBytes, 26, (short) 0x73);
|
||||
leConverter.putShort(resourceBytes, 28, (short) 0x74);
|
||||
leConverter.putShort(resourceBytes, 30, (short) 0x20);
|
||||
leConverter.putShort(resourceBytes, 32, (short) 0x44);
|
||||
leConverter.putShort(resourceBytes, 34, (short) 0x69);
|
||||
leConverter.putShort(resourceBytes, 36, (short) 0x61);
|
||||
leConverter.putShort(resourceBytes, 38, (short) 0x6c);
|
||||
leConverter.putShort(resourceBytes, 40, (short) 0x6f);
|
||||
leConverter.putShort(resourceBytes, 42, (short) 0x67);
|
||||
leConverter.putShort(resourceBytes, 44, (short) 0x0);
|
||||
|
||||
// optional font fields, present here because DS_SETFONT was used
|
||||
leConverter.putShort(resourceBytes, 46, (short) 8); // pointsize
|
||||
leConverter.putShort(resourceBytes, 48, (short) 0x54); // typeface: "Test Typeface"
|
||||
leConverter.putShort(resourceBytes, 50, (short) 0x65);
|
||||
leConverter.putShort(resourceBytes, 52, (short) 0x73);
|
||||
leConverter.putShort(resourceBytes, 54, (short) 0x74);
|
||||
leConverter.putShort(resourceBytes, 56, (short) 0x20);
|
||||
leConverter.putShort(resourceBytes, 58, (short) 0x54);
|
||||
leConverter.putShort(resourceBytes, 60, (short) 0x79);
|
||||
leConverter.putShort(resourceBytes, 62, (short) 0x70);
|
||||
leConverter.putShort(resourceBytes, 64, (short) 0x65);
|
||||
leConverter.putShort(resourceBytes, 66, (short) 0x66);
|
||||
leConverter.putShort(resourceBytes, 68, (short) 0x61);
|
||||
leConverter.putShort(resourceBytes, 70, (short) 0x63);
|
||||
leConverter.putShort(resourceBytes, 72, (short) 0x65);
|
||||
leConverter.putShort(resourceBytes, 74, (short) 0x0);
|
||||
// @formatter:on
|
||||
|
||||
return resourceBytes;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user