From d937254c36b93841aadd519a25d1378e3eb2062e Mon Sep 17 00:00:00 2001 From: Joel Anderson Date: Sun, 19 Dec 2021 00:33:51 -0500 Subject: [PATCH 1/3] 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. --- .../model/data/DialogResourceDataType.java | 34 ++- .../data/DialogResourceDataTypeTest.java | 210 ++++++++++++++++++ 2 files changed, 235 insertions(+), 9 deletions(-) create mode 100644 Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DialogResourceDataTypeTest.java diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DialogResourceDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DialogResourceDataType.java index 6e92f09d92..2651ecac47 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DialogResourceDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DialogResourceDataType.java @@ -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 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 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"); diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DialogResourceDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DialogResourceDataTypeTest.java new file mode 100644 index 0000000000..b9a751d6d0 --- /dev/null +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DialogResourceDataTypeTest.java @@ -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; + } +} From 334014b993642ee1f805c7bea818e7893d835585 Mon Sep 17 00:00:00 2001 From: Joel Anderson Date: Tue, 28 Dec 2021 21:28:29 -0500 Subject: [PATCH 2/3] extend abstract test Changes the new test suite to extend an abstract test class to inherit setup, including the id generator initialization. Also cleans up some commit changes that were unnecessary to this fix. --- .../program/model/data/DialogResourceDataType.java | 9 +++++---- .../program/model/data/DialogResourceDataTypeTest.java | 5 ++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DialogResourceDataType.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DialogResourceDataType.java index 2651ecac47..a39a9dde8e 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DialogResourceDataType.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/data/DialogResourceDataType.java @@ -203,11 +203,12 @@ public class DialogResourceDataType extends DynamicDataType { //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 comps, int tempOffset, - boolean ex) { + private int addDialogFontComponents(MemBuffer memBuffer, List 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 diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DialogResourceDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DialogResourceDataTypeTest.java index b9a751d6d0..1057c8a275 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DialogResourceDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DialogResourceDataTypeTest.java @@ -22,21 +22,20 @@ import java.nio.charset.Charset; import org.junit.Before; import org.junit.Test; +import generic.test.AbstractGTest; 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 { +public class DialogResourceDataTypeTest extends AbstractGTest { private GenericAddressSpace addressSpace; @Before public void setUp() { - UniversalIdGenerator.initialize(); addressSpace = new GenericAddressSpace("Test Address Space", 32, AddressSpace.TYPE_RAM, 1); } From 596d0b0aa734c581835c4ffb16493f6c1aee0e01 Mon Sep 17 00:00:00 2001 From: Ryan Kurtz Date: Mon, 14 Nov 2022 11:18:15 -0500 Subject: [PATCH 3/3] GP-2821: Certify --- .../ghidra/program/model/data/DialogResourceDataTypeTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DialogResourceDataTypeTest.java b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DialogResourceDataTypeTest.java index 1057c8a275..4f0c61f098 100644 --- a/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DialogResourceDataTypeTest.java +++ b/Ghidra/Framework/SoftwareModeling/src/test/java/ghidra/program/model/data/DialogResourceDataTypeTest.java @@ -4,9 +4,9 @@ * 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.