ghidra/Ghidra/Features/Decompiler/src/decompile/cpp/ghidra_process.cc

520 lines
14 KiB
C++

/* ###
* 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();
}