Permalink
Cannot retrieve contributors at this time
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.cc
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
520 lines (442 sloc)
14.5 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ### | |
* 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. | |
*/ | |
#include "ghidra_process.hh" | |
#include "flow.hh" | |
#include "blockaction.hh" | |
#ifdef __REMOTE_SOCKET__ | |
#include "ifacedecomp.hh" | |
static IfaceStatus *ghidra_dcp = (IfaceStatus *)0; | |
static RemoteSocket *remote = (RemoteSocket *)0; | |
/// \brief Establish a debug console for decompilation of the given function | |
/// | |
/// Attempt to connect to a UNIX domain socket and direct the i/o streams to | |
/// the decompiler console interface. The socket must have been previously established | |
/// by another process. | |
/// From the command-line, `nc -l -U /tmp/ghidrasocket` for example. | |
void connect_to_console(Funcdata *fd) | |
{ | |
if (remote == (RemoteSocket *)0) { | |
remote = new RemoteSocket(); | |
if (remote->open("/tmp/ghidrasocket")) { | |
ghidra_dcp = new IfaceStatus("[ghidradbg]> ",*remote->getInputStream(),*remote->getOutputStream()); | |
IfaceCapability::registerAllCommands(ghidra_dcp); | |
} | |
} | |
if (!remote->isSocketOpen()) | |
return; | |
IfaceDecompData *decomp_data = (IfaceDecompData *)ghidra_dcp->getData("decompile"); | |
decomp_data->fd = fd; | |
decomp_data->conf = fd->getArch(); | |
ostream *oldPrintStream = decomp_data->conf->print->getOutputStream(); | |
bool emitXml = decomp_data->conf->print->emitsXml(); | |
decomp_data->conf->setDebugStream(remote->getOutputStream()); | |
decomp_data->conf->print->setOutputStream(remote->getOutputStream()); | |
decomp_data->conf->print->setXML(false); | |
ghidra_dcp->reset(); | |
mainloop(ghidra_dcp); | |
decomp_data->conf->clearAnalysis(fd); | |
decomp_data->conf->print->setOutputStream(oldPrintStream); | |
decomp_data->conf->print->setXML(emitXml); | |
fd->debugDisable(); | |
decomp_data->conf->allacts.getCurrent()->clearBreakPoints(); | |
} | |
#endif | |
vector<ArchitectureGhidra *> archlist; // List of architectures currently running | |
map<string,GhidraCommand *> GhidraCapability::commandmap; // List of commands we can receive from Ghidra proper | |
// Constructing the singleton registers the capability | |
GhidraDecompCapability GhidraDecompCapability::ghidraDecompCapability; | |
/// This method reads an id selecting the Architecture to act on, but it can be overloaded | |
/// to read any set of data from the Ghidra client to configure how the command is executed. | |
/// Individual parameters are read using the method protocol. | |
void GhidraCommand::loadParameters(void) | |
{ | |
int4 id = -1; | |
int4 type = ArchitectureGhidra::readToAnyBurst(sin); | |
if (type != 14) | |
throw JavaError("alignment","Expecting arch id start"); | |
sin >> dec >> id; | |
type = ArchitectureGhidra::readToAnyBurst(sin); | |
if (type != 15) | |
throw JavaError("alignment","Expecting arch id end"); | |
if ((id>=0)&&(id<archlist.size())) | |
ghidra = archlist[id]; | |
if (ghidra == (ArchitectureGhidra *)0) | |
throw JavaError("decompiler","No architecture registered with decompiler"); | |
ghidra->clearWarnings(); | |
} | |
/// This method sends any warnings accumulated during execution back, but it can be overloaded | |
/// to send back any kind of information. Individual records are sent using | |
/// the message protocol. | |
void GhidraCommand::sendResult(void) | |
{ | |
if (ghidra != (ArchitectureGhidra *)0) { | |
sout.write("\000\000\001\020",4); | |
sout << ghidra->getWarnings(); | |
sout.write("\000\000\001\021",4); | |
} | |
} | |
/// This method calls the main overloaded methods: | |
/// - loadParameters() | |
/// - rawAction() | |
/// - sendResult() | |
/// | |
/// It wraps the sequence with appropriate error handling and message protocol. | |
/// \return the meta-command (0=continue, 1=terminate) as issued by the command. | |
int4 GhidraCommand::doit(void) | |
{ | |
status = 0; | |
sout.write("\000\000\001\006",4); // Command response header | |
try { | |
loadParameters(); | |
int4 type = ArchitectureGhidra::readToAnyBurst(sin); | |
if (type != 3) | |
throw JavaError("alignment","Missing end of command"); | |
rawAction(); | |
} | |
catch(XmlError &err) { | |
string errmsg; | |
errmsg = "XML processing error: " + err.explain; | |
ghidra->printMessage( errmsg ); | |
} | |
catch(JavaError &err) { | |
ArchitectureGhidra::passJavaException(sout,err.type,err.explain); | |
return status; // Abort sending any results | |
} | |
catch(RecovError &err) { | |
string errmsg; | |
errmsg = "Recoverable Error: " + err.explain; | |
ghidra->printMessage( errmsg ); | |
} | |
catch(LowlevelError &err) { | |
string errmsg; | |
errmsg = "Low-level Error: " + err.explain; | |
ghidra->printMessage( errmsg ); | |
} | |
sendResult(); | |
sout.write("\000\000\001\007",4); // Command response closer | |
sout.flush(); | |
return status; | |
} | |
void RegisterProgram::loadParameters(void) | |
{ | |
pspec.clear(); | |
cspec.clear(); | |
tspec.clear(); | |
corespec.clear(); | |
ArchitectureGhidra::readStringStream(sin,pspec); | |
ArchitectureGhidra::readStringStream(sin,cspec); | |
ArchitectureGhidra::readStringStream(sin,tspec); | |
ArchitectureGhidra::readStringStream(sin,corespec); | |
} | |
void RegisterProgram::rawAction(void) | |
{ | |
int4 i; | |
int4 open = -1; | |
for(i=0;i<archlist.size();++i) { | |
ghidra = archlist[i]; | |
if (ghidra == (ArchitectureGhidra *)0) { | |
open = i; // Found open slot | |
} | |
} | |
ghidra = new ArchitectureGhidra(pspec,cspec,tspec,corespec,sin,sout); | |
DocumentStorage store; // temp storage of initialization xml docs | |
ghidra->init(store); | |
if (open == -1) { | |
open = archlist.size(); | |
archlist.push_back((ArchitectureGhidra *)0); | |
} | |
archlist[open] = ghidra; | |
archid = open; | |
} | |
void RegisterProgram::sendResult(void) | |
{ | |
sout.write("\000\000\001\016",4); | |
sout << dec << archid; | |
sout.write("\000\000\001\017",4); | |
GhidraCommand::sendResult(); | |
} | |
void DeregisterProgram::loadParameters(void) | |
{ | |
inid = -1; | |
int4 type = ArchitectureGhidra::readToAnyBurst(sin); | |
if (type!=14) | |
throw JavaError("alignment","Expecting deregister id start"); | |
sin >> dec >> inid; | |
type = ArchitectureGhidra::readToAnyBurst(sin); | |
if (type!=15) | |
throw JavaError("alignment","Expecting deregister id end"); | |
if ((inid>=0)&&(inid<archlist.size())) | |
ghidra = archlist[inid]; | |
if (ghidra == (ArchitectureGhidra *)0) | |
throw JavaError("decompiler","No architecture registered with decompiler"); | |
ghidra->clearWarnings(); | |
} | |
void DeregisterProgram::rawAction(void) | |
{ | |
#ifdef __REMOTE_SOCKET__ | |
if (ghidra_dcp != (IfaceStatus *)0) | |
delete ghidra_dcp; | |
if (remote != (RemoteSocket *)0) | |
delete remote; | |
ghidra_dcp = (IfaceStatus *)0; | |
remote = (RemoteSocket *)0; | |
#endif | |
if (ghidra != (ArchitectureGhidra *)0) { | |
res = 1; | |
archlist[inid] = (ArchitectureGhidra *)0; | |
delete ghidra; | |
ghidra = (ArchitectureGhidra *)0; | |
status = 1; | |
} | |
else | |
res = 0; | |
} | |
void DeregisterProgram::sendResult(void) | |
{ | |
sout.write("\000\000\001\016",4); | |
sout << dec << res; | |
sout.write("\000\000\001\017",4); | |
GhidraCommand::sendResult(); | |
} | |
void FlushNative::rawAction(void) | |
{ | |
Scope *globscope = ghidra->symboltab->getGlobalScope(); | |
globscope->clear(); // Clear symbols first as this may delete scopes | |
ghidra->symboltab->deleteSubScopes(globscope); // Flush cached function and globals database | |
ghidra->types->clearNoncore(); // Reset type information | |
ghidra->commentdb->clear(); // Clear any comments | |
ghidra->stringManager->clear(); // Clear string decodings | |
ghidra->cpool->clear(); | |
res = 0; | |
} | |
void FlushNative::sendResult(void) | |
{ | |
sout.write("\000\000\001\016",4); | |
sout << dec << res; | |
sout.write("\000\000\001\017",4); | |
GhidraCommand::sendResult(); | |
} | |
void DecompileAt::loadParameters(void) | |
{ | |
GhidraCommand::loadParameters(); | |
Document *doc; | |
doc = ArchitectureGhidra::readXMLStream(sin); // Read XML of address directly from in stream | |
addr = Address::restoreXml(doc->getRoot(),ghidra); // Parse XML for functions address | |
addr.toPhysical(); // Only for backward compatibility | |
// with SLED | |
delete doc; | |
} | |
void DecompileAt::rawAction(void) | |
{ | |
Funcdata *fd = ghidra->symboltab->getGlobalScope()->queryFunction(addr); | |
if (fd == (Funcdata *)0) { | |
ostringstream s; | |
s << "Bad decompile address: " << addr.getShortcut(); | |
addr.printRaw(s); | |
s << "\n"; | |
s << addr.getSpace()->getName() << " may not be a global space in the spec file."; | |
throw LowlevelError(s.str()); | |
} | |
if (!fd->isProcStarted()) { | |
#ifdef __REMOTE_SOCKET__ | |
connect_to_console(fd); | |
#endif | |
ghidra->allacts.getCurrent()->reset( *fd ); | |
ghidra->allacts.getCurrent()->perform( *fd ); | |
} | |
sout.write("\000\000\001\016",4); | |
// Write output XML directly to outstream | |
if (fd->isProcComplete()) { | |
//bool v1 = ghidra->getSendParamMeasures(); | |
//sout << "value: " << ghidra->getSendParamMeasures() << "\n"; | |
//bool v2 = (ghidra->allacts.getCurrentName() == "paramid"); | |
//sout << "value: " << (ghidra->allacts.getCurrentName() == "paramid") << "\n"; | |
//bool v3 = v1 && v2; | |
sout << "<doc>\n"; | |
//sout << (v1?"1":"0") << "(" << (int4)v1 << ")\n" << (v2?"1":"0") << "\n" << (v3?"1":"0") << "\n"; | |
if (ghidra->getSendParamMeasures() && (ghidra->allacts.getCurrentName() == "paramid")) { | |
ParamIDAnalysis pidanalysis( fd, true ); // Only send back final prototype | |
pidanalysis.saveXml( sout, true ); | |
} | |
else { | |
if (ghidra->getSendParamMeasures()) { | |
ParamIDAnalysis pidanalysis( fd, false ); | |
pidanalysis.saveXml( sout, true ); | |
} | |
fd->saveXml(sout,0,ghidra->getSendSyntaxTree()); | |
if (ghidra->getSendCCode()&& | |
(ghidra->allacts.getCurrentName() == "decompile")) | |
ghidra->print->docFunction(fd); | |
} | |
sout << "</doc>\n"; | |
} | |
sout.write("\000\000\001\017",4); | |
} | |
void StructureGraph::loadParameters(void) | |
{ | |
GhidraCommand::loadParameters(); | |
Document *doc; | |
doc = ArchitectureGhidra::readXMLStream(sin); | |
ingraph.restoreXml(doc->getRoot(),ghidra); | |
delete doc; | |
} | |
void StructureGraph::rawAction(void) | |
{ | |
BlockGraph resultgraph; | |
vector<FlowBlock *> rootlist; | |
resultgraph.buildCopy(ingraph); | |
resultgraph.structureLoops(rootlist); | |
resultgraph.calcForwardDominator(rootlist); | |
CollapseStructure collapse(resultgraph); | |
collapse.collapseAll(); | |
resultgraph.orderBlocks(); | |
sout.write("\000\000\001\016",4); | |
resultgraph.saveXml(sout); | |
sout.write("\000\000\001\017",4); | |
ingraph.clear(); | |
} | |
void SetAction::loadParameters(void) | |
{ | |
GhidraCommand::loadParameters(); | |
actionstring.clear(); | |
printstring.clear(); | |
ArchitectureGhidra::readStringStream(sin,actionstring); | |
ArchitectureGhidra::readStringStream(sin,printstring); | |
} | |
void SetAction::rawAction(void) | |
{ | |
res = false; | |
if (actionstring.size() != 0) | |
ghidra->allacts.setCurrent(actionstring); | |
if (printstring.size() != 0) { | |
if (printstring == "tree") | |
ghidra->setSendSyntaxTree(true); | |
else if (printstring == "notree") | |
ghidra->setSendSyntaxTree(false); | |
else if (printstring == "c") | |
ghidra->setSendCCode(true); | |
else if (printstring == "noc") | |
ghidra->setSendCCode(false); | |
else if (printstring == "parammeasures") | |
ghidra->setSendParamMeasures(true); | |
else if (printstring == "noparammeasures") | |
ghidra->setSendParamMeasures(false); | |
else if (printstring == "jumpload") | |
ghidra->flowoptions |= FlowInfo::record_jumploads; | |
else if (printstring == "nojumpload") | |
ghidra->flowoptions &= ~((uint4)FlowInfo::record_jumploads); | |
else | |
throw LowlevelError("Unknown print action: "+printstring); | |
} | |
res = true; | |
} | |
void SetAction::sendResult(void) | |
{ | |
if (res) | |
ArchitectureGhidra::writeStringStream(sout,"t"); | |
else | |
ArchitectureGhidra::writeStringStream(sout,"f"); | |
GhidraCommand::sendResult(); | |
} | |
SetOptions::SetOptions(void) : GhidraCommand() | |
{ | |
doc = (Document *)0; | |
} | |
SetOptions::~SetOptions(void) | |
{ | |
if (doc != (Document *)0) | |
delete doc; | |
} | |
void SetOptions::loadParameters(void) | |
{ | |
GhidraCommand::loadParameters(); | |
if (doc != (Document *)0) { | |
delete doc; | |
doc = (Document *)0; | |
} | |
doc = ArchitectureGhidra::readXMLStream(sin); | |
} | |
void SetOptions::rawAction(void) | |
{ | |
res = false; | |
ghidra->resetDefaults(); | |
ghidra->options->restoreXml(doc->getRoot()); | |
delete doc; | |
doc = (Document *)0; | |
res = true; | |
} | |
void SetOptions::sendResult(void) | |
{ | |
if (res) | |
ArchitectureGhidra::writeStringStream(sout,"t"); | |
else | |
ArchitectureGhidra::writeStringStream(sout,"f"); | |
GhidraCommand::sendResult(); | |
} | |
/// A command is read from the Ghidra client. The matching GhidraCommand object is | |
/// looked up in the \b commandmap, and control is handed over to the command, | |
/// with the i/o streams. The command must be issued following the proper | |
/// message protocol (see ArchitectureGhidra::readToAnyBurst) or an exception is thrown. | |
/// \param sin is the input stream from the client | |
/// \param out is the output stream to the client | |
/// \return the result code of the command | |
int4 GhidraCapability::readCommand(istream &sin,ostream &out) | |
{ | |
string function; | |
int4 type; | |
do { | |
type = ArchitectureGhidra::readToAnyBurst(sin); // Align ourselves | |
} while(type != 2); | |
ArchitectureGhidra::readStringStream(sin,function); | |
map<string,GhidraCommand *>::const_iterator iter; | |
iter = commandmap.find(function); | |
if (iter == commandmap.end()) { | |
out.write("\000\000\001\006",4); // Command response header | |
out.write("\000\000\001\020",4); | |
out << "Bad command: " << function; | |
out.write("\000\000\001\021",4); | |
out.write("\000\000\001\007",4); // Command response closer | |
out.flush(); | |
return 0; | |
} | |
return (*iter).second->doit(); | |
} | |
void GhidraCapability::shutDown(void) | |
{ | |
map<string,GhidraCommand *>::iterator iter; | |
for(iter=commandmap.begin();iter!=commandmap.end();++iter) | |
delete (*iter).second; | |
} | |
void GhidraDecompCapability::initialize(void) | |
{ | |
commandmap["registerProgram"] = new RegisterProgram(); | |
commandmap["deregisterProgram"] = new DeregisterProgram(); | |
commandmap["flushNative"] = new FlushNative(); | |
commandmap["decompileAt"] = new DecompileAt(); | |
commandmap["structureGraph"] = new StructureGraph(); | |
commandmap["setAction"] = new SetAction(); | |
commandmap["setOptions"] = new SetOptions(); | |
} | |
int main(int argc,char **argv) | |
{ | |
signal(SIGSEGV, &ArchitectureGhidra::segvHandler); // Exit on SEGV errors | |
CapabilityPoint::initializeAll(); | |
int4 status = 0; | |
while(status == 0) { | |
status = GhidraCapability::readCommand(cin,cout); | |
} | |
GhidraCapability::shutDown(); | |
} | |