mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-09-28 17:11:11 -09:00
Merge remote-tracking branch 'origin/GP-42_dev747368_PdbSymbolServer_for_10'
This commit is contained in:
@@ -15,102 +15,183 @@
|
||||
*/
|
||||
package help.screenshot;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Window;
|
||||
import java.util.ArrayList;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.junit.*;
|
||||
|
||||
import docking.widgets.dialogs.ObjectChooserDialog;
|
||||
import ghidra.app.util.pdb.PdbLocator;
|
||||
import ghidra.util.Msg;
|
||||
import pdb.URLChoice;
|
||||
import ghidra.app.util.bin.format.pdb.PdbInfo;
|
||||
import ghidra.app.util.bin.format.pdb.PdbInfoDotNet;
|
||||
import ghidra.app.util.datatype.microsoft.GUID;
|
||||
import ghidra.framework.options.Options;
|
||||
import ghidra.program.model.listing.Program;
|
||||
import pdb.PdbPlugin;
|
||||
import pdb.symbolserver.*;
|
||||
import pdb.symbolserver.ui.ConfigPdbDialog;
|
||||
import pdb.symbolserver.ui.LoadPdbDialog;
|
||||
|
||||
public class PdbScreenShots extends GhidraScreenShotGenerator {
|
||||
|
||||
@Test
|
||||
public void testPdbOrXmlDialog() throws Exception {
|
||||
private static final String GUID1_STR = "012345670123012301230123456789AB";
|
||||
|
||||
performAction("Download_PDB_File", "PdbSymbolServerPlugin", false);
|
||||
private int tx;
|
||||
private File temporaryDir;
|
||||
|
||||
Window pdbDialog = waitForWindow("pdb or pdb.xml");
|
||||
pdbDialog.setSize(new Dimension(750, 200));
|
||||
captureWindow(pdbDialog);
|
||||
@Override
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
pressButtonByText(pdbDialog, "Cancel");
|
||||
temporaryDir = createTempDirectory("example_pdb");
|
||||
tx = program.startTransaction("set analyzed flag");
|
||||
Options proplist = program.getOptions(Program.PROGRAM_INFO);
|
||||
proplist.setBoolean(Program.ANALYZED, false);
|
||||
PdbInfo pdbInfo = PdbInfoDotNet.fromValues("HelloWorld.pdb", 1, new GUID(GUID1_STR));
|
||||
pdbInfo.serializeToOptions(proplist);
|
||||
proplist.setString("Executable Location",
|
||||
new File(temporaryDir, program.getName()).getPath());
|
||||
}
|
||||
|
||||
@Override
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
program.endTransaction(tx, false);
|
||||
super.tearDown();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPeSpecifiedPathDialog() throws Exception {
|
||||
|
||||
performAction("Download_PDB_File", "PdbSymbolServerPlugin", false);
|
||||
|
||||
Window pdbDialog = waitForWindow("pdb or pdb.xml");
|
||||
pressButtonByText(pdbDialog, "PDB");
|
||||
|
||||
Window peSpecifiedPathDialog = waitForWindow("PE-specified PDB Path");
|
||||
captureWindow(peSpecifiedPathDialog);
|
||||
|
||||
pressButtonByText(peSpecifiedPathDialog, "Cancel");
|
||||
public void testSymbolServerConfig_Screenshot() throws IOException {
|
||||
PdbPlugin.saveSymbolServerServiceConfig(null);
|
||||
ConfigPdbDialog configPdbDialog = new ConfigPdbDialog();
|
||||
showDialogWithoutBlocking(tool, configPdbDialog);
|
||||
waitForSwing();
|
||||
captureDialog(ConfigPdbDialog.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSymbolServerURLDialog() throws Exception {
|
||||
|
||||
// Set up for local directory
|
||||
PdbLocator.setDefaultPdbSymbolsDir(getTestDataDirectory());
|
||||
|
||||
performAction("Download_PDB_File", "PdbSymbolServerPlugin", false);
|
||||
|
||||
Window pdbDialog = waitForWindow("pdb or pdb.xml");
|
||||
pressButtonByText(pdbDialog, "PDB");
|
||||
|
||||
Window peSpecifiedPathDialog = waitForWindow("PE-specified PDB Path");
|
||||
pressButtonByText(peSpecifiedPathDialog, "Yes");
|
||||
|
||||
Window saveLocationDialog = waitForWindow("Select Location to Save Retrieved File");
|
||||
pressButtonByText(saveLocationDialog, "OK");
|
||||
|
||||
Window urlDialog = waitForWindow("Symbol Server URL");
|
||||
urlDialog.setSize(new Dimension(850, 135));
|
||||
|
||||
captureWindow(urlDialog);
|
||||
|
||||
pressButtonByText(urlDialog, "Cancel");
|
||||
public void testLoadPdb_Initial_Screenshot() throws IOException {
|
||||
LoadPdbDialog loadPdbDialog = new LoadPdbDialog(program);
|
||||
showDialogWithoutBlocking(tool, loadPdbDialog);
|
||||
captureDialog(loadPdbDialog);
|
||||
pressButtonByText(loadPdbDialog, "Cancel");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKnownSymbolServerURLsDialog() throws Exception {
|
||||
|
||||
List<URLChoice> urlChoices = new ArrayList<>();
|
||||
urlChoices.add(new URLChoice("Internet", "https://msdl.microsoft.com/download/symbols"));
|
||||
urlChoices.add(new URLChoice("My Network", "https://my_symbol_server.my.org"));
|
||||
|
||||
final ObjectChooserDialog<URLChoice> urlDialog = new ObjectChooserDialog<>("Choose a URL",
|
||||
URLChoice.class, urlChoices, "getNetwork", "getUrl");
|
||||
public void testSymbolServerConfig_AddButtonMenu() throws IOException {
|
||||
File localSymbolStore1Root = new File(temporaryDir, "symbols");
|
||||
LocalSymbolStore.create(localSymbolStore1Root, 1);
|
||||
LocalSymbolStore localSymbolStore1 =
|
||||
new LocalSymbolStoreWithFakePath(localSymbolStore1Root, "/home/user/symbols");
|
||||
SymbolServerService symbolServerService =
|
||||
new SymbolServerService(localSymbolStore1, List.of());
|
||||
PdbPlugin.saveSymbolServerServiceConfig(symbolServerService);
|
||||
|
||||
LoadPdbDialog choosePdbDialog = new LoadPdbDialog(program);
|
||||
showDialogWithoutBlocking(tool, choosePdbDialog);
|
||||
waitForSwing();
|
||||
pressButtonByText(choosePdbDialog, "Advanced >>");
|
||||
runSwing(() -> {
|
||||
// Do nothing
|
||||
choosePdbDialog.pushAddLocationBution();
|
||||
});
|
||||
showDialogWithoutBlocking(tool, urlDialog);
|
||||
captureDialog();
|
||||
|
||||
pressButtonByText(urlDialog, "Cancel");
|
||||
waitForSwing();
|
||||
captureMenu();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessDialog() throws Exception {
|
||||
public void testLoadPdb_Advanced_NeedsConfig() throws IOException {
|
||||
PdbPlugin.saveSymbolServerServiceConfig(null);
|
||||
LoadPdbDialog choosePdbDialog = new LoadPdbDialog(program);
|
||||
showDialogWithoutBlocking(tool, choosePdbDialog);
|
||||
waitForSwing();
|
||||
pressButtonByText(choosePdbDialog, "Advanced >>");
|
||||
waitForSwing();
|
||||
captureDialog(LoadPdbDialog.class);
|
||||
pressButtonByText(choosePdbDialog, "Cancel");
|
||||
}
|
||||
|
||||
// Can't really get success message without actually downloading a file.
|
||||
// So, fake out the message by showing the same sort of dialog the user would see.
|
||||
Msg.showInfo(getClass(), null, "File Retrieved",
|
||||
"Downloaded and saved file 'example.pdb' to \n" +
|
||||
"C:\\Symbols\\example.pdb\\1123A456B7889012C3DDFA4556789B011");
|
||||
@Test
|
||||
public void testLoadPdb_Advanced_Screenshot() throws IOException {
|
||||
// Show the advanced side of the LoadPdbDialog, with
|
||||
// some faked search locations and search results so we
|
||||
// can have pretty paths
|
||||
File localSymbolStore1Root = new File(temporaryDir, "symbols");
|
||||
LocalSymbolStore.create(localSymbolStore1Root, 1);
|
||||
LocalSymbolStore localSymbolStore1 =
|
||||
new LocalSymbolStoreWithFakePath(localSymbolStore1Root, "/home/user/symbols");
|
||||
SameDirSymbolStoreWithFakePath sameDirSymbolStoreWithFakePath =
|
||||
new SameDirSymbolStoreWithFakePath(temporaryDir, "/home/user/examples");
|
||||
List<SymbolServer> symbolServers = List.of(sameDirSymbolStoreWithFakePath,
|
||||
new HttpSymbolServer(URI.create("https://msdl.microsoft.com/download/symbols/")));
|
||||
SymbolServerService symbolServerService =
|
||||
new SymbolServerService(localSymbolStore1, symbolServers);
|
||||
PdbPlugin.saveSymbolServerServiceConfig(symbolServerService);
|
||||
|
||||
Window successDialog = waitForWindow("File Retrieved");
|
||||
captureWindow(successDialog);
|
||||
LoadPdbDialog loadPdbDialog = new LoadPdbDialog(program);
|
||||
showDialogWithoutBlocking(tool, loadPdbDialog);
|
||||
waitForSwing();
|
||||
pressButtonByText(loadPdbDialog, "Advanced >>");
|
||||
List<SymbolFileLocation> symbolFileLocations = List.of(
|
||||
new SymbolFileLocation("HelloWorld.pdb/" + GUID1_STR + "1/HelloWorld.pdb",
|
||||
localSymbolStore1, SymbolFileInfo.fromValues("HelloWorld.pdb", GUID1_STR, 1)),
|
||||
new SymbolFileLocation("HelloWorld.pdb/" + GUID1_STR + "2/HelloWorld.pdb",
|
||||
localSymbolStore1, SymbolFileInfo.fromValues("HelloWorld.pdb", GUID1_STR, 2)),
|
||||
new SymbolFileLocation("HelloWorld.pdb", sameDirSymbolStoreWithFakePath,
|
||||
SymbolFileInfo.fromValues("HelloWorld.pdb", GUID1_STR, 1)),
|
||||
new SymbolFileLocation("HelloWorld_ver2.pdb", sameDirSymbolStoreWithFakePath,
|
||||
SymbolFileInfo.fromValues("HelloWorld.pdb", GUID1_STR, 2)));
|
||||
Set<FindOption> findOptions = FindOption.of(FindOption.ALLOW_REMOTE, FindOption.ANY_AGE);
|
||||
runSwing(() -> {
|
||||
loadPdbDialog.setSearchOptions(findOptions);
|
||||
loadPdbDialog.setSymbolServers(symbolServers);
|
||||
loadPdbDialog.setSymbolStorageDirectoryTextOnly("/home/user/symbols");
|
||||
loadPdbDialog.setSearchResults(symbolFileLocations, findOptions);
|
||||
loadPdbDialog.selectRowByLocation(symbolFileLocations.get(0));
|
||||
});
|
||||
waitForSwing();
|
||||
captureDialog(LoadPdbDialog.class);
|
||||
pressButtonByText(loadPdbDialog, "Cancel");
|
||||
}
|
||||
|
||||
pressButtonByText(successDialog, "OK");
|
||||
private static class LocalSymbolStoreWithFakePath extends LocalSymbolStore {
|
||||
private String fakeRootDirPath;
|
||||
|
||||
public LocalSymbolStoreWithFakePath(File rootDir, String fakeRootDirPath) {
|
||||
super(rootDir);
|
||||
this.fakeRootDirPath = fakeRootDirPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescriptiveName() {
|
||||
return fakeRootDirPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileLocation(String filename) {
|
||||
return FilenameUtils.concat(fakeRootDirPath, filename);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SameDirSymbolStoreWithFakePath extends SameDirSymbolStore {
|
||||
private String fakeRootDirPath;
|
||||
|
||||
public SameDirSymbolStoreWithFakePath(File rootDir, String fakeRootDirPath) {
|
||||
super(rootDir);
|
||||
this.fakeRootDirPath = fakeRootDirPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescriptiveName() {
|
||||
return String.format(PROGRAMS_IMPORT_LOCATION_DESCRIPTION_STR + " - %s",
|
||||
fakeRootDirPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFileLocation(String filename) {
|
||||
return FilenameUtils.concat(fakeRootDirPath, filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/* ###
|
||||
* 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 pdb.symbolserver;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.google.common.io.BaseEncoding;
|
||||
|
||||
import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||
import ghidra.util.exception.CancelledException;
|
||||
import ghidra.util.task.TaskMonitor;
|
||||
import utilities.util.FileUtilities;
|
||||
|
||||
/**
|
||||
* Tests for Pdb SymbolServer stuff that need to be in the Integration module because they depend
|
||||
* on FileFormat's file system stuff to decompress .cab files
|
||||
*/
|
||||
public class SymbolServerService2Test extends AbstractGhidraHeadedIntegrationTest {
|
||||
private File temporaryDir;
|
||||
private File localSymbolStore1Root;
|
||||
private LocalSymbolStore localSymbolStore1;
|
||||
|
||||
// Bytes for a very small .cab file that contains a singleton file named 'test.pdb' with
|
||||
// contents of "test"
|
||||
byte[] smallCabFileBytes = BaseEncoding.base16()
|
||||
.decode(("4d5343460000000055000000000000002c000000000000000301010001" +
|
||||
"00000000000000450000000100010004000000000000000000a248bc5c2000746573742e7064620" +
|
||||
"066652e4908000400434b2b492d2e0100").toUpperCase());
|
||||
|
||||
private File mkFile(File file, byte[] bytes) throws IOException {
|
||||
FileUtilities.checkedMkdirs(file.getParentFile());
|
||||
FileUtilities.writeBytes(file, bytes);
|
||||
return file;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() throws IOException {
|
||||
temporaryDir = createTempDirectory("symbolservers");
|
||||
localSymbolStore1Root = new File(temporaryDir, "symbols1");
|
||||
LocalSymbolStore.create(localSymbolStore1Root, 1);
|
||||
|
||||
localSymbolStore1 = new LocalSymbolStore(localSymbolStore1Root);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocalCab() throws IOException, CancelledException {
|
||||
mkFile(new File(localSymbolStore1Root, "test.pdb/112233441/test.pd_"), smallCabFileBytes);
|
||||
|
||||
SymbolServerService symbolServerService =
|
||||
new SymbolServerService(localSymbolStore1, List.of());
|
||||
List<SymbolFileLocation> results =
|
||||
symbolServerService.find(SymbolFileInfo.fromValues("test.pdb", "11223344", 1),
|
||||
FindOption.NO_OPTIONS, TaskMonitor.DUMMY);
|
||||
|
||||
assertEquals(1, results.size());
|
||||
assertEquals("test.pd_", FilenameUtils.getName(results.get(0).getPath()));
|
||||
|
||||
File pdbFile = symbolServerService.getSymbolFile(results.get(0), TaskMonitor.DUMMY);
|
||||
assertEquals("test\n" /* extra \n because FileUtilities.getText() adds it */,
|
||||
FileUtilities.getText(pdbFile));
|
||||
|
||||
// search again and we should only find the now decompressed pdb file
|
||||
List<SymbolFileLocation> results2 =
|
||||
symbolServerService.find(SymbolFileInfo.fromValues("test.pdb", "11223344", 1),
|
||||
FindOption.NO_OPTIONS, TaskMonitor.DUMMY);
|
||||
|
||||
assertEquals(1, results2.size());
|
||||
assertEquals("test.pdb", FilenameUtils.getName(results2.get(0).getPath()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoteCab() throws IOException, CancelledException {
|
||||
|
||||
SymbolServerService symbolServerService =
|
||||
new SymbolServerService(localSymbolStore1,
|
||||
List.of(new DummySymbolServer(smallCabFileBytes, true)));
|
||||
|
||||
List<SymbolFileLocation> results =
|
||||
symbolServerService.find(SymbolFileInfo.fromValues("test.pdb", "11223344", 1),
|
||||
FindOption.of(FindOption.ALLOW_REMOTE), TaskMonitor.DUMMY);
|
||||
|
||||
assertEquals(1, results.size());
|
||||
System.out.println(results.get(0).getLocationStr());
|
||||
|
||||
File pdbFile = symbolServerService.getSymbolFile(results.get(0), TaskMonitor.DUMMY);
|
||||
assertEquals("test\n" /* extra \n because FileUtilities.getText() adds it */,
|
||||
FileUtilities.getText(pdbFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoteCabAlreadyExistLocal() throws IOException, CancelledException {
|
||||
|
||||
SymbolServerService symbolServerService =
|
||||
new SymbolServerService(localSymbolStore1,
|
||||
List.of(new DummySymbolServer(smallCabFileBytes, true)));
|
||||
|
||||
List<SymbolFileLocation> results =
|
||||
symbolServerService.find(SymbolFileInfo.fromValues("test.pdb", "11223344", 1),
|
||||
FindOption.of(FindOption.ALLOW_REMOTE), TaskMonitor.DUMMY);
|
||||
|
||||
assertEquals(1, results.size());
|
||||
System.out.println(results.get(0).getLocationStr());
|
||||
|
||||
// cheese the file into the local symbol store after the remote file has been found
|
||||
// but before it has been downloaded
|
||||
mkFile(new File(localSymbolStore1Root, "test.pdb/112233441/test.pdb"),
|
||||
"nottest".getBytes());
|
||||
|
||||
// normally this would download the remote file and decompress it
|
||||
File pdbFile = symbolServerService.getSymbolFile(results.get(0), TaskMonitor.DUMMY);
|
||||
|
||||
// ensure that the original file wasn't overwritten by the new file
|
||||
assertEquals("nottest\n" /* extra \n because FileUtilities.getText() adds it */,
|
||||
FileUtilities.getText(pdbFile));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user