mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-04 18:29:37 +02:00
Converted StringManager to use vectors
This commit is contained in:
parent
0eb48e441f
commit
1529e635fc
11 changed files with 161 additions and 111 deletions
|
@ -19,6 +19,7 @@
|
|||
#include "ghidra_translate.hh"
|
||||
#include "typegrp_ghidra.hh"
|
||||
#include "comment_ghidra.hh"
|
||||
#include "string_ghidra.hh"
|
||||
#include "cpool_ghidra.hh"
|
||||
#include "inject_ghidra.hh"
|
||||
|
||||
|
@ -346,6 +347,12 @@ void ArchitectureGhidra::buildCommentDB(DocumentStorage &store)
|
|||
commentdb = new CommentDatabaseGhidra(this);
|
||||
}
|
||||
|
||||
void ArchitectureGhidra::buildStringManager(DocumentStorage &store)
|
||||
|
||||
{
|
||||
stringManager = new GhidraStringManager(this,2048);
|
||||
}
|
||||
|
||||
void ArchitectureGhidra::buildConstantPool(DocumentStorage &store)
|
||||
|
||||
{
|
||||
|
@ -615,7 +622,7 @@ void ArchitectureGhidra::getBytes(uint1 *buf,int4 size,const Address &inaddr)
|
|||
readResponseEnd(sin);
|
||||
}
|
||||
|
||||
uint4 ArchitectureGhidra::getStringData(uint1 *buf,const Address &addr,Datatype *ct,int4 maxBytes)
|
||||
void ArchitectureGhidra::getStringData(vector<uint1> &buffer,const Address &addr,Datatype *ct,int4 maxBytes)
|
||||
|
||||
{
|
||||
sout.write("\000\000\001\004",4);
|
||||
|
@ -633,32 +640,28 @@ uint4 ArchitectureGhidra::getStringData(uint1 *buf,const Address &addr,Datatype
|
|||
|
||||
readToResponse(sin);
|
||||
int4 type = readToAnyBurst(sin);
|
||||
uint4 size = 0;
|
||||
if (type == 12) {
|
||||
int4 c = sin.get();
|
||||
size ^= (c-0x20);
|
||||
uint4 size = (c-0x20);
|
||||
c = sin.get();
|
||||
size ^= ((c-0x20)<<6);
|
||||
buffer.reserve(size);
|
||||
uint1 *dblbuf = new uint1[size * 2];
|
||||
sin.read((char *)dblbuf,size*2);
|
||||
for (int4 i=0; i < size; i++) {
|
||||
buf[i] = ((dblbuf[i*2]-'A') << 4) | (dblbuf[i*2 + 1]-'A');
|
||||
buffer.push_back(((dblbuf[i*2]-'A') << 4) | (dblbuf[i*2 + 1]-'A'));
|
||||
}
|
||||
delete [] dblbuf;
|
||||
}
|
||||
else if ((type&1)==1) {
|
||||
ostringstream errmsg;
|
||||
errmsg << "GHIDRA has no string in the loadimage at " << addr.getShortcut();
|
||||
addr.printRaw(errmsg);
|
||||
throw DataUnavailError(errmsg.str());
|
||||
}
|
||||
else
|
||||
throw JavaError("alignment","Expecting bytes or end of query response");
|
||||
type = readToAnyBurst(sin);
|
||||
if (type != 13)
|
||||
throw JavaError("alignment","Expecting byte alignment end");
|
||||
readResponseEnd(sin);
|
||||
return size;
|
||||
type = readToAnyBurst(sin);
|
||||
}
|
||||
if ((type&1)==1) {
|
||||
// Leave the buffer empty
|
||||
}
|
||||
else
|
||||
throw JavaError("alignment","Expecting end of query response");
|
||||
}
|
||||
|
||||
/// \brief Retrieve p-code to inject for a specific context
|
||||
|
|
|
@ -74,6 +74,7 @@ class ArchitectureGhidra : public Architecture {
|
|||
virtual PcodeInjectLibrary *buildPcodeInjectLibrary(void);
|
||||
virtual void buildTypegrp(DocumentStorage &store);
|
||||
virtual void buildCommentDB(DocumentStorage &store);
|
||||
virtual void buildStringManager(DocumentStorage &store);
|
||||
virtual void buildConstantPool(DocumentStorage &store);
|
||||
virtual void buildContext(DocumentStorage &store);
|
||||
virtual void buildSpecFile(DocumentStorage &store);
|
||||
|
@ -124,7 +125,7 @@ public:
|
|||
|
||||
bool getSendParamMeasures(void) const { return sendParamMeasures; } ///< Get the current setting for emitting parameter info
|
||||
|
||||
virtual uint4 getStringData(uint1 *buf,const Address &addr,Datatype *ct,int4 maxBytes);
|
||||
virtual void getStringData(vector<uint1> &buffer,const Address &addr,Datatype *ct,int4 maxBytes);
|
||||
virtual void printMessage(const string &message) const;
|
||||
|
||||
static void segvHandler(int4 sig); ///< Handler for a segment violation (SIGSEGV) signal
|
||||
|
|
|
@ -127,6 +127,7 @@ void IfaceDecompCapability::registerCommands(IfaceStatus *status)
|
|||
status->registerCom(new IfcCallFixup(),"fixup","call");
|
||||
status->registerCom(new IfcCallOtherFixup(),"fixup","callother");
|
||||
status->registerCom(new IfcVolatile(),"volatile");
|
||||
status->registerCom(new IfcReadonly(),"readonly");
|
||||
status->registerCom(new IfcPreferSplit(),"prefersplit");
|
||||
status->registerCom(new IfcStructureBlocks(),"structure","blocks");
|
||||
status->registerCom(new IfcAnalyzeRange(), "analyze","range");
|
||||
|
@ -2304,6 +2305,22 @@ void IfcVolatile::execute(istream &s)
|
|||
*status->optr << "Successfully marked range as volatile" << endl;
|
||||
}
|
||||
|
||||
void IfcReadonly::execute(istream &s)
|
||||
|
||||
{
|
||||
int4 size = 0;
|
||||
if (dcp->conf == (Architecture *)0)
|
||||
throw IfaceExecutionError("No load image present");
|
||||
Address addr = parse_machaddr(s,size,*dcp->conf->types); // Read required address
|
||||
|
||||
if (size == 0)
|
||||
throw IfaceExecutionError("Must specify a size");
|
||||
Range range( addr.getSpace(), addr.getOffset(), addr.getOffset() + (size-1));
|
||||
dcp->conf->symboltab->setPropertyRange(Varnode::readonly,range);
|
||||
|
||||
*status->optr << "Successfully marked range as readonly" << endl;
|
||||
}
|
||||
|
||||
void IfcPreferSplit::execute(istream &s)
|
||||
|
||||
{ // Mark a particular storage location as something we would prefer to split
|
||||
|
|
|
@ -531,6 +531,11 @@ public:
|
|||
virtual void execute(istream &s);
|
||||
};
|
||||
|
||||
class IfcReadonly : public IfaceDecompCommand {
|
||||
public:
|
||||
virtual void execute(istream &s);
|
||||
};
|
||||
|
||||
class IfcPreferSplit : public IfaceDecompCommand {
|
||||
public:
|
||||
virtual void execute(istream &s);
|
||||
|
|
|
@ -1216,20 +1216,23 @@ bool PrintC::doEmitWideCharPrefix(void) const
|
|||
bool PrintC::printCharacterConstant(ostream &s,const Address &addr,Datatype *charType) const
|
||||
|
||||
{
|
||||
const uint1 *buffer;
|
||||
StringManager *manager = glb->stringManager;
|
||||
try {
|
||||
buffer = manager->getStringData(addr, charType);
|
||||
} catch(DataUnavailError &err) {
|
||||
|
||||
// Retrieve UTF8 version of string
|
||||
const vector<uint1> &buffer(manager->getStringData(addr, charType));
|
||||
if (buffer.empty())
|
||||
return false;
|
||||
}
|
||||
if (doEmitWideCharPrefix() && charType->getSize() > 1)
|
||||
s << 'L'; // Print symbol indicating wide character
|
||||
s << '"';
|
||||
if (!escapeCharacterData(s,buffer,manager->getMaximumBytes(),charType->getSize(),glb->translate->isBigEndian()))
|
||||
if (!escapeCharacterData(s,buffer.data(),buffer.size(),1,glb->translate->isBigEndian()))
|
||||
s << "...\" /* TRUNCATED STRING LITERAL */";
|
||||
else {
|
||||
if (buffer.size() > manager->getMaximumBytes())
|
||||
s << "...\" /* TRUNCATED STRING LITERAL */";
|
||||
else
|
||||
s << '"';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include "ghidra_string.hh"
|
||||
#include "string_ghidra.hh"
|
||||
|
||||
GhidraStringManager::GhidraStringManager(ArchitectureGhidra *g,int4 max)
|
||||
: StringManager(max)
|
||||
|
@ -28,14 +28,15 @@ GhidraStringManager::~GhidraStringManager(void)
|
|||
delete [] testBuffer;
|
||||
}
|
||||
|
||||
const uint1 *GhidraStringManager::getStringData(const Address &addr,Datatype *charType)
|
||||
const vector<uint1> &GhidraStringManager::getStringData(const Address &addr,Datatype *charType)
|
||||
|
||||
{
|
||||
map<Address,const uint1 *>::iterator iter;
|
||||
map<Address,vector<uint1> >::iterator iter;
|
||||
iter = stringMap.find(addr);
|
||||
if (iter != stringMap.end())
|
||||
return (*iter).second;
|
||||
|
||||
int4 size = glb->getStringData(testBuffer, addr, charType, maximumBytes);
|
||||
return mapBuffer(addr, testBuffer, size);
|
||||
vector<uint1> &buffer(stringMap[addr]);
|
||||
glb->getStringData(buffer, addr, charType, maximumBytes);
|
||||
return buffer;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class GhidraStringManager : public StringManager {
|
|||
public:
|
||||
GhidraStringManager(ArchitectureGhidra *g,int4 max); ///< Constructor
|
||||
virtual ~GhidraStringManager(void);
|
||||
virtual const uint1 *getStringData(const Address &addr,Datatype *charType);
|
||||
virtual const vector<uint1> &getStringData(const Address &addr,Datatype *charType);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -16,21 +16,6 @@
|
|||
#include "stringmanage.hh"
|
||||
#include "architecture.hh"
|
||||
|
||||
/// Before calling, we must check that there is no other buffer stored at the address.
|
||||
/// \param addr is the Address to store the buffer at
|
||||
/// \param buf is the buffer to be copied into storage
|
||||
/// \param size is the number of bytes in the buffer
|
||||
/// \return the new permanent copy of the buffer
|
||||
const uint1 *StringManager::mapBuffer(const Address &addr,const uint1 *buf,int4 size)
|
||||
|
||||
{
|
||||
uint1 *storeBuf = new uint1[size + 1];
|
||||
stringMap[addr] = storeBuf;
|
||||
memcpy(storeBuf,buf,size);
|
||||
storeBuf[size] = 0;
|
||||
return storeBuf;
|
||||
}
|
||||
|
||||
/// \param max is the maximum number of bytes to allow in a decoded string
|
||||
StringManager::StringManager(int4 max)
|
||||
|
||||
|
@ -44,16 +29,6 @@ StringManager::~StringManager(void)
|
|||
clear();
|
||||
}
|
||||
|
||||
void StringManager::clear(void)
|
||||
|
||||
{
|
||||
map<Address,const uint1 *>::iterator iter;
|
||||
|
||||
for(iter=stringMap.begin();iter!=stringMap.end();++iter) {
|
||||
delete [] (*iter).second;
|
||||
}
|
||||
}
|
||||
|
||||
/// Encode the given unicode codepoint as UTF8 (1, 2, 3, or 4 bytes) and
|
||||
/// write the bytes to the stream.
|
||||
/// \param s is the output stream
|
||||
|
@ -103,14 +78,8 @@ void StringManager::writeUtf8(ostream &s,int4 codepoint)
|
|||
bool StringManager::isString(const Address &addr,Datatype *charType)
|
||||
|
||||
{
|
||||
const uint1 *buffer = (const uint1 *)0;
|
||||
try {
|
||||
buffer = getStringData(addr,charType);
|
||||
}
|
||||
catch(DataUnavailError &err) {
|
||||
return false;
|
||||
}
|
||||
return (buffer != (const uint1 *)0);
|
||||
const vector<uint1> &buffer(getStringData(addr,charType));
|
||||
return !buffer.empty();
|
||||
}
|
||||
|
||||
/// Write \<stringmanage> tag, with \<string> sub-tags.
|
||||
|
@ -120,15 +89,14 @@ void StringManager::saveXml(ostream &s) const
|
|||
{
|
||||
s << "<stringmanage>\n";
|
||||
|
||||
map<Address,const uint1 *>::const_iterator iter1;
|
||||
map<Address,vector<uint1> >::const_iterator iter1;
|
||||
for(iter1=stringMap.begin();iter1!=stringMap.end();++iter1) {
|
||||
s << "<string>\n";
|
||||
(*iter1).first.saveXml(s);
|
||||
const uint1 *buf = (*iter1).second;
|
||||
const vector<uint1> &vec( (*iter1).second );
|
||||
s << " <bytes>\n" << setfill('0');
|
||||
for(int4 i=0;;++i) {
|
||||
if (buf[i] == 0) break;
|
||||
s << hex << setw(2) << (int4)buf[i];
|
||||
for(int4 i=0;vec.size();++i) {
|
||||
s << hex << setw(2) << (int4)vec[i];
|
||||
if (i%20 == 19)
|
||||
s << "\n ";
|
||||
}
|
||||
|
@ -148,7 +116,7 @@ void StringManager::restoreXml(const Element *el,const AddrSpaceManager *m)
|
|||
iter = list.begin();
|
||||
Address addr = Address::restoreXml(*iter, m);
|
||||
++iter;
|
||||
vector<uint1> vec;
|
||||
vector<uint1> &vec(stringMap[addr]);
|
||||
istringstream is((*iter)->getContent());
|
||||
int4 val;
|
||||
char c1, c2;
|
||||
|
@ -174,7 +142,6 @@ void StringManager::restoreXml(const Element *el,const AddrSpaceManager *m)
|
|||
c1 = is.get();
|
||||
c2 = is.get();
|
||||
}
|
||||
mapBuffer(addr,vec.data(),vec.size());
|
||||
}
|
||||
|
||||
/// \param buffer is the byte buffer
|
||||
|
@ -300,49 +267,61 @@ StringManagerUnicode::~StringManagerUnicode(void)
|
|||
delete [] testBuffer;
|
||||
}
|
||||
|
||||
const uint1 *StringManagerUnicode::getStringData(const Address &addr,Datatype *charType)
|
||||
const vector<uint1> &StringManagerUnicode::getStringData(const Address &addr,Datatype *charType)
|
||||
|
||||
{
|
||||
map<Address,const uint1 *>::iterator iter;
|
||||
map<Address,vector<uint1> >::iterator iter;
|
||||
iter = stringMap.find(addr);
|
||||
if (iter != stringMap.end())
|
||||
return (*iter).second;
|
||||
|
||||
vector<uint1> &vec(stringMap[addr]); // Allocate (initially empty) byte vector
|
||||
|
||||
int4 curBufferSize = 0;
|
||||
int4 charsize = charType->getSize();
|
||||
bool foundTerminator = false;
|
||||
|
||||
try {
|
||||
do {
|
||||
int4 amount = 32; // Grab 32 bytes of image at a time
|
||||
uint4 newBufferSize = curBufferSize + amount;
|
||||
if (newBufferSize > maximumBytes) {
|
||||
newBufferSize = maximumBytes;
|
||||
amount = newBufferSize - curBufferSize;
|
||||
if (amount == 0) break;
|
||||
if (amount == 0)
|
||||
break;
|
||||
}
|
||||
glb->loader->loadFill(testBuffer+curBufferSize,amount,addr + curBufferSize);
|
||||
foundTerminator = hasCharTerminator(testBuffer+curBufferSize,amount,charsize);
|
||||
glb->loader->loadFill(testBuffer + curBufferSize, amount,
|
||||
addr + curBufferSize);
|
||||
foundTerminator = hasCharTerminator(testBuffer + curBufferSize, amount,
|
||||
charsize);
|
||||
curBufferSize = newBufferSize;
|
||||
} while (!foundTerminator);
|
||||
} catch (DataUnavailError &err) {
|
||||
return vec; // Return the empty buffer
|
||||
}
|
||||
|
||||
const uint1 *resBuffer;
|
||||
if (charsize == 1) {
|
||||
if (!isCharacterConstant(testBuffer,curBufferSize,charsize))
|
||||
return (const uint1 *)0;
|
||||
resBuffer = mapBuffer(addr,testBuffer,curBufferSize);
|
||||
return vec; // Return the empty buffer
|
||||
vec.reserve(curBufferSize);
|
||||
vec.assign(testBuffer,testBuffer+curBufferSize);
|
||||
}
|
||||
else {
|
||||
// We need to translate to UTF8
|
||||
ostringstream s;
|
||||
if (!writeUnicode(s, testBuffer, curBufferSize, charsize))
|
||||
return (const uint1 *)0;
|
||||
return vec; // Return the empty buffer
|
||||
string resString = s.str();
|
||||
int4 newSize = resString.size();
|
||||
if (newSize > maximumBytes)
|
||||
newSize = maximumBytes;
|
||||
resBuffer = mapBuffer(addr,(const uint1 *)resString.c_str(),newSize);
|
||||
vector<uint1> &vec(stringMap[addr]);
|
||||
vec.reserve(newSize);
|
||||
const uint1 *ptr = (const uint1 *)resString.c_str();
|
||||
vec.assign(ptr,ptr+newSize);
|
||||
}
|
||||
return resBuffer;
|
||||
return vec;
|
||||
}
|
||||
|
||||
/// If the string is encoded in UTF8 or ASCII, we get (on average) a bit of check
|
||||
|
|
|
@ -30,27 +30,25 @@ class Architecture;
|
|||
/// Stores the decoded string until its needed for presentation.
|
||||
class StringManager {
|
||||
protected:
|
||||
map<Address,const uint1 *> stringMap; ///< Map from address to string (in UTF8 format)
|
||||
map<Address,vector<uint1> > stringMap; ///< Map from address to string (in UTF8 format)
|
||||
int4 maximumBytes; ///< Maximum bytes (in UTF8 encoding) allowed
|
||||
|
||||
const uint1 *mapBuffer(const Address &addr,const uint1 *buf,int4 size); ///< Move a decoded buffer into storage
|
||||
public:
|
||||
StringManager(int4 max); ///< Constructor
|
||||
virtual ~StringManager(void); ///< Destructor
|
||||
|
||||
int4 getMaximumBytes(void) const { return maximumBytes; } ///< Return the maximum bytes allowed in a string decoding
|
||||
void clear(void); ///< Clear out any cached strings
|
||||
void clear(void) { stringMap.clear(); } ///< Clear out any cached strings
|
||||
|
||||
bool isString(const Address &addr,Datatype *charType); // Determine if data at the given address is a string
|
||||
|
||||
/// \brief Retrieve string data at the given address as a UTF8 byte array
|
||||
///
|
||||
/// If the address does not represent string data, null is returned. Otherwise,
|
||||
/// If the address does not represent string data, a zero length vector is returned. Otherwise,
|
||||
/// the string data is fetched, converted to a UTF8 encoding, cached and returned.
|
||||
/// \param addr is the given address
|
||||
/// \param charType is a character data-type indicating the encoding
|
||||
/// \return the byte array of UTF8 data (or null)
|
||||
virtual const uint1 *getStringData(const Address &addr,Datatype *charType)=0;
|
||||
/// \return the byte array of UTF8 data
|
||||
virtual const vector<uint1> &getStringData(const Address &addr,Datatype *charType)=0;
|
||||
|
||||
void saveXml(ostream &s) const; ///< Save cached strings to a stream as XML
|
||||
void restoreXml(const Element *el,const AddrSpaceManager *m); ///< Restore string cache from XML
|
||||
|
@ -72,7 +70,7 @@ public:
|
|||
StringManagerUnicode(Architecture *g,int4 max); ///< Constructor
|
||||
virtual ~StringManagerUnicode(void);
|
||||
|
||||
virtual const uint1 *getStringData(const Address &addr,Datatype *charType);
|
||||
virtual const vector<uint1> &getStringData(const Address &addr,Datatype *charType);
|
||||
bool isCharacterConstant(const uint1 *buf,int4 size,int4 charsize) const; ///< Return \b true if buffer looks like unicode
|
||||
bool writeUnicode(ostream &s,uint1 *buffer,int4 size,int4 charsize); ///< Write unicode byte array to stream (as UTF8)
|
||||
};
|
||||
|
|
|
@ -1181,6 +1181,35 @@ public class DecompileCallback {
|
|||
return listing.getFunctionAt(addr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if there are no "replacement" characters in the string
|
||||
* @param string is the string to test
|
||||
* @return true if no replacements
|
||||
*/
|
||||
private boolean isValidChars(String string) {
|
||||
char replaceChar = '\ufffd';
|
||||
for (int i = 0; i < string.length(); ++i) {
|
||||
char c = string.charAt(i);
|
||||
if (c == replaceChar) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check for a string at an address and return a UTF8 encoded byte array.
|
||||
* If there is already data present at the address, use this to determine the
|
||||
* string encoding. Otherwise use the data-type info passed in to determine the encoding.
|
||||
* Check that the bytes at the address represent a valid string encoding that doesn't
|
||||
* exceed the maximum byte limit passed in. Return null if the string is invalid.
|
||||
* Return the string translated into a UTF8 byte array otherwise. A (valid) empty
|
||||
* string is returned as a zero length array.
|
||||
* @param addrString is the XML encoded address and maximum byte limit
|
||||
* @param dtName is the name of a character data-type
|
||||
* @param dtId is the id associated with the character data-type
|
||||
* @return the UTF8 encoded byte array or null
|
||||
*/
|
||||
public byte[] getStringData(String addrString, String dtName, String dtId) {
|
||||
Address addr;
|
||||
int maxBytes;
|
||||
|
@ -1199,9 +1228,21 @@ public class DecompileCallback {
|
|||
Settings settings = SettingsImpl.NO_SETTINGS;
|
||||
AbstractStringDataType dataType = null;
|
||||
if (data != null) {
|
||||
settings = data;
|
||||
if (data.getDataType() instanceof AbstractStringDataType) {
|
||||
settings = data;
|
||||
dataType = (AbstractStringDataType) data.getDataType();
|
||||
int len = data.getLength();
|
||||
if (len > 0) {
|
||||
long diff = addr.subtract(data.getAddress()) *
|
||||
addr.getAddressSpace().getAddressableUnitSize();
|
||||
if (diff < 0 || diff >= len) {
|
||||
return null;
|
||||
}
|
||||
len -= diff;
|
||||
if (len < maxBytes) {
|
||||
maxBytes = len;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dataType == null) {
|
||||
|
@ -1228,17 +1269,17 @@ public class DecompileCallback {
|
|||
}
|
||||
}
|
||||
MemoryBufferImpl buf = new MemoryBufferImpl(program.getMemory(), addr, 64);
|
||||
Object value = dataType.getValue(buf, settings, maxBytes);
|
||||
if (!(value instanceof String)) {
|
||||
StringDataInstance stringInstance = dataType.getStringDataInstance(buf, settings, maxBytes);
|
||||
int len = stringInstance.getStringLength();
|
||||
if (len < 0 || len > maxBytes) {
|
||||
return null;
|
||||
}
|
||||
String stringVal = (String) value;
|
||||
byte[] res = stringVal.getBytes(utf8Charset);
|
||||
if (res.length > maxBytes) {
|
||||
byte[] trim = new byte[maxBytes];
|
||||
System.arraycopy(res, 0, trim, 0, maxBytes);
|
||||
|
||||
String stringVal = stringInstance.getStringValue();
|
||||
if (!isValidChars(stringVal)) {
|
||||
return null;
|
||||
}
|
||||
return res;
|
||||
return stringVal.getBytes(utf8Charset);
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
|
|
@ -789,19 +789,21 @@ public class DecompileProcess {
|
|||
String dtId = readQueryString();
|
||||
byte[] res = callback.getStringData(addr, dtName, dtId);
|
||||
write(query_response_start);
|
||||
if ((res != null) && (res.length > 0)) {
|
||||
int sz = res.length;
|
||||
if (res != null) {
|
||||
int sz = res.length + 1; // We add a null terminator character
|
||||
int sz1 = (sz & 0x3f) + 0x20;
|
||||
sz >>>= 6;
|
||||
int sz2 = (sz & 0x3f) + 0x20;
|
||||
write(byte_start);
|
||||
write(sz1);
|
||||
write(sz2);
|
||||
byte[] dblres = new byte[res.length * 2];
|
||||
byte[] dblres = new byte[res.length * 2 + 2];
|
||||
for (int i = 0; i < res.length; i++) {
|
||||
dblres[i * 2] = (byte) (((res[i] >> 4) & 0xf) + 65);
|
||||
dblres[i * 2 + 1] = (byte) ((res[i] & 0xf) + 65);
|
||||
}
|
||||
dblres[res.length * 2] = 65; // Adding null terminator
|
||||
dblres[res.length * 2 + 1] = 65;
|
||||
write(dblres);
|
||||
write(byte_end);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue