1
0
Fork 0
x64dbg/src/dbg/commands/cmd-types.cpp

732 lines
20 KiB
C++

#include "cmd-types.h"
#include "console.h"
#include "encodemap.h"
#include "value.h"
#include "types.h"
#include "memory.h"
#include "variable.h"
#include "filehelper.h"
#include "label.h"
using namespace Types;
static bool cbInstrDataGeneric(ENCODETYPE type, int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 2))
return false;
duint addr;
if(!valfromstring(argv[1], &addr, false))
return false;
duint size = 1;
if(argc >= 3)
if(!valfromstring(argv[2], &size, false))
return false;
bool created;
if(!EncodeMapSetType(addr, size, type, &created))
{
dputs(QT_TRANSLATE_NOOP("DBG", "EncodeMapSetType failed..."));
return false;
}
if(created)
DbgCmdExec("disasm dis.sel()");
else
GuiUpdateDisassemblyView();
return false;
}
bool cbInstrDataUnknown(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_unknown, argc, argv);
}
bool cbInstrDataByte(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_byte, argc, argv);
}
bool cbInstrDataWord(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_word, argc, argv);
}
bool cbInstrDataDword(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_dword, argc, argv);
}
bool cbInstrDataFword(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_fword, argc, argv);
}
bool cbInstrDataQword(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_qword, argc, argv);
}
bool cbInstrDataTbyte(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_tbyte, argc, argv);
}
bool cbInstrDataOword(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_oword, argc, argv);
}
bool cbInstrDataMmword(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_mmword, argc, argv);
}
bool cbInstrDataXmmword(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_xmmword, argc, argv);
}
bool cbInstrDataYmmword(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_ymmword, argc, argv);
}
bool cbInstrDataFloat(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_real4, argc, argv);
}
bool cbInstrDataDouble(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_real8, argc, argv);
}
bool cbInstrDataLongdouble(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_real10, argc, argv);
}
bool cbInstrDataAscii(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_ascii, argc, argv);
}
bool cbInstrDataUnicode(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_unicode, argc, argv);
}
bool cbInstrDataCode(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_code, argc, argv);
}
bool cbInstrDataJunk(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_junk, argc, argv);
}
bool cbInstrDataMiddle(int argc, char* argv[])
{
return cbInstrDataGeneric(enc_middle, argc, argv);
}
#define towner "cmd"
bool cbInstrAddType(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 3))
return false;
if(!AddType(towner, argv[1], argv[2]))
{
dputs(QT_TRANSLATE_NOOP("DBG", "AddType failed"));
return false;
}
dprintf_untranslated("typedef %s %s;\n", argv[2], argv[1]);
return true;
}
bool cbInstrAddStruct(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 2))
return false;
if(!AddStruct(towner, argv[1]))
{
dputs(QT_TRANSLATE_NOOP("DBG", "AddStruct failed"));
return false;
}
dprintf_untranslated("struct %s;\n", argv[1]);
return true;
}
bool cbInstrAddUnion(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 2))
return false;
if(!AddUnion(towner, argv[1]))
{
dputs(QT_TRANSLATE_NOOP("DBG", "AddUnion failed"));
return false;
}
dprintf_untranslated("union %s;\n", argv[1]);
return true;
}
bool cbInstrAddMember(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 4))
return false;
auto parent = argv[1];
auto type = argv[2];
auto name = argv[3];
int arrsize = 0, offset = -1;
if(argc > 4)
{
duint value;
if(!valfromstring(argv[4], &value, false))
return false;
arrsize = int(value);
if(argc > 5)
{
if(!valfromstring(argv[5], &value, false))
return false;
offset = int(value);
}
}
if(!AddMember(parent, type, name, arrsize, offset))
{
dputs(QT_TRANSLATE_NOOP("DBG", "AddMember failed"));
return false;
}
dprintf_untranslated("%s: %s %s", parent, type, name);
if(arrsize > 0)
dprintf_untranslated("[%d]", arrsize);
if(offset >= 0)
dprintf_untranslated(" (offset: %d)", offset);
dputs_untranslated(";");
return true;
}
bool cbInstrAppendMember(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 3))
return false;
auto type = argv[1];
auto name = argv[2];
int arrsize = 0, offset = -1;
if(argc > 3)
{
duint value;
if(!valfromstring(argv[3], &value, false))
return false;
arrsize = int(value);
if(argc > 4)
{
if(!valfromstring(argv[4], &value, false))
return false;
offset = int(value);
}
}
if(!AppendMember(type, name, arrsize, offset))
{
dputs(QT_TRANSLATE_NOOP("DBG", "AppendMember failed"));
return false;
}
dprintf_untranslated("%s %s", type, name);
if(arrsize > 0)
dprintf_untranslated("[%d]", arrsize);
if(offset >= 0)
dprintf_untranslated(" (offset: %d)", offset);
dputs_untranslated(";");
return true;
}
bool cbInstrAddFunction(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 3))
return false;
auto name = argv[1];
auto rettype = argv[2];
auto callconv = Cdecl;
auto noreturn = false;
if(argc > 3)
{
if(scmp(argv[3], "cdecl"))
callconv = Cdecl;
else if(scmp(argv[3], "stdcall"))
callconv = Stdcall;
else if(scmp(argv[3], "thiscall"))
callconv = Thiscall;
else if(scmp(argv[3], "delphi"))
callconv = Delphi;
else
{
dprintf(QT_TRANSLATE_NOOP("DBG", "Unknown calling convention \"%s\"\n"), argv[3]);
return false;
}
if(argc > 4)
{
duint value;
if(!valfromstring(argv[4], &value, false))
return false;
noreturn = value != 0;
}
}
if(!AddFunction(towner, name, rettype, callconv, noreturn))
{
dputs(QT_TRANSLATE_NOOP("DBG", "AddFunction failed"));
return false;
}
dprintf_untranslated("%s %s();\n", rettype, name);
return true;
}
bool cbInstrAddArg(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 4))
return false;
if(!AddArg(argv[1], argv[2], argv[3]))
{
dputs(QT_TRANSLATE_NOOP("DBG", "AddArg failed"));
return false;
}
dprintf_untranslated("%s: %s %s;\n", argv[1], argv[2], argv[3]);
return true;
}
bool cbInstrAppendArg(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 3))
return false;
if(!AppendArg(argv[1], argv[2]))
{
dputs(QT_TRANSLATE_NOOP("DBG", "AppendArg failed"));
return false;
}
dprintf_untranslated("%s %s;\n", argv[1], argv[2]);
return true;
}
bool cbInstrSizeofType(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 2))
return false;
auto size = SizeofType(argv[1]);
if(!size)
{
dputs(QT_TRANSLATE_NOOP("DBG", "SizeofType failed"));
return false;
}
dprintf_untranslated("sizeof(%s) = %d\n", argv[1], size);
varset("$result", size, false);
return true;
}
struct PrintVisitor : TypeManager::Visitor
{
explicit PrintVisitor(duint data = 0, int maxPtrDepth = 0)
: mAddr(data), mMaxPtrDepth(maxPtrDepth) { }
template<typename T>
static String basicPrint(void* data, const char* format)
{
return StringUtils::sprintf(format, *(T*)data);
}
template<typename T, typename U>
static String basicPrint(void* data, const char* format)
{
return StringUtils::sprintf(format, *(T*)data, *(U*)data);
}
static bool cbPrintPrimitive(const TYPEDESCRIPTOR* type, char* dest, size_t* destCount)
{
if(!type->addr)
{
*dest = '\0';
return true;
}
String valueStr;
Memory<unsigned char*> data(type->size);
if(MemRead(type->addr + type->offset, data(), data.size()))
{
if(type->reverse)
std::reverse(data(), data() + data.size());
switch(Primitive(type->id))
{
case Void:
valueStr.clear();
break;
case Int8:
valueStr += StringUtils::sprintf("0x%02X, '%s'", *(unsigned char*)data(), StringUtils::Escape(*(char*)data()).c_str());
break;
case Uint8:
valueStr += basicPrint<unsigned char, unsigned char>(data(), "0x%02X, %d");
break;
case Int16:
valueStr += basicPrint<unsigned short, short>(data(), "0x%04X, %d");
break;
case Uint16:
valueStr += basicPrint<unsigned short, unsigned short>(data(), "0x%04X, %u");
break;
case Int32:
valueStr += basicPrint<unsigned int, int>(data(), "0x%08X, %d");
break;
case Uint32:
valueStr += basicPrint<unsigned int, unsigned int>(data(), "0x%08X, %u");
break;
case Int64:
valueStr += basicPrint<unsigned long long, long long>(data(), "0x%016llX, %lld");
break;
case Uint64:
valueStr += basicPrint<unsigned long long, unsigned long long>(data(), "0x%016llX, %llu");
break;
case Dsint:
#ifdef _WIN64
valueStr += basicPrint<duint, dsint>(data(), "0x%016llX, %lld");
#else
valueStr += basicPrint<duint, dsint>(data(), "0x%08X, %d");
#endif //_WIN64
break;
case Duint:
#ifdef _WIN64
valueStr += basicPrint<duint, duint>(data(), "0x%016llX, %llu");
#else
valueStr += basicPrint<duint, duint>(data(), "0x%08X, %u");
#endif //_WIN64
break;
case Float:
valueStr += basicPrint<float>(data(), "%f");
break;
case Double:
valueStr += basicPrint<double>(data(), "%f");
break;
case Pointer:
valueStr += basicPrint<void*>(data(), "0x%p");
break;
case PtrString:
{
valueStr += basicPrint<char*>(data(), "0x%p ");
Memory<char*> strdata(MAX_STRING_SIZE + 1);
if(MemRead(*(duint*)data(), strdata(), strdata.size() - 1))
{
valueStr += "\"";
valueStr += StringUtils::Escape(strdata());
valueStr.push_back('\"');
}
else
valueStr += "???";
}
break;
case PtrWString:
{
valueStr += basicPrint<wchar_t*>(data(), "0x%p ");
Memory<wchar_t*> strdata(MAX_STRING_SIZE * 2 + 2);
if(MemRead(*(duint*)data(), strdata(), strdata.size() - 2))
{
valueStr += "L\"";
valueStr += StringUtils::Utf16ToUtf8(strdata());
valueStr.push_back('\"');
}
else
valueStr += "???";
}
break;
default:
return false;
}
}
else
valueStr = "???";
if(*destCount <= valueStr.size())
{
*destCount = valueStr.size() + 1;
return false;
}
strcpy_s(dest, *destCount, valueStr.c_str());
return true;
}
bool visitType(const Member & member, const Type & type) override
{
if(!mParents.empty() && parent().type == Parent::Union)
mOffset = parent().offset;
String tname;
auto ptype = mParents.empty() ? Parent::Struct : parent().type;
if(ptype == Parent::Array)
{
tname = StringUtils::sprintf("%s[%u]", member.name.c_str(), parent().index++);
}
else
{
tname = StringUtils::sprintf("%s %s", type.name.c_str(), member.name.c_str());
// Prepend struct/union to pointer types
if(!type.pointto.empty())
{
auto ptrname = StructUnionPtrType(type.pointto);
if(!ptrname.empty())
tname = ptrname + " " + tname;
}
}
std::string path;
for(size_t i = 0; i < mPath.size(); i++)
{
if(ptype == Parent::Array && i + 1 == mPath.size())
break;
path.append(mPath[i]);
}
path.append(member.name);
auto ptr = mAddr + mOffset;
if(MemIsValidReadPtr(ptr))
{
if(!LabelGet(ptr, nullptr) && (parent().index == 1 || ptype != Parent::Array))
LabelSet(ptr, path.c_str(), false, true);
}
TYPEDESCRIPTOR td;
td.expanded = false;
td.reverse = false;
td.name = tname.c_str();
td.addr = mAddr;
td.offset = mOffset;
td.id = type.primitive;
td.size = type.size;
td.callback = cbPrintPrimitive;
td.userdata = nullptr;
mNode = GuiTypeAddNode(mParents.empty() ? nullptr : parent().node, &td);
mOffset += type.size;
return true;
}
bool visitStructUnion(const Member & member, const StructUnion & type) override
{
if(!mParents.empty() && parent().type == Parent::Type::Union)
mOffset = parent().offset;
String tname = StringUtils::sprintf("%s %s %s", type.isunion ? "union" : "struct", type.name.c_str(), member.name.c_str());
TYPEDESCRIPTOR td;
td.expanded = true;
td.reverse = false;
td.name = tname.c_str();
td.addr = mAddr;
td.offset = mOffset;
td.id = Void;
td.size = type.size;
td.callback = nullptr;
td.userdata = nullptr;
auto node = GuiTypeAddNode(mParents.empty() ? nullptr : parent().node, &td);
mPath.push_back((member.name == "visit" ? type.name : member.name) + ".");
mParents.push_back(Parent(type.isunion ? Parent::Union : Parent::Struct));
parent().node = node;
parent().size = td.size;
parent().offset = mOffset;
return true;
}
bool visitArray(const Member & member) override
{
String tname = StringUtils::sprintf("%s %s[%d]", member.type.c_str(), member.name.c_str(), member.arrsize);
TYPEDESCRIPTOR td;
td.expanded = member.arrsize <= 5;
td.reverse = false;
td.name = tname.c_str();
td.addr = mAddr;
td.offset = mOffset;
td.id = Void;
td.size = member.arrsize * SizeofType(member.type);
td.callback = nullptr;
td.userdata = nullptr;
auto node = GuiTypeAddNode(mParents.empty() ? nullptr : parent().node, &td);
mPath.push_back(member.name + ".");
mParents.push_back(Parent(Parent::Array));
parent().node = node;
parent().size = td.size;
return true;
}
bool visitPtr(const Member & member, const Type & type) override
{
auto offset = mOffset;
auto res = visitType(member, type); //print the pointer value
if(mPtrDepth >= mMaxPtrDepth)
return false;
duint value = 0;
if(!mAddr || !MemRead(mAddr + offset, &value, sizeof(value)))
return false;
mPath.push_back(member.name + "->");
mParents.push_back(Parent(Parent::Pointer));
parent().offset = mOffset;
parent().addr = mAddr;
parent().node = mNode;
parent().size = type.size;
mOffset = 0;
mAddr = value;
mPtrDepth++;
return res;
}
bool visitBack(const Member & member) override
{
if(parent().type == Parent::Pointer)
{
mOffset = parent().offset;
mAddr = parent().addr;
mPtrDepth--;
}
else if(parent().type == Parent::Union)
{
mOffset = parent().offset + parent().size;
}
mParents.pop_back();
mPath.pop_back();
return true;
}
private:
struct Parent
{
enum Type
{
Struct,
Union,
Array,
Pointer
};
Type type;
unsigned int index = 0;
duint addr = 0;
duint offset = 0;
void* node = nullptr;
int size = 0;
explicit Parent(Type type)
: type(type) { }
};
Parent & parent()
{
return mParents[mParents.size() - 1];
}
std::vector<Parent> mParents;
duint mOffset = 0;
duint mAddr = 0;
int mPtrDepth = 0;
int mMaxPtrDepth = 0;
void* mNode = nullptr;
std::vector<String> mPath;
};
bool cbInstrVisitType(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 2))
return false;
auto type = argv[1];
auto name = "visit";
duint addr = 0;
auto maxPtrDepth = 0;
if(argc > 2)
{
if(!valfromstring(argv[2], &addr, false))
return false;
if(argc > 3)
{
duint value;
if(!valfromstring(argv[3], &value, false))
return false;
maxPtrDepth = int(value);
if(argc > 4)
name = argv[4];
}
}
PrintVisitor visitor(addr, maxPtrDepth);
if(!VisitType(type, name, visitor))
{
dputs(QT_TRANSLATE_NOOP("DBG", "VisitType failed"));
return false;
}
GuiUpdateAllViews();
dputs(QT_TRANSLATE_NOOP("DBG", "Done!"));
return true;
}
bool cbInstrClearTypes(int argc, char* argv[])
{
auto owner = towner;
if(argc > 1)
owner = argv[1];
ClearTypes(owner);
return true;
}
bool cbInstrRemoveType(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 2))
return false;
if(!RemoveType(argv[1]))
{
dputs(QT_TRANSLATE_NOOP("DBG", "RemoveType failed"));
return false;
}
dprintf(QT_TRANSLATE_NOOP("DBG", "Type %s removed\n"), argv[1]);
return true;
}
bool cbInstrEnumTypes(int argc, char* argv[])
{
std::vector<TypeManager::Summary> typeList;
EnumTypes(typeList);
for(auto & type : typeList)
{
if(type.owner.empty())
type.owner.assign("x64dbg");
dprintf_untranslated("%s: %s %s, sizeof(%s) = %d\n", type.owner.c_str(), type.kind.c_str(), type.name.c_str(), type.name.c_str(), type.size);
}
return true;
}
bool cbInstrLoadTypes(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 2))
return false;
auto owner = FileHelper::GetFileName(argv[1]);
ClearTypes(owner);
if(!LoadTypesFile(argv[1], owner))
{
dputs(QT_TRANSLATE_NOOP("DBG", "LoadTypes failed"));
return false;
}
dputs(QT_TRANSLATE_NOOP("DBG", "Types loaded"));
return true;
}
bool cbInstrParseTypes(int argc, char* argv[])
{
if(IsArgumentsLessThan(argc, 2))
return false;
auto owner = FileHelper::GetFileName(argv[1]);
ClearTypes(owner);
std::string data;
if(!FileHelper::ReadAllText(argv[1], data))
{
dputs("failed to read file!");
return false;
}
if(!ParseTypes(data, owner))
return false;
dprintf("Parsed header: %s\n", argv[1]);
return true;
}