mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2026-05-27 23:17:03 +08:00
Merge remote-tracking branch 'origin/Ghidra_9.2'
This commit is contained in:
@@ -21,7 +21,7 @@ void listSupportedArchMachTargets(void)
|
|||||||
const char** targetList;
|
const char** targetList;
|
||||||
const char** archList;
|
const char** archList;
|
||||||
int i, j;
|
int i, j;
|
||||||
|
|
||||||
targetList = bfd_target_list();
|
targetList = bfd_target_list();
|
||||||
if(targetList != NULL){
|
if(targetList != NULL){
|
||||||
for(i=0, j=0; targetList[i] !=0; i++){
|
for(i=0, j=0; targetList[i] !=0; i++){
|
||||||
@@ -29,7 +29,7 @@ void listSupportedArchMachTargets(void)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
printf("\ndone with targetList.\n");
|
printf("\ndone with targetList.\n");
|
||||||
|
|
||||||
archList = bfd_arch_list();
|
archList = bfd_arch_list();
|
||||||
if(archList != NULL){
|
if(archList != NULL){
|
||||||
for(i=0, j=0; archList[i] !=0; i++){
|
for(i=0, j=0; archList[i] !=0; i++){
|
||||||
@@ -44,29 +44,29 @@ void listSupportedArchMachTargets(void)
|
|||||||
/* sprintf to a "stream". */
|
/* sprintf to a "stream". */
|
||||||
int objdump_sprintf (SFILE *f, const char *format, ...)
|
int objdump_sprintf (SFILE *f, const char *format, ...)
|
||||||
{
|
{
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
size_t n;
|
size_t n;
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
va_start (args, format);
|
va_start (args, format);
|
||||||
n = vsnprintf (f->buffer + f->pos, BUFF_SIZE, format, args);
|
n = vsnprintf (f->buffer + f->pos, BUFF_SIZE, format, args);
|
||||||
strncat(disassembled_buffer, f->buffer, n);
|
strncat(disassembled_buffer, f->buffer, n);
|
||||||
va_end (args);
|
va_end (args);
|
||||||
|
|
||||||
return n;
|
return n;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void configureDisassembleInfo(bfd* abfd,
|
void configureDisassembleInfo(bfd* abfd,
|
||||||
disassemble_info* info,
|
disassemble_info* info,
|
||||||
enum bfd_architecture arch,
|
enum bfd_architecture arch,
|
||||||
unsigned long mach,
|
unsigned long mach,
|
||||||
enum bfd_endian end)
|
enum bfd_endian end)
|
||||||
{
|
{
|
||||||
|
|
||||||
memset(sfile.buffer, 0x00, BUFF_SIZE);
|
memset(sfile.buffer, 0x00, BUFF_SIZE);
|
||||||
|
|
||||||
INIT_DISASSEMBLE_INFO(*info, stdout, objdump_sprintf);
|
INIT_DISASSEMBLE_INFO(*info, stdout, objdump_sprintf);
|
||||||
info->arch = (enum bfd_architecture) arch;
|
info->arch = (enum bfd_architecture) arch;
|
||||||
info->mach = mach;
|
info->mach = mach;
|
||||||
@@ -79,24 +79,24 @@ void configureDisassembleInfo(bfd* abfd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
disassembler_ftype configureBfd(bfd* abfd,
|
disassembler_ftype configureBfd(bfd* abfd,
|
||||||
enum bfd_architecture arch,
|
enum bfd_architecture arch,
|
||||||
unsigned long mach,
|
unsigned long mach,
|
||||||
enum bfd_endian endian,
|
enum bfd_endian endian,
|
||||||
disassemble_info* DI,
|
disassemble_info* DI,
|
||||||
disassembler_ftype* disassemble_fn)
|
disassembler_ftype* disassemble_fn)
|
||||||
{
|
{
|
||||||
struct bfd_target *xvec;
|
struct bfd_target *xvec;
|
||||||
|
|
||||||
abfd->flags |= EXEC_P;
|
abfd->flags |= EXEC_P;
|
||||||
|
|
||||||
|
|
||||||
// set up xvec byteorder.
|
// set up xvec byteorder.
|
||||||
xvec = (struct bfd_target *) malloc (sizeof (struct bfd_target));
|
xvec = (struct bfd_target *) malloc (sizeof (struct bfd_target));
|
||||||
memset(xvec, 0x00, sizeof (struct bfd_target));
|
memset(xvec, 0x00, sizeof (struct bfd_target));
|
||||||
memcpy (xvec, abfd->xvec, sizeof (struct bfd_target));
|
memcpy (xvec, abfd->xvec, sizeof (struct bfd_target));
|
||||||
xvec->byteorder = endian;
|
xvec->byteorder = endian;
|
||||||
abfd->xvec = xvec;
|
abfd->xvec = xvec;
|
||||||
|
|
||||||
configureDisassembleInfo(abfd, DI, arch, mach, endian);
|
configureDisassembleInfo(abfd, DI, arch, mach, endian);
|
||||||
if(endian == BFD_ENDIAN_BIG){
|
if(endian == BFD_ENDIAN_BIG){
|
||||||
bfd_big_endian(abfd);
|
bfd_big_endian(abfd);
|
||||||
@@ -106,29 +106,29 @@ disassembler_ftype configureBfd(bfd* abfd,
|
|||||||
bfd_little_endian(abfd);
|
bfd_little_endian(abfd);
|
||||||
DI->display_endian = DI->endian = BFD_ENDIAN_LITTLE;
|
DI->display_endian = DI->endian = BFD_ENDIAN_LITTLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
bfd_error_type err = bfd_get_error();
|
bfd_error_type err = bfd_get_error();
|
||||||
printf("bfd_error_msg: %s.\n", bfd_errmsg(err));
|
printf("bfd_error_msg: %s.\n", bfd_errmsg(err));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Use libopcodes to locate a suitable disassembler. */
|
/* Use libopcodes to locate a suitable disassembler. */
|
||||||
*disassemble_fn = NULL;
|
*disassemble_fn = NULL;
|
||||||
*disassemble_fn = disassembler (arch, endian == BFD_ENDIAN_BIG, mach, abfd);
|
*disassemble_fn = disassembler (arch, endian == BFD_ENDIAN_BIG, mach, abfd);
|
||||||
if (!*disassemble_fn){
|
if (!*disassemble_fn){
|
||||||
printf("can't disassemble for arch 0x%08X, mach 0x%08lX\n", arch, mach);
|
printf("can't disassemble for arch 0x%08X, mach 0x%08lX\n", arch, mach);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return *disassemble_fn;
|
return *disassemble_fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
int disassemble_buffer( disassembler_ftype disassemble_fn,
|
int disassemble_buffer( disassembler_ftype disassemble_fn,
|
||||||
disassemble_info *info,
|
disassemble_info *info,
|
||||||
int* offset,
|
int* offset,
|
||||||
PDIS_INFO pDisInfo)
|
PDIS_INFO pDisInfo)
|
||||||
{
|
{
|
||||||
int i, j, size = 0;
|
int i, j, size = 0;
|
||||||
int len = 0;
|
int len = 0;
|
||||||
@@ -136,7 +136,7 @@ int disassemble_buffer( disassembler_ftype disassemble_fn,
|
|||||||
while ( *offset < info->buffer_length ) {
|
while ( *offset < info->buffer_length ) {
|
||||||
/* call the libopcodes disassembler */
|
/* call the libopcodes disassembler */
|
||||||
memset(pDisInfo->disassemblyString, 0x00, MAX_DIS_STRING);
|
memset(pDisInfo->disassemblyString, 0x00, MAX_DIS_STRING);
|
||||||
|
|
||||||
/* set the insn_info_valid bit to 0, as explained in BFD's
|
/* set the insn_info_valid bit to 0, as explained in BFD's
|
||||||
* include/dis-asm.h. The bit will then be set to tell us
|
* include/dis-asm.h. The bit will then be set to tell us
|
||||||
* whether the decoder supports "extra" information about the
|
* whether the decoder supports "extra" information about the
|
||||||
@@ -147,7 +147,7 @@ int disassemble_buffer( disassembler_ftype disassemble_fn,
|
|||||||
size = (*disassemble_fn)(info->buffer_vma + *offset, info);
|
size = (*disassemble_fn)(info->buffer_vma + *offset, info);
|
||||||
/* -- analyze disassembled instruction here -- */
|
/* -- analyze disassembled instruction here -- */
|
||||||
/* -- print any symbol names as labels here -- */
|
/* -- print any symbol names as labels here -- */
|
||||||
|
|
||||||
/* save off corresponding hex bytes */
|
/* save off corresponding hex bytes */
|
||||||
for ( j= 0,i = 0; i < 8; i++, j+=3) {
|
for ( j= 0,i = 0; i < 8; i++, j+=3) {
|
||||||
if ( i < size ){
|
if ( i < size ){
|
||||||
@@ -155,7 +155,7 @@ int disassemble_buffer( disassembler_ftype disassemble_fn,
|
|||||||
pDisInfo->bytesBufferBin[i] = info->buffer[*offset + i];
|
pDisInfo->bytesBufferBin[i] = info->buffer[*offset + i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* add the augmented information to our disassembly info struct */
|
/* add the augmented information to our disassembly info struct */
|
||||||
pDisInfo->count = size;
|
pDisInfo->count = size;
|
||||||
pDisInfo->insn_info_valid = info->insn_info_valid;
|
pDisInfo->insn_info_valid = info->insn_info_valid;
|
||||||
@@ -167,44 +167,44 @@ int disassemble_buffer( disassembler_ftype disassemble_fn,
|
|||||||
|
|
||||||
strcat(&(pDisInfo->disassemblyString[0]), disassembled_buffer);
|
strcat(&(pDisInfo->disassemblyString[0]), disassembled_buffer);
|
||||||
memset(disassembled_buffer, 0x00, BUFF_SIZE);
|
memset(disassembled_buffer, 0x00, BUFF_SIZE);
|
||||||
|
|
||||||
if(size != 0){
|
if(size != 0){
|
||||||
*offset += size; /* advance position in buffer */
|
*offset += size; /* advance position in buffer */
|
||||||
goto END;
|
goto END;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
END:
|
END:
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void processBuffer(unsigned char* buff,
|
void processBuffer(unsigned char* buff,
|
||||||
int buff_len,
|
int buff_len,
|
||||||
bfd_vma buff_vma,
|
bfd_vma buff_vma,
|
||||||
disassembler_ftype disassemble_fn,
|
disassembler_ftype disassemble_fn,
|
||||||
struct disassemble_info* DI)
|
struct disassemble_info* DI)
|
||||||
{
|
{
|
||||||
int bytesConsumed = -1;
|
int bytesConsumed = -1;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
int numDisassemblies = 0;
|
int numDisassemblies = 0;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
DI->buffer = buff; /* buffer of bytes to disassemble */
|
DI->buffer = buff; /* buffer of bytes to disassemble */
|
||||||
DI->buffer_length = buff_len; /* size of buffer */
|
DI->buffer_length = buff_len; /* size of buffer */
|
||||||
DI->buffer_vma = buff_vma; /* base RVA of buffer */
|
DI->buffer_vma = buff_vma; /* base RVA of buffer */
|
||||||
|
|
||||||
memset(disassemblyInfoBuffer, 0x00, sizeof(DIS_INFO)*MAX_NUM_ENTRIES);
|
memset(disassemblyInfoBuffer, 0x00, sizeof(DIS_INFO)*MAX_NUM_ENTRIES);
|
||||||
|
|
||||||
while((buff_len - offset) > 0 && bytesConsumed != 0 && numDisassemblies < MAX_NUM_ENTRIES){
|
while((buff_len - offset) > 0 && bytesConsumed != 0 && numDisassemblies < MAX_NUM_ENTRIES){
|
||||||
bytesConsumed = disassemble_buffer( disassemble_fn, DI, &offset, &(disassemblyInfoBuffer[numDisassemblies++]));
|
bytesConsumed = disassemble_buffer( disassemble_fn, DI, &offset, &(disassemblyInfoBuffer[numDisassemblies++]));
|
||||||
}
|
}
|
||||||
for (i = 0; i < numDisassemblies; i++) {
|
for (i = 0; i < numDisassemblies; i++) {
|
||||||
printf("%s\nInfo: %d,%d,%d,%d,%d\n", disassemblyInfoBuffer[i].disassemblyString,
|
printf("%s\nInfo: %d,%d,%d,%d,%d\n", disassemblyInfoBuffer[i].disassemblyString,
|
||||||
disassemblyInfoBuffer[i].count,
|
disassemblyInfoBuffer[i].count,
|
||||||
disassemblyInfoBuffer[i].insn_info_valid,
|
disassemblyInfoBuffer[i].insn_info_valid,
|
||||||
disassemblyInfoBuffer[i].branch_delay_insns,
|
disassemblyInfoBuffer[i].branch_delay_insns,
|
||||||
disassemblyInfoBuffer[i].data_size,
|
disassemblyInfoBuffer[i].data_size,
|
||||||
disassemblyInfoBuffer[i].insn_type);
|
disassemblyInfoBuffer[i].insn_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,7 +224,7 @@ int main(int argc, char* argv[]){
|
|||||||
char elf_file_location[MAX_ELF_FILE_PATH_LEN];
|
char elf_file_location[MAX_ELF_FILE_PATH_LEN];
|
||||||
char arch_str[256];
|
char arch_str[256];
|
||||||
char mach_str[256];
|
char mach_str[256];
|
||||||
|
|
||||||
if ( argc < 8) {
|
if ( argc < 8) {
|
||||||
fprintf(stderr, "Usage: %s target-str, arch, mach, disassembly base-addr (for rel offsets instrs), full-path to Little and Big Elfs, big/little ascii-byte-string up to %d chars\n", argv[0], MAX_ASCII_CHAR_BYTE_STRING);
|
fprintf(stderr, "Usage: %s target-str, arch, mach, disassembly base-addr (for rel offsets instrs), full-path to Little and Big Elfs, big/little ascii-byte-string up to %d chars\n", argv[0], MAX_ASCII_CHAR_BYTE_STRING);
|
||||||
listSupportedArchMachTargets();
|
listSupportedArchMachTargets();
|
||||||
@@ -247,16 +247,16 @@ int main(int argc, char* argv[]){
|
|||||||
mach = 0x00000000;
|
mach = 0x00000000;
|
||||||
arch = (enum bfd_architecture) 0x00;
|
arch = (enum bfd_architecture) 0x00;
|
||||||
offset = 0x00000000;
|
offset = 0x00000000;
|
||||||
|
|
||||||
sscanf(argv[2], "%128s", arch_str);
|
sscanf(argv[2], "%128s", arch_str);
|
||||||
sscanf(argv[3], "%18lX", &mach);
|
sscanf(argv[3], "%18lX", &mach);
|
||||||
sscanf(argv[4], "%10X", &end);
|
sscanf(argv[4], "%10X", &end);
|
||||||
sscanf(argv[5], "%18lX", &offset);
|
sscanf(argv[5], "%18lX", &offset);
|
||||||
|
|
||||||
// if arch starts with 0x, then parse a number
|
// if arch starts with 0x, then parse a number
|
||||||
// else lookup the string in the table to get the arch, ignore the mach
|
// else lookup the string in the table to get the arch, ignore the mach
|
||||||
if (arch_str[0] == '0' && arch_str[1] == 'x') {
|
if (arch_str[0] == '0' && arch_str[1] == 'x') {
|
||||||
sscanf(arch_str, "%10X", &arch);
|
sscanf(arch_str, "%10X", &arch);
|
||||||
} else {
|
} else {
|
||||||
const char** archList = bfd_arch_list();
|
const char** archList = bfd_arch_list();
|
||||||
const bfd_arch_info_type* ait;
|
const bfd_arch_info_type* ait;
|
||||||
@@ -271,17 +271,17 @@ int main(int argc, char* argv[]){
|
|||||||
archList++;
|
archList++;
|
||||||
}
|
}
|
||||||
if (ait == NULL) {
|
if (ait == NULL) {
|
||||||
printf("Couldn't find arch %s\n", arch_str);
|
printf("Couldn't find arch %s\n", arch_str);
|
||||||
return(-1);
|
return(-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
endian = (enum bfd_endian) end;
|
endian = (enum bfd_endian) end;
|
||||||
/* open a correct type of file to fill in most of the required data. */
|
/* open a correct type of file to fill in most of the required data. */
|
||||||
|
|
||||||
// printf("Arch is: 0x%08X, Machine is: 0x%08lX Endian is: 0x%02X.\n", arch, mach, endian);
|
// printf("Arch is: 0x%08X, Machine is: 0x%08lX Endian is: 0x%02X.\n", arch, mach, endian);
|
||||||
|
|
||||||
memset(elf_file_location, 0x00, MAX_ELF_FILE_PATH_LEN);
|
memset(elf_file_location, 0x00, MAX_ELF_FILE_PATH_LEN);
|
||||||
strncpy(elf_file_location, argv[6], MAX_ELF_FILE_PATH_LEN-sizeof(LITTLE_ELF_FILE)-2); // actual file name and nulls
|
strncpy(elf_file_location, argv[6], MAX_ELF_FILE_PATH_LEN-sizeof(LITTLE_ELF_FILE)-2); // actual file name and nulls
|
||||||
|
|
||||||
@@ -294,59 +294,66 @@ int main(int argc, char* argv[]){
|
|||||||
stdin_mode = 1; // use STDIN
|
stdin_mode = 1; // use STDIN
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned char byteBuffer[BYTE_BUFFER_SIZE];
|
|
||||||
char byteStringBuffer[(BYTE_BUFFER_SIZE*2)];
|
|
||||||
|
|
||||||
char addressStringBuffer[128];
|
|
||||||
|
|
||||||
if (endian == BFD_ENDIAN_BIG){
|
if (endian == BFD_ENDIAN_BIG){
|
||||||
strcat(elf_file_location, BIG_ELF_FILE);
|
strcat(elf_file_location, BIG_ELF_FILE);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
strcat(elf_file_location, LITTLE_ELF_FILE);
|
strcat(elf_file_location, LITTLE_ELF_FILE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bfd_init();
|
||||||
|
|
||||||
while (stdin_mode) {
|
while (stdin_mode) {
|
||||||
|
|
||||||
|
//bfd_init();
|
||||||
// convert user input AsciiHex to Binary data for processing
|
// convert user input AsciiHex to Binary data for processing
|
||||||
char tmp[3];
|
char tmp[3];
|
||||||
unsigned int byteValue;
|
unsigned int byteValue;
|
||||||
tmp[0] = tmp[1] = tmp[2] = 0x00;
|
tmp[0] = tmp[1] = tmp[2] = 0x00;
|
||||||
|
char disassemblerOptions[128] = {0};
|
||||||
|
unsigned char byteBuffer[BYTE_BUFFER_SIZE] = {0};
|
||||||
|
char byteStringBuffer[(BYTE_BUFFER_SIZE*2)] = {0};
|
||||||
|
char addressStringBuffer[128] = {0};
|
||||||
|
char bytesAndOptionsBuffer[BYTE_BUFFER_SIZE*2 + sizeof(disassemblerOptions)] = {0};
|
||||||
|
|
||||||
if (stdin_mode == 1) { // use stdin
|
if (stdin_mode == 1) { // use stdin
|
||||||
// read in the address
|
// read in the address
|
||||||
if (fgets(addressStringBuffer, sizeof(addressStringBuffer), stdin)) {
|
if (fgets(addressStringBuffer, sizeof(addressStringBuffer), stdin)) {
|
||||||
|
|
||||||
//fprintf(stderr, "read: %s\n", addressStringBuffer);
|
|
||||||
//char *p = strchr(addressStringBuffer, '\n');
|
|
||||||
//if (p) {
|
|
||||||
// *p = '\0';
|
|
||||||
//}
|
|
||||||
|
|
||||||
sscanf(addressStringBuffer, "%18lX", &offset);
|
sscanf(addressStringBuffer, "%18lX", &offset);
|
||||||
}
|
}
|
||||||
//getchar();
|
//Read in the rest of the input. There are two possible styles:
|
||||||
// read in the ASCII hex string from stdin
|
//"old": input is just the bytes to disassemble
|
||||||
if (fgets(byteStringBuffer, sizeof(byteStringBuffer), stdin)) {
|
//"new": input = bytes"*"<disassmbler_options>
|
||||||
|
//for compatibility reasons, handle both styles
|
||||||
//fprintf(stderr, "read: %s\n", byteStringBuffer);
|
if (fgets(bytesAndOptionsBuffer, sizeof(bytesAndOptionsBuffer), stdin)) {
|
||||||
// remove trailing newline
|
char *p = strchr(bytesAndOptionsBuffer, '*');
|
||||||
char *p = strchr(byteStringBuffer, '\n');
|
|
||||||
if (p) {
|
if (p) {
|
||||||
*p = '\0';
|
strncpy(byteStringBuffer,bytesAndOptionsBuffer,(int) (p - bytesAndOptionsBuffer));
|
||||||
|
strncpy(disassemblerOptions,p+1,sizeof(disassemblerOptions));
|
||||||
|
p = strchr(disassemblerOptions,'\n');
|
||||||
|
if (p){
|
||||||
|
*p = '\0';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//no "*" in the string, so no disassembly options
|
||||||
|
//replace newline with null terminator
|
||||||
|
strncpy(byteStringBuffer, bytesAndOptionsBuffer, sizeof(byteStringBuffer));
|
||||||
|
p = strchr(byteStringBuffer,'\n');
|
||||||
|
if (p){
|
||||||
|
*p = '\0';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//if (strcmp(byteStringBuffer, "EOF") == 0) {
|
|
||||||
//return 0; // terminate on EOF string
|
|
||||||
//}
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "exiting, no ASCII hex found\n");
|
|
||||||
return 0; // finished! #TODO
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
} else {
|
fprintf(stderr, "exiting, no ASCII hex found\n");
|
||||||
|
return 0; // finished! #TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
if(strlen(byteString) > BYTE_BUFFER_SIZE*2) {
|
if(strlen(byteString) > BYTE_BUFFER_SIZE*2) {
|
||||||
fprintf(stderr, "Max ascii string size is %d you provided: %lu chars. Exiting.\n", BYTE_BUFFER_SIZE*2,
|
fprintf(stderr, "Max ascii string size is %d you provided: %lu chars. Exiting.\n", BYTE_BUFFER_SIZE*2,
|
||||||
strlen(byteString));
|
strlen(byteString));
|
||||||
exit(-1);
|
exit(-1);
|
||||||
}
|
}
|
||||||
strncpy(byteStringBuffer, byteString, BYTE_BUFFER_SIZE*2);
|
strncpy(byteStringBuffer, byteString, BYTE_BUFFER_SIZE*2);
|
||||||
@@ -378,9 +385,9 @@ int main(int argc, char* argv[]){
|
|||||||
for(j=0; j < BYTE_BUFFER_SIZE; j++){
|
for(j=0; j < BYTE_BUFFER_SIZE; j++){
|
||||||
printf("0x%02X ", byteBuffer[j]);
|
printf("0x%02X ", byteBuffer[j]);
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
bfd_init( );
|
//bfd_init( );
|
||||||
target = argv[1];
|
target = argv[1];
|
||||||
bfd_set_default_target(target);
|
bfd_set_default_target(target);
|
||||||
|
|
||||||
@@ -392,7 +399,7 @@ int main(int argc, char* argv[]){
|
|||||||
bfdfile = bfd_openr(elf_file_location, target );
|
bfdfile = bfd_openr(elf_file_location, target );
|
||||||
if ( ! bfdfile ) {
|
if ( ! bfdfile ) {
|
||||||
printf("Error opening BIG ELF file: %s\n", elf_file_location);
|
printf("Error opening BIG ELF file: %s\n", elf_file_location);
|
||||||
bfd_perror( "Error on bfdfile" );
|
bfd_perror( "Error on bfdfile" );
|
||||||
return(3);
|
return(3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -401,34 +408,37 @@ int main(int argc, char* argv[]){
|
|||||||
if ( ! bfdfile ) {
|
if ( ! bfdfile ) {
|
||||||
printf("Error opening LITTLE ELF file: %s\n", elf_file_location);
|
printf("Error opening LITTLE ELF file: %s\n", elf_file_location);
|
||||||
// bfdfile = bfd_openr(elf_file_location, target );
|
// bfdfile = bfd_openr(elf_file_location, target );
|
||||||
bfd_perror( "Error on bfdfile" );
|
bfd_perror( "Error on bfdfile" );
|
||||||
return(3);
|
return(3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memset((void*) &DI, 0x00, sizeof(struct disassemble_info));
|
memset((void*) &DI, 0x00, sizeof(struct disassemble_info));
|
||||||
|
|
||||||
disassemble_fn = NULL;
|
disassemble_fn = NULL;
|
||||||
|
|
||||||
// important set up!
|
// important set up!
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
ai.arch = arch;
|
ai.arch = arch;
|
||||||
ai.mach = mach;
|
ai.mach = mach;
|
||||||
bfd_set_arch_info(bfdfile, &ai);
|
bfd_set_arch_info(bfdfile, &ai);
|
||||||
//---------------------------------------
|
//---------------------------------------
|
||||||
|
|
||||||
/*
|
/*
|
||||||
bfd_error_type err = bfd_get_error();
|
bfd_error_type err = bfd_get_error();
|
||||||
printf("bfd_error_msg: %s.\n", bfd_errmsg(err));
|
printf("bfd_error_msg: %s.\n", bfd_errmsg(err));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*if (disassemblerOptions[0] != '\0'){
|
||||||
|
DI.disassembler_options = disassemblerOptions;
|
||||||
|
}*/
|
||||||
configureBfd(bfdfile, arch, mach, endian, &DI, &disassemble_fn);
|
configureBfd(bfdfile, arch, mach, endian, &DI, &disassemble_fn);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
err = bfd_get_error();
|
err = bfd_get_error();
|
||||||
printf("bfd_error_msg: %s.\n", bfd_errmsg(err));
|
printf("bfd_error_msg: %s.\n", bfd_errmsg(err));
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (disassemble_fn == NULL){
|
if (disassemble_fn == NULL){
|
||||||
fprintf(stderr, "Error: disassemble_fn is NULL. Nothing I can do.\n");
|
fprintf(stderr, "Error: disassemble_fn is NULL. Nothing I can do.\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -437,21 +447,25 @@ int main(int argc, char* argv[]){
|
|||||||
/*
|
/*
|
||||||
printf("the disassemble_fn func pointer is: 0x%08X.\n", disassemble_fn);
|
printf("the disassemble_fn func pointer is: 0x%08X.\n", disassemble_fn);
|
||||||
printf("We can try to disassemble for this arch/mach. calling disassemble_init_for_target().\n");
|
printf("We can try to disassemble for this arch/mach. calling disassemble_init_for_target().\n");
|
||||||
*/
|
*/
|
||||||
|
|
||||||
disassemble_init_for_target(&DI);
|
disassemble_init_for_target(&DI);
|
||||||
|
if (disassemblerOptions[0] != '\0'){
|
||||||
|
DI.disassembler_options = disassemblerOptions;
|
||||||
|
}
|
||||||
|
|
||||||
// go diassemble the buffer and build up the result in a accumulator string buffer.
|
// go diassemble the buffer and build up the result in a accumulator string buffer.
|
||||||
processBuffer(byteBuffer, size >> 1, offset, disassemble_fn, &DI); //
|
processBuffer(byteBuffer, size >> 1, offset, disassemble_fn, &DI); //
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
free((void*)bfdfile->xvec);
|
free((void*)bfdfile->xvec);
|
||||||
bfd_close(bfdfile);
|
bfd_close(bfdfile);
|
||||||
|
|
||||||
printf("EOF\n");
|
printf("EOF\n");
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
|
|
||||||
} // while loop on lines of stdin
|
} // while loop on lines of stdin
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -23,6 +23,8 @@ public interface ExternalDisassembler extends ExtensionPoint {
|
|||||||
|
|
||||||
public String getDisassembly(CodeUnit cu) throws Exception;
|
public String getDisassembly(CodeUnit cu) throws Exception;
|
||||||
|
|
||||||
|
public String getDisassemblyDisplayPrefix(CodeUnit cu) throws Exception;
|
||||||
|
|
||||||
public String getDisassemblyOfBytes(Language language, boolean isBigEndian, long address,
|
public String getDisassemblyOfBytes(Language language, boolean isBigEndian, long address,
|
||||||
byte[] byteString) throws Exception;
|
byte[] byteString) throws Exception;
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -150,7 +150,10 @@ public class ExternalDisassemblyFieldFactory extends FieldFactory {
|
|||||||
if (disassembly == null) {
|
if (disassembly == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
String prefix = disassembler.getDisassemblyDisplayPrefix(cu);
|
||||||
|
if (prefix != null) {
|
||||||
|
disassembly = prefix + " " + disassembly;
|
||||||
|
}
|
||||||
return disassembly;
|
return disassembly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+205
-15
@@ -18,19 +18,23 @@ package ghidra.app.util.disassemble;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.jdom.*;
|
||||||
|
import org.jdom.input.SAXBuilder;
|
||||||
|
|
||||||
import generic.jar.ResourceFile;
|
import generic.jar.ResourceFile;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.ByteProvider;
|
||||||
import ghidra.app.util.bin.MemoryByteProvider;
|
import ghidra.app.util.bin.MemoryByteProvider;
|
||||||
import ghidra.framework.*;
|
import ghidra.framework.*;
|
||||||
import ghidra.program.model.address.Address;
|
import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressOutOfBoundsException;
|
import ghidra.program.model.address.AddressOutOfBoundsException;
|
||||||
import ghidra.program.model.lang.Language;
|
import ghidra.program.model.lang.*;
|
||||||
import ghidra.program.model.lang.LanguageID;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.listing.CodeUnit;
|
|
||||||
import ghidra.program.model.listing.Program;
|
|
||||||
import ghidra.program.model.mem.MemBuffer;
|
import ghidra.program.model.mem.MemBuffer;
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
import ghidra.program.model.mem.MemoryAccessException;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.xml.XmlUtilities;
|
||||||
|
import util.CollectionUtils;
|
||||||
|
|
||||||
public class GNUExternalDisassembler implements ExternalDisassembler {
|
public class GNUExternalDisassembler implements ExternalDisassembler {
|
||||||
|
|
||||||
@@ -39,6 +43,7 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
// magic values for gdis that direct it to read bytes from stdin
|
// magic values for gdis that direct it to read bytes from stdin
|
||||||
private static final String READ_FROM_STDIN_PARAMETER = "stdin";
|
private static final String READ_FROM_STDIN_PARAMETER = "stdin";
|
||||||
private static final String SEPARATOR_CHARACTER = "\n";
|
private static final String SEPARATOR_CHARACTER = "\n";
|
||||||
|
private static final String OPTIONS_SEPARATOR = "*";
|
||||||
private static final String ADDRESS_OUT_OF_BOUNDS = "is out of bounds.";
|
private static final String ADDRESS_OUT_OF_BOUNDS = "is out of bounds.";
|
||||||
private static final String ENDING_STRING = "EOF";
|
private static final String ENDING_STRING = "EOF";
|
||||||
private static final int NUM_BYTES = 32;
|
private static final int NUM_BYTES = 32;
|
||||||
@@ -48,6 +53,8 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
private static final String GDIS_EXE =
|
private static final String GDIS_EXE =
|
||||||
Platform.CURRENT_PLATFORM.getOperatingSystem() == OperatingSystem.WINDOWS ? "gdis.exe"
|
Platform.CURRENT_PLATFORM.getOperatingSystem() == OperatingSystem.WINDOWS ? "gdis.exe"
|
||||||
: "gdis";
|
: "gdis";
|
||||||
|
private static final String EMPTY_DISASSEMBLER_OPTIONS = "";
|
||||||
|
private static final String GDIS_OPTIONS_FILENAME_PROPERTY = "gdis.disassembler.options.file";
|
||||||
|
|
||||||
private static HashMap<String, File> languageGdisMap;
|
private static HashMap<String, File> languageGdisMap;
|
||||||
private static File defaultGdisExecFile;
|
private static File defaultGdisExecFile;
|
||||||
@@ -82,6 +89,21 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
return gdisConfig != null && gdisConfig.architecture != UNSUPPORTED;
|
return gdisConfig != null && gdisConfig.architecture != UNSUPPORTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDisassemblyDisplayPrefix(CodeUnit cu) throws Exception {
|
||||||
|
GdisConfig gdisConfig = checkLanguage(cu.getProgram().getLanguage());
|
||||||
|
if (gdisConfig == null || gdisConfig.architecture == UNSUPPORTED) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Register contextRegister = gdisConfig.getContextRegister();
|
||||||
|
if (contextRegister == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
long value = getContextRegisterValue(cu, contextRegister);
|
||||||
|
String option = gdisConfig.getDisplayPrefixMap().get(value);
|
||||||
|
return option;
|
||||||
|
}
|
||||||
|
|
||||||
private static void reportMultipleMappings(Language language) {
|
private static void reportMultipleMappings(Language language) {
|
||||||
List<String> externalNames = language.getLanguageDescription().getExternalNames("gnu");
|
List<String> externalNames = language.getLanguageDescription().getExternalNames("gnu");
|
||||||
if (externalNames != null && externalNames.size() > 1) {
|
if (externalNames != null && externalNames.size() > 1) {
|
||||||
@@ -89,8 +111,9 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
boolean prependSeparator = false;
|
boolean prependSeparator = false;
|
||||||
for (String name : externalNames) {
|
for (String name : externalNames) {
|
||||||
if (prependSeparator)
|
if (prependSeparator) {
|
||||||
sb.append(", ");
|
sb.append(", ");
|
||||||
|
}
|
||||||
sb.append(name);
|
sb.append(name);
|
||||||
prependSeparator = true;
|
prependSeparator = true;
|
||||||
}
|
}
|
||||||
@@ -110,11 +133,17 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
String machineId;
|
String machineId;
|
||||||
File gdisExecFile;
|
File gdisExecFile;
|
||||||
boolean usingDefault;
|
boolean usingDefault;
|
||||||
|
Register contextRegister;
|
||||||
|
Map<Long, String> valueToOptionString;
|
||||||
|
Map<Long, String> valueToDisplayPrefix;
|
||||||
|
Language lang;
|
||||||
|
String globalDisassemblerOptions;
|
||||||
|
|
||||||
GdisConfig(Language language, boolean isBigEndian) {
|
GdisConfig(Language language, boolean isBigEndian) {
|
||||||
|
|
||||||
this.languageId = language.getLanguageID().toString();
|
this.languageId = language.getLanguageID().toString();
|
||||||
this.isBigEndian = isBigEndian;
|
this.isBigEndian = isBigEndian;
|
||||||
|
this.lang = language;
|
||||||
|
|
||||||
List<String> architectures = language.getLanguageDescription().getExternalNames("gnu");
|
List<String> architectures = language.getLanguageDescription().getExternalNames("gnu");
|
||||||
//get first non-null
|
//get first non-null
|
||||||
@@ -143,12 +172,123 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
gdisExecFile = defaultGdisExecFile;
|
gdisExecFile = defaultGdisExecFile;
|
||||||
usingDefault = true;
|
usingDefault = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<String> gDisOptionsFile =
|
||||||
|
language.getLanguageDescription().getExternalNames(GDIS_OPTIONS_FILENAME_PROPERTY);
|
||||||
|
if (!CollectionUtils.isBlank(gDisOptionsFile)) {
|
||||||
|
try {
|
||||||
|
parseGdisOptionsFile(gDisOptionsFile.get(0));
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
Msg.error(this, "Error reading gdis options file " + e.getMessage());
|
||||||
|
contextRegister = null;
|
||||||
|
valueToOptionString = null;
|
||||||
|
valueToDisplayPrefix = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GdisConfig(Language lang) {
|
GdisConfig(Language lang) {
|
||||||
this(lang, lang.isBigEndian());
|
this(lang, lang.isBigEndian());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void parseGdisOptionsFile(String fileName) throws IOException {
|
||||||
|
LanguageDescription desc = lang.getLanguageDescription();
|
||||||
|
if (!(desc instanceof SleighLanguageDescription)) {
|
||||||
|
throw new IOException("Not a Sleigh Language: " + lang.getLanguageID());
|
||||||
|
}
|
||||||
|
|
||||||
|
SleighLanguageDescription sld = (SleighLanguageDescription) desc;
|
||||||
|
ResourceFile defsFile = sld.getDefsFile();
|
||||||
|
ResourceFile parentFile = defsFile.getParentFile();
|
||||||
|
ResourceFile gdisOpts = new ResourceFile(parentFile, fileName);
|
||||||
|
SAXBuilder sax = XmlUtilities.createSecureSAXBuilder(false, false);
|
||||||
|
try (InputStream fis = gdisOpts.getInputStream()) {
|
||||||
|
Document doc = sax.build(fis);
|
||||||
|
Element rootElem = doc.getRootElement();
|
||||||
|
Element globalElement = rootElem.getChild("global");
|
||||||
|
if (globalElement != null) {
|
||||||
|
globalDisassemblerOptions = globalElement.getAttributeValue("optstring");
|
||||||
|
}
|
||||||
|
Element contextRegisterElement = rootElem.getChild("context_register");
|
||||||
|
if (contextRegisterElement == null) {
|
||||||
|
//no context_register element found in the xml file
|
||||||
|
//this is not necessarily an error - might only be a global optstring
|
||||||
|
//global optstring has already been parsed, so we're done
|
||||||
|
if (globalElement != null) {
|
||||||
|
Msg.info(this,
|
||||||
|
"no context register element in " + gdisOpts.getAbsolutePath());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
//no context register element or global element, error
|
||||||
|
throw new JDOMException(
|
||||||
|
"No context_register element or global element in gdis options file");
|
||||||
|
}
|
||||||
|
if (contextRegisterElement.getContentSize() == 0) {
|
||||||
|
throw new JDOMException("No context register name provided.");
|
||||||
|
}
|
||||||
|
String contextRegisterName = contextRegisterElement.getContent(0).getValue();
|
||||||
|
contextRegister = lang.getRegister(contextRegisterName);
|
||||||
|
if (contextRegister == null) {
|
||||||
|
//the context register named in the xml file does not exist in the sleigh language
|
||||||
|
//this is an error
|
||||||
|
throw new JDOMException("Unknown context register " + contextRegisterName +
|
||||||
|
" for language " + lang.getLanguageID().getIdAsString());
|
||||||
|
}
|
||||||
|
valueToOptionString = new HashMap<>();
|
||||||
|
valueToDisplayPrefix = new HashMap<>();
|
||||||
|
Element options = rootElem.getChild("options");
|
||||||
|
List<Element> optList = options.getChildren("option");
|
||||||
|
for (Element opt : optList) {
|
||||||
|
Long value = Long.decode(opt.getAttributeValue("value"));
|
||||||
|
String optString = opt.getAttributeValue("optstring");
|
||||||
|
valueToOptionString.put(value, optString);
|
||||||
|
String displayPrefix = opt.getAttributeValue("display_prefix");
|
||||||
|
valueToDisplayPrefix.put(value, displayPrefix);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (JDOMException e) {
|
||||||
|
Msg.error(this, "Error reading " + fileName + ": " + e.getMessage());
|
||||||
|
contextRegister = null;
|
||||||
|
valueToOptionString = null;
|
||||||
|
valueToDisplayPrefix = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the global disassembler options
|
||||||
|
* @return global option string, or {@code null} if the
|
||||||
|
* global is unspecified.
|
||||||
|
*/
|
||||||
|
public String getGlobalDisassemblerOptions() {
|
||||||
|
return globalDisassemblerOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the context register determine by the gdis options file
|
||||||
|
* @return the context register
|
||||||
|
*/
|
||||||
|
public Register getContextRegister() {
|
||||||
|
return contextRegister;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the map from context register values to gdis disassembler options
|
||||||
|
* @return map values->options
|
||||||
|
*/
|
||||||
|
public Map<Long, String> getOptionsMap() {
|
||||||
|
return valueToOptionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the map from context register values to disassembly display prefixes
|
||||||
|
* @return map values->prefixes
|
||||||
|
*/
|
||||||
|
public Map<Long, String> getDisplayPrefixMap() {
|
||||||
|
return valueToDisplayPrefix;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (!(obj instanceof GdisConfig)) {
|
if (!(obj instanceof GdisConfig)) {
|
||||||
@@ -260,7 +400,7 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
|
|
||||||
String bytes = getBytes(byteProvider, blockSize);
|
String bytes = getBytes(byteProvider, blockSize);
|
||||||
|
|
||||||
return runDisassembler(gdisConfig, address, bytes);
|
return runDisassembler(gdisConfig, address, bytes, EMPTY_DISASSEMBLER_OPTIONS);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<GnuDisassembledInstruction> getBlockDisassembly(Program program, Address addr,
|
public List<GnuDisassembledInstruction> getBlockDisassembly(Program program, Address addr,
|
||||||
@@ -272,8 +412,8 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
int blockSize = pow2(blockSizeFactor);
|
int blockSize = pow2(blockSizeFactor);
|
||||||
|
|
||||||
Address blockAddr = addr.getNewAddress(addr.getOffset() & -blockSize); // block
|
Address blockAddr = addr.getNewAddress(addr.getOffset() & -blockSize); // block
|
||||||
// aligned
|
// aligned
|
||||||
// address
|
// address
|
||||||
|
|
||||||
return getBlockDisassembly(program.getLanguage(), blockAddr, blockSizeFactor,
|
return getBlockDisassembly(program.getLanguage(), blockAddr, blockSizeFactor,
|
||||||
new MemoryByteProvider(program.getMemory(), blockAddr));
|
new MemoryByteProvider(program.getMemory(), blockAddr));
|
||||||
@@ -301,7 +441,37 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
List<GnuDisassembledInstruction> disassembly = runDisassembler(gdisConfig, address, bytes);
|
String disOptions = EMPTY_DISASSEMBLER_OPTIONS;
|
||||||
|
String globalOptions = gdisConfig.getGlobalDisassemblerOptions();
|
||||||
|
boolean hasGlobal = false;
|
||||||
|
if (globalOptions != null) {
|
||||||
|
hasGlobal = true;
|
||||||
|
//set the disOptions to the global options here in case there are global
|
||||||
|
//options but no options for context register values
|
||||||
|
disOptions = globalOptions;
|
||||||
|
}
|
||||||
|
Register contextRegister = gdisConfig.getContextRegister();
|
||||||
|
if (contextRegister != null) {
|
||||||
|
long value = getContextRegisterValue(cu, contextRegister);
|
||||||
|
String contextRegisterValueOption = gdisConfig.getOptionsMap().get(value);
|
||||||
|
if (contextRegisterValueOption == null) {
|
||||||
|
Msg.warn(this, "No option for value " + value + " of context register " +
|
||||||
|
contextRegister.getName());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//need to put a comma between the global options and the options
|
||||||
|
//for this context register value
|
||||||
|
if (hasGlobal) {
|
||||||
|
disOptions = globalOptions + "," + contextRegisterValueOption;
|
||||||
|
}
|
||||||
|
//no global options, just send the options for this context register
|
||||||
|
else {
|
||||||
|
disOptions = contextRegisterValueOption;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
List<GnuDisassembledInstruction> disassembly =
|
||||||
|
runDisassembler(gdisConfig, address, bytes, disOptions);
|
||||||
|
|
||||||
if (disassembly == null || disassembly.size() == 0 || disassembly.get(0) == null) {
|
if (disassembly == null || disassembly.size() == 0 || disassembly.get(0) == null) {
|
||||||
return "(bad)";
|
return "(bad)";
|
||||||
@@ -324,7 +494,7 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
String address = "0x" + Long.toHexString(addressOffset);
|
String address = "0x" + Long.toHexString(addressOffset);
|
||||||
|
|
||||||
List<GnuDisassembledInstruction> disassembly =
|
List<GnuDisassembledInstruction> disassembly =
|
||||||
runDisassembler(gdisConfig, address, bytesString);
|
runDisassembler(gdisConfig, address, bytesString, EMPTY_DISASSEMBLER_OPTIONS);
|
||||||
|
|
||||||
if (disassembly == null || disassembly.isEmpty() || disassembly.get(0) == null) {
|
if (disassembly == null || disassembly.isEmpty() || disassembly.get(0) == null) {
|
||||||
return "(bad)";
|
return "(bad)";
|
||||||
@@ -332,14 +502,26 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
return disassembly.get(0).toString();
|
return disassembly.get(0).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long getContextRegisterValue(CodeUnit cu, Register contextRegister) {
|
||||||
|
ProgramContext context = cu.getProgram().getProgramContext();
|
||||||
|
RegisterValue value = context.getRegisterValue(contextRegister, cu.getAddress());
|
||||||
|
if (value != null) {
|
||||||
|
return value.getUnsignedValue().longValue();
|
||||||
|
}
|
||||||
|
//we tried, return 0 to match SLEIGH default
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private String converBytesToString(byte[] bytes) {
|
private String converBytesToString(byte[] bytes) {
|
||||||
String byteString = null;
|
String byteString = null;
|
||||||
for (byte thisByte : bytes) {
|
for (byte thisByte : bytes) {
|
||||||
String thisByteString = Integer.toHexString(thisByte);
|
String thisByteString = Integer.toHexString(thisByte);
|
||||||
if (thisByteString.length() == 1)
|
if (thisByteString.length() == 1) {
|
||||||
thisByteString = "0" + thisByteString; // pad single digits
|
thisByteString = "0" + thisByteString; // pad single digits
|
||||||
if (thisByteString.length() > 2)
|
}
|
||||||
|
if (thisByteString.length() > 2) {
|
||||||
thisByteString = thisByteString.substring(thisByteString.length() - 2);
|
thisByteString = thisByteString.substring(thisByteString.length() - 2);
|
||||||
|
}
|
||||||
// append this byte's hex string to the larger word length string
|
// append this byte's hex string to the larger word length string
|
||||||
byteString = byteString + thisByteString;
|
byteString = byteString + thisByteString;
|
||||||
}
|
}
|
||||||
@@ -402,7 +584,7 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private List<GnuDisassembledInstruction> runDisassembler(GdisConfig gdisConfig,
|
private List<GnuDisassembledInstruction> runDisassembler(GdisConfig gdisConfig,
|
||||||
String addrString, String bytes) throws IOException {
|
String addrString, String bytes, String disassemblerOptions) throws IOException {
|
||||||
|
|
||||||
// if this is the first time running the disassembler process, or a
|
// if this is the first time running the disassembler process, or a
|
||||||
// parameter has changed (notably, not the address--we pass that in
|
// parameter has changed (notably, not the address--we pass that in
|
||||||
@@ -414,8 +596,9 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
|
|
||||||
if (disassemblerProcess == null || !sameConfig) {
|
if (disassemblerProcess == null || !sameConfig) {
|
||||||
|
|
||||||
if (!setupDisassembler(gdisConfig))
|
if (!setupDisassembler(gdisConfig)) {
|
||||||
return null;
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
outputWriter = new OutputStreamWriter(disassemblerProcess.getOutputStream());
|
outputWriter = new OutputStreamWriter(disassemblerProcess.getOutputStream());
|
||||||
|
|
||||||
@@ -432,7 +615,14 @@ public class GNUExternalDisassembler implements ExternalDisassembler {
|
|||||||
return null; // if process previously died return nothing - quickly
|
return null; // if process previously died return nothing - quickly
|
||||||
}
|
}
|
||||||
|
|
||||||
String disassemblyRequest = addrString + SEPARATOR_CHARACTER + bytes + SEPARATOR_CHARACTER;
|
String disassemblyRequest = addrString + SEPARATOR_CHARACTER + bytes;
|
||||||
|
if (StringUtils.isEmpty(disassemblerOptions)) {
|
||||||
|
disassemblyRequest += '\n';
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
disassemblyRequest += OPTIONS_SEPARATOR + disassemblerOptions + SEPARATOR_CHARACTER;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
outputWriter.write(disassemblyRequest);
|
outputWriter.write(disassemblyRequest);
|
||||||
outputWriter.flush();
|
outputWriter.flush();
|
||||||
|
|||||||
@@ -141,4 +141,28 @@ public abstract class CallNode extends GTreeSlowLoadingNode {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// overridden since we may have multiple children with the same function name, but in
|
||||||
|
// different namespaces
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CallNode other = (CallNode) obj;
|
||||||
|
return getSourceAddress().equals(other.getSourceAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getSourceAddress().hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+34
-19
@@ -16,6 +16,7 @@
|
|||||||
package ghidra.app.plugin.core.compositeeditor;
|
package ghidra.app.plugin.core.compositeeditor;
|
||||||
|
|
||||||
import docking.ActionContext;
|
import docking.ActionContext;
|
||||||
|
import ghidra.app.plugin.core.datamgr.util.DataTypeUtils;
|
||||||
import ghidra.app.services.DataTypeManagerService;
|
import ghidra.app.services.DataTypeManagerService;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.data.Enum;
|
import ghidra.program.model.data.Enum;
|
||||||
@@ -43,29 +44,43 @@ public class EditComponentAction extends CompositeEditorTableAction {
|
|||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionContext context) {
|
public void actionPerformed(ActionContext context) {
|
||||||
int row = model.getRow();
|
int row = model.getRow();
|
||||||
if (row < model.getNumComponents()) {
|
if (row >= model.getNumComponents()) {
|
||||||
DataTypeComponent comp = model.getComponent(row);
|
requestTableFocus();
|
||||||
DataType dt = DataTypeHelper.getBaseType(comp.getDataType());
|
return;
|
||||||
if ((dt instanceof Structure) || (dt instanceof Union) || (dt instanceof Enum)) {
|
}
|
||||||
DataTypeManager dtm = model.getOriginalDataTypeManager();
|
|
||||||
if (dtm != null) {
|
DataTypeComponent comp = model.getComponent(row);
|
||||||
dt = dtm.getDataType(dt.getDataTypePath());
|
DataType clickedType = comp.getDataType();
|
||||||
if (dt != null) {
|
DataType dt = DataTypeUtils.getBaseDataType(clickedType);
|
||||||
this.dtmService.edit(dt);
|
boolean isEditableType =
|
||||||
return;
|
(dt instanceof Structure) || (dt instanceof Union) || (dt instanceof Enum);
|
||||||
}
|
if (isEditableType) {
|
||||||
}
|
edit(dt, clickedType.getName());
|
||||||
String name =
|
}
|
||||||
(dt != null) ? dt.getDisplayName() : comp.getDataType().getDisplayName();
|
else {
|
||||||
model.setStatus("Can't edit \"" + name + "\".");
|
model.setStatus("Can only edit a structure, union or enum.");
|
||||||
}
|
|
||||||
else {
|
|
||||||
model.setStatus("Can only edit a structure, union or enum.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
requestTableFocus();
|
requestTableFocus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void edit(DataType dt, String clickedName) {
|
||||||
|
|
||||||
|
DataTypeManager dtm = model.getOriginalDataTypeManager();
|
||||||
|
if (dtm == null) {
|
||||||
|
// shouldn't happen
|
||||||
|
model.setStatus("No Data Type Manager found for '" + clickedName + "'");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DataType actualType = dtm.getDataType(dt.getDataTypePath());
|
||||||
|
if (actualType == null) {
|
||||||
|
model.setStatus("Can't edit '" + dt.getDisplayName() + "' - type not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dtmService.edit(actualType);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void adjustEnablement() {
|
public void adjustEnablement() {
|
||||||
setEnabled(model.isEditComponentAllowed());
|
setEnabled(model.isEditComponentAllowed());
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ public class BinaryReader {
|
|||||||
* @exception IOException if an I/O error occurs
|
* @exception IOException if an I/O error occurs
|
||||||
*/
|
*/
|
||||||
public String readNextNullTerminatedAsciiString() throws IOException {
|
public String readNextNullTerminatedAsciiString() throws IOException {
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuilder buffer = new StringBuilder();
|
||||||
while (currentIndex < provider.length()) {
|
while (currentIndex < provider.length()) {
|
||||||
byte b = provider.readByte(currentIndex++);
|
byte b = provider.readByte(currentIndex++);
|
||||||
if (b == 0) {
|
if (b == 0) {
|
||||||
@@ -430,7 +430,7 @@ public class BinaryReader {
|
|||||||
* @exception IOException if an I/O error occurs
|
* @exception IOException if an I/O error occurs
|
||||||
*/
|
*/
|
||||||
public String readAsciiString(long index) throws IOException {
|
public String readAsciiString(long index) throws IOException {
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuilder buffer = new StringBuilder();
|
||||||
long len = provider.length();
|
long len = provider.length();
|
||||||
while (true) {
|
while (true) {
|
||||||
if (index == len) {
|
if (index == len) {
|
||||||
@@ -459,7 +459,7 @@ public class BinaryReader {
|
|||||||
* @exception IOException if an I/O error occurs
|
* @exception IOException if an I/O error occurs
|
||||||
*/
|
*/
|
||||||
public String readAsciiString(long index, int length) throws IOException {
|
public String readAsciiString(long index, int length) throws IOException {
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuilder buffer = new StringBuilder();
|
||||||
for (int i = 0; i < length; ++i) {
|
for (int i = 0; i < length; ++i) {
|
||||||
byte b = provider.readByte(index++);
|
byte b = provider.readByte(index++);
|
||||||
buffer.append((char) (b & 0x00FF));
|
buffer.append((char) (b & 0x00FF));
|
||||||
@@ -479,7 +479,7 @@ public class BinaryReader {
|
|||||||
* @exception IOException if an I/O error occurs
|
* @exception IOException if an I/O error occurs
|
||||||
*/
|
*/
|
||||||
public String readTerminatedString(long index, char termChar) throws IOException {
|
public String readTerminatedString(long index, char termChar) throws IOException {
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuilder buffer = new StringBuilder();
|
||||||
long len = provider.length();
|
long len = provider.length();
|
||||||
while (index < len) {
|
while (index < len) {
|
||||||
char c = (char) provider.readByte(index++);
|
char c = (char) provider.readByte(index++);
|
||||||
@@ -503,7 +503,7 @@ public class BinaryReader {
|
|||||||
* @exception IOException if an I/O error occurs
|
* @exception IOException if an I/O error occurs
|
||||||
*/
|
*/
|
||||||
public String readTerminatedString(long index, String termChars) throws IOException {
|
public String readTerminatedString(long index, String termChars) throws IOException {
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuilder buffer = new StringBuilder();
|
||||||
long len = provider.length();
|
long len = provider.length();
|
||||||
while (index < len) {
|
while (index < len) {
|
||||||
char c = (char) provider.readByte(index++);
|
char c = (char) provider.readByte(index++);
|
||||||
@@ -544,7 +544,7 @@ public class BinaryReader {
|
|||||||
* @exception IOException if an I/O error occurs
|
* @exception IOException if an I/O error occurs
|
||||||
*/
|
*/
|
||||||
public String readUnicodeString(long index) throws IOException {
|
public String readUnicodeString(long index) throws IOException {
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuilder buffer = new StringBuilder();
|
||||||
while (index < length()) {
|
while (index < length()) {
|
||||||
int ch = readUnsignedShort(index);
|
int ch = readUnsignedShort(index);
|
||||||
if (ch == 0) {
|
if (ch == 0) {
|
||||||
@@ -571,7 +571,7 @@ public class BinaryReader {
|
|||||||
* @exception IOException if an I/O error occurs
|
* @exception IOException if an I/O error occurs
|
||||||
*/
|
*/
|
||||||
public String readUnicodeString(long index, int length) throws IOException {
|
public String readUnicodeString(long index, int length) throws IOException {
|
||||||
StringBuffer buffer = new StringBuffer(length);
|
StringBuilder buffer = new StringBuilder(length);
|
||||||
long endOffset = index + (length * 2);
|
long endOffset = index + (length * 2);
|
||||||
while (index < endOffset) {
|
while (index < endOffset) {
|
||||||
int ch = readUnsignedShort(index);
|
int ch = readUnsignedShort(index);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import ghidra.util.Msg;
|
|||||||
* This implementation relies on java.net.RandomAccessFile,
|
* This implementation relies on java.net.RandomAccessFile,
|
||||||
* but adds buffering to limit the amount.
|
* but adds buffering to limit the amount.
|
||||||
*/
|
*/
|
||||||
public class GhidraRandomAccessFile {
|
public class GhidraRandomAccessFile implements AutoCloseable {
|
||||||
private static final byte[] EMPTY = new byte[0];
|
private static final byte[] EMPTY = new byte[0];
|
||||||
private static final int BUFFER_SIZE = 0x100000;
|
private static final int BUFFER_SIZE = 0x100000;
|
||||||
|
|
||||||
@@ -112,6 +112,7 @@ public class GhidraRandomAccessFile {
|
|||||||
* If this file has an associated channel then the channel is closed as well.
|
* If this file has an associated channel then the channel is closed as well.
|
||||||
* @exception IOException if an I/O error occurs.
|
* @exception IOException if an I/O error occurs.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void close() throws IOException {
|
public void close() throws IOException {
|
||||||
checkOpen();
|
checkOpen();
|
||||||
open = false;
|
open = false;
|
||||||
@@ -291,8 +292,11 @@ public class GhidraRandomAccessFile {
|
|||||||
|
|
||||||
buffer = new byte[BUFFER_SIZE];
|
buffer = new byte[BUFFER_SIZE];
|
||||||
randomAccessFile.seek(bufferFileStartIndex);
|
randomAccessFile.seek(bufferFileStartIndex);
|
||||||
randomAccessFile.read(buffer);
|
int bytesRead = randomAccessFile.read(buffer);
|
||||||
bufferOffset = 0;
|
bufferOffset = 0;
|
||||||
|
if (bytesRead <= 0) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
bufferOffset = newBufferOffset;
|
bufferOffset = newBufferOffset;
|
||||||
|
|||||||
+34
-21
@@ -18,6 +18,7 @@ package ghidra.app.util.bin;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
import ghidra.formats.gfilesystem.FSRL;
|
import ghidra.formats.gfilesystem.FSRL;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An implementation of ByteProvider where the underlying
|
* An implementation of ByteProvider where the underlying
|
||||||
@@ -33,15 +34,15 @@ public class RandomAccessByteProvider implements ByteProvider {
|
|||||||
protected File file;
|
protected File file;
|
||||||
protected GhidraRandomAccessFile randomAccessFile;
|
protected GhidraRandomAccessFile randomAccessFile;
|
||||||
private FSRL fsrl;
|
private FSRL fsrl;
|
||||||
private Long cachedLength;
|
private long fileLength;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a {@link ByteProvider} using the specified {@link File}.
|
* Constructs a {@link ByteProvider} using the specified {@link File}.
|
||||||
*
|
*
|
||||||
* @param file the {@link File} to open for random access
|
* @param file the {@link File} to open for random access
|
||||||
* @throws FileNotFoundException if the {@link File} does not exist
|
* @throws IOException if the {@link File} does not exist or other error
|
||||||
*/
|
*/
|
||||||
public RandomAccessByteProvider(File file) throws FileNotFoundException {
|
public RandomAccessByteProvider(File file) throws IOException {
|
||||||
this(file, "r");
|
this(file, "r");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,11 +51,10 @@ public class RandomAccessByteProvider implements ByteProvider {
|
|||||||
*
|
*
|
||||||
* @param file the {@link File} to open for random access
|
* @param file the {@link File} to open for random access
|
||||||
* @param fsrl the {@link FSRL} to use for the {@link File}'s path
|
* @param fsrl the {@link FSRL} to use for the {@link File}'s path
|
||||||
* @throws FileNotFoundException if the {@link File} does not exist
|
* @throws IOException if the {@link File} does not exist or other error
|
||||||
*/
|
*/
|
||||||
public RandomAccessByteProvider(File file, FSRL fsrl) throws FileNotFoundException {
|
public RandomAccessByteProvider(File file, FSRL fsrl) throws IOException {
|
||||||
this(file, "r");
|
this(file, fsrl, "r");
|
||||||
this.fsrl = fsrl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -62,11 +62,17 @@ public class RandomAccessByteProvider implements ByteProvider {
|
|||||||
*
|
*
|
||||||
* @param file the {@link File} to open for random access
|
* @param file the {@link File} to open for random access
|
||||||
* @param permissions indicating permissions used for open
|
* @param permissions indicating permissions used for open
|
||||||
* @throws FileNotFoundException if the {@link File} does not exist
|
* @throws IOException if the {@link File} does not exist or other error
|
||||||
*/
|
*/
|
||||||
public RandomAccessByteProvider(File file, String permissions) throws FileNotFoundException {
|
public RandomAccessByteProvider(File file, String permissions) throws IOException {
|
||||||
|
this(file, null, permissions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private RandomAccessByteProvider(File file, FSRL fsrl, String permissions) throws IOException {
|
||||||
this.file = file;
|
this.file = file;
|
||||||
|
this.fsrl = fsrl;
|
||||||
this.randomAccessFile = new GhidraRandomAccessFile(file, permissions);
|
this.randomAccessFile = new GhidraRandomAccessFile(file, permissions);
|
||||||
|
this.fileLength = randomAccessFile.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -111,21 +117,13 @@ public class RandomAccessByteProvider implements ByteProvider {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long length() throws IOException {
|
public long length() {
|
||||||
if (cachedLength == null) {
|
return fileLength;
|
||||||
cachedLength = randomAccessFile.length();
|
|
||||||
}
|
|
||||||
return cachedLength;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValidIndex(long index) {
|
public boolean isValidIndex(long index) {
|
||||||
try {
|
return 0 <= index && index < fileLength;
|
||||||
return index >= 0 && index < length();
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -136,12 +134,27 @@ public class RandomAccessByteProvider implements ByteProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public byte[] readBytes(long index, long length) throws IOException {
|
public byte[] readBytes(long index, long length) throws IOException {
|
||||||
randomAccessFile.seek(index);
|
|
||||||
byte[] b = new byte[(int) length];
|
byte[] b = new byte[(int) length];
|
||||||
|
if (index > fileLength) {
|
||||||
|
throw new EOFException(
|
||||||
|
"Invalid file offset " + index + " while reading " + file.getName());
|
||||||
|
}
|
||||||
|
if (index + length > fileLength) {
|
||||||
|
Msg.trace(this, "Read at EOF, can't return partial buffer, throwing IOException: " +
|
||||||
|
file.getName());
|
||||||
|
throw new EOFException("EOF: unable to read " + length + " bytes at " + index);
|
||||||
|
}
|
||||||
|
randomAccessFile.seek(index);
|
||||||
int nRead = randomAccessFile.read(b);
|
int nRead = randomAccessFile.read(b);
|
||||||
if (nRead != length) {
|
if (nRead != length) {
|
||||||
throw new IOException("Unable to read " + length + " bytes");
|
throw new IOException("Unable to read " + length + " bytes");
|
||||||
}
|
}
|
||||||
return b;
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "RandomAccessByteProvider [\n file=" + file + ",\n fsrl=" + fsrl +
|
||||||
|
",\n fileLength=" + fileLength + "\n]";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+48
-31
@@ -15,16 +15,17 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.coff.archive;
|
package ghidra.app.util.bin.format.coff.archive;
|
||||||
|
|
||||||
import ghidra.app.util.bin.*;
|
|
||||||
import ghidra.app.util.bin.format.coff.CoffException;
|
|
||||||
import ghidra.program.model.data.*;
|
|
||||||
import ghidra.util.exception.DuplicateNameException;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import ghidra.app.util.bin.*;
|
||||||
|
import ghidra.app.util.bin.format.coff.CoffException;
|
||||||
|
import ghidra.program.model.data.*;
|
||||||
|
import ghidra.util.Msg;
|
||||||
|
import ghidra.util.exception.DuplicateNameException;
|
||||||
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A class that represents a COFF archive file (ie. MS .lib files, Unix .ar files)
|
* A class that represents a COFF archive file (ie. MS .lib files, Unix .ar files)
|
||||||
* <p>
|
* <p>
|
||||||
@@ -74,39 +75,54 @@ public final class CoffArchiveHeader implements StructConverter {
|
|||||||
|
|
||||||
CoffArchiveHeader cah = new CoffArchiveHeader();
|
CoffArchiveHeader cah = new CoffArchiveHeader();
|
||||||
|
|
||||||
while (reader.getPointerIndex() < reader.length() - 1) {
|
long eofPos = reader.length() - CoffArchiveMemberHeader.CAMH_MIN_SIZE;
|
||||||
|
|
||||||
|
while (reader.getPointerIndex() < eofPos) {
|
||||||
if (monitor.isCancelled()) {
|
if (monitor.isCancelled()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
CoffArchiveMemberHeader camh =
|
try {
|
||||||
CoffArchiveMemberHeader.read(reader, cah._longNameMember);
|
CoffArchiveMemberHeader camh =
|
||||||
|
CoffArchiveMemberHeader.read(reader, cah._longNameMember);
|
||||||
|
|
||||||
if (camh.getName().equals(CoffArchiveMemberHeader.SLASH)) {
|
if (camh.getName().equals(CoffArchiveMemberHeader.SLASH)) {
|
||||||
switch (memberNum) {
|
switch (memberNum) {
|
||||||
case 0:
|
case 0:
|
||||||
cah._firstLinkerMember = new FirstLinkerMember(reader, camh, true);
|
cah._firstLinkerMember = new FirstLinkerMember(reader, camh, true);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
cah._secondLinkerMember = new SecondLinkerMember(reader, camh, true);
|
cah._secondLinkerMember = new SecondLinkerMember(reader, camh, true);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
throw new CoffException(
|
||||||
|
"Invalid COFF: multiple 1st and 2nd linker members detected.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (camh.getName().equals(CoffArchiveMemberHeader.SLASH_SLASH)) {
|
||||||
|
if (cah._longNameMember == null) {
|
||||||
|
cah._longNameMember = new LongNamesMember(reader, camh);
|
||||||
|
}
|
||||||
|
else {
|
||||||
throw new CoffException(
|
throw new CoffException(
|
||||||
"Invalid COFF: multiple 1st and 2nd linker members detected.");
|
"Invalid COFF: multiple long name members detected.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
cah._memberHeaders.add(camh);
|
||||||
else if (camh.getName().equals(CoffArchiveMemberHeader.SLASH_SLASH)) {
|
memberNum++;
|
||||||
if (cah._longNameMember == null) {
|
|
||||||
cah._longNameMember = new LongNamesMember(reader, camh);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new CoffException("Invalid COFF: multiple long name members detected.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cah._memberHeaders.add(camh);
|
|
||||||
memberNum++;
|
|
||||||
|
|
||||||
reader.setPointerIndex(camh.getPayloadOffset() + camh.getSize());
|
reader.setPointerIndex(camh.getPayloadOffset() + camh.getSize());
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// if we run into bad data, return partial success if there has been at least some
|
||||||
|
// good ones, otherwise propagate the exception upwards
|
||||||
|
if (memberNum <= 3) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
Msg.warn(CoffArchiveMemberHeader.class, "Problem reading COFF archive headers in " +
|
||||||
|
provider.getFSRL() + ", only " + memberNum + " members found.", e);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: check for null terminators in the longname string table vs. \n terminators
|
// TODO: check for null terminators in the longname string table vs. \n terminators
|
||||||
@@ -125,6 +141,7 @@ public final class CoffArchiveHeader implements StructConverter {
|
|||||||
protected CoffArchiveHeader() {
|
protected CoffArchiveHeader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public DataType toDataType() throws DuplicateNameException, IOException {
|
public DataType toDataType() throws DuplicateNameException, IOException {
|
||||||
Structure struct = new StructureDataType(StructConverterUtil.parseName(CoffArchiveHeader.class), 0);
|
Structure struct = new StructureDataType(StructConverterUtil.parseName(CoffArchiveHeader.class), 0);
|
||||||
struct.add(STRING, CoffArchiveConstants.MAGIC_LEN, "magic", null);
|
struct.add(STRING, CoffArchiveConstants.MAGIC_LEN, "magic", null);
|
||||||
|
|||||||
+3
-1
@@ -50,6 +50,7 @@ public class CoffArchiveMemberHeader implements StructConverter {
|
|||||||
private static final String CAMH_EOH_MAGIC = "`\n";
|
private static final String CAMH_EOH_MAGIC = "`\n";
|
||||||
|
|
||||||
private static final int CAMH_PAYLOAD_OFF = 60;
|
private static final int CAMH_PAYLOAD_OFF = 60;
|
||||||
|
public static final int CAMH_MIN_SIZE = CAMH_PAYLOAD_OFF;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a COFF archive member header from the specified {@link BinaryReader reader},
|
* Reads a COFF archive member header from the specified {@link BinaryReader reader},
|
||||||
@@ -162,8 +163,9 @@ public class CoffArchiveMemberHeader implements StructConverter {
|
|||||||
try {
|
try {
|
||||||
long offset = Long.parseLong(name.substring(1));
|
long offset = Long.parseLong(name.substring(1));
|
||||||
name = longNames.getStringAtOffset(reader.getByteProvider(), offset);
|
name = longNames.getStringAtOffset(reader.getByteProvider(), offset);
|
||||||
if (name.endsWith("/"))
|
if (name.endsWith("/")) {
|
||||||
name = name.substring(0, name.length() - 1);
|
name = name.substring(0, name.length() - 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (NumberFormatException nfe) {
|
catch (NumberFormatException nfe) {
|
||||||
throw new IOException("Bad long name offset: " + name);
|
throw new IOException("Bad long name offset: " + name);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
/* ###
|
/* ###
|
||||||
* IP: GHIDRA
|
* IP: GHIDRA
|
||||||
* REVIEWED: YES
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
|||||||
+23
-17
@@ -16,13 +16,11 @@
|
|||||||
package ghidra.app.util.bin.format.macho;
|
package ghidra.app.util.bin.format.macho;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import generic.continues.GenericFactory;
|
import generic.continues.GenericFactory;
|
||||||
import ghidra.app.util.bin.ByteProvider;
|
import ghidra.app.util.bin.*;
|
||||||
import ghidra.app.util.bin.StructConverter;
|
|
||||||
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
import ghidra.app.util.bin.format.FactoryBundledWithBinaryReader;
|
||||||
import ghidra.app.util.bin.format.macho.commands.*;
|
import ghidra.app.util.bin.format.macho.commands.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
@@ -44,12 +42,28 @@ public class MachHeader implements StructConverter {
|
|||||||
private int reserved;//only used in 64-bit
|
private int reserved;//only used in 64-bit
|
||||||
|
|
||||||
private boolean _is32bit;
|
private boolean _is32bit;
|
||||||
private List<LoadCommand> _commands = new ArrayList<LoadCommand>();
|
private List<LoadCommand> _commands = new ArrayList<>();
|
||||||
private long _commandIndex;
|
private long _commandIndex;
|
||||||
private FactoryBundledWithBinaryReader _reader;
|
private FactoryBundledWithBinaryReader _reader;
|
||||||
private long _machHeaderStartIndexInProvider;
|
private long _machHeaderStartIndexInProvider;
|
||||||
private boolean _parsed = false;
|
private boolean _parsed = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the specified ByteProvider starts with a Mach header magic signature.
|
||||||
|
*
|
||||||
|
* @param provider {@link ByteProvider} to check
|
||||||
|
* @return boolean true if byte provider starts with a MachHeader
|
||||||
|
*/
|
||||||
|
public static boolean isMachHeader(ByteProvider provider) {
|
||||||
|
try {
|
||||||
|
return provider.length() > Integer.BYTES &&
|
||||||
|
MachConstants.isMagic(readMagic(provider, 0));
|
||||||
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
// dont care
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Assumes the MachHeader starts at index 0 in the ByteProvider.
|
* Assumes the MachHeader starts at index 0 in the ByteProvider.
|
||||||
* @param provider the ByteProvider
|
* @param provider the ByteProvider
|
||||||
@@ -233,7 +247,7 @@ public class MachHeader implements StructConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<Section> getAllSections() {
|
public List<Section> getAllSections() {
|
||||||
List<Section> tmp = new ArrayList<Section>();
|
List<Section> tmp = new ArrayList<>();
|
||||||
for (SegmentCommand segment : getAllSegments()) {
|
for (SegmentCommand segment : getAllSegments()) {
|
||||||
tmp.addAll(segment.getSections());
|
tmp.addAll(segment.getSections());
|
||||||
}
|
}
|
||||||
@@ -245,7 +259,7 @@ public class MachHeader implements StructConverter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public <T> List<T> getLoadCommands(Class<T> classType) {
|
public <T> List<T> getLoadCommands(Class<T> classType) {
|
||||||
List<T> tmp = new ArrayList<T>();
|
List<T> tmp = new ArrayList<>();
|
||||||
for (LoadCommand command : _commands) {
|
for (LoadCommand command : _commands) {
|
||||||
if (classType.isAssignableFrom(command.getClass())) {
|
if (classType.isAssignableFrom(command.getClass())) {
|
||||||
tmp.add(classType.cast(command));
|
tmp.add(classType.cast(command));
|
||||||
@@ -282,22 +296,14 @@ public class MachHeader implements StructConverter {
|
|||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getDataStream() throws IOException {
|
|
||||||
return _reader.getByteProvider().getInputStream(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getDescription();
|
return getDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
private int readMagic(ByteProvider provider, long machHeaderStartIndexInProvider)
|
private static int readMagic(ByteProvider provider, long machHeaderStartIndexInProvider)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
int value = 0;
|
BinaryReader br = new BinaryReader(provider, false);
|
||||||
value |= (provider.readByte(machHeaderStartIndexInProvider + 0) << 0x18) & 0xff000000;
|
return br.readInt(machHeaderStartIndexInProvider);
|
||||||
value |= (provider.readByte(machHeaderStartIndexInProvider + 1) << 0x10) & 0x00ff0000;
|
|
||||||
value |= (provider.readByte(machHeaderStartIndexInProvider + 2) << 0x08) & 0x0000ff00;
|
|
||||||
value |= (provider.readByte(machHeaderStartIndexInProvider + 3) << 0x00) & 0x000000ff;
|
|
||||||
return value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public class Section implements StructConverter {
|
|||||||
|
|
||||||
private FactoryBundledWithBinaryReader reader;
|
private FactoryBundledWithBinaryReader reader;
|
||||||
private boolean is32bit;
|
private boolean is32bit;
|
||||||
private List<RelocationInfo> relocations = new ArrayList<RelocationInfo>();
|
private List<RelocationInfo> relocations = new ArrayList<>();
|
||||||
|
|
||||||
public static Section createSection(FactoryBundledWithBinaryReader reader, boolean is32bit)
|
public static Section createSection(FactoryBundledWithBinaryReader reader, boolean is32bit)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
@@ -71,8 +71,8 @@ public class Section implements StructConverter {
|
|||||||
sectname = reader.readNextAsciiString(MachConstants.NAME_LENGTH);
|
sectname = reader.readNextAsciiString(MachConstants.NAME_LENGTH);
|
||||||
segname = reader.readNextAsciiString(MachConstants.NAME_LENGTH);
|
segname = reader.readNextAsciiString(MachConstants.NAME_LENGTH);
|
||||||
if (is32bit) {
|
if (is32bit) {
|
||||||
addr = reader.readNextInt() & 0xffffffffL;
|
addr = reader.readNextUnsignedInt();
|
||||||
size = reader.readNextInt() & 0xffffffffL;
|
size = reader.readNextUnsignedInt();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
addr = reader.readNextLong();
|
addr = reader.readNextLong();
|
||||||
|
|||||||
@@ -15,7 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package ghidra.app.util.bin.format.pe;
|
package ghidra.app.util.bin.format.pe;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.IOException;
|
||||||
|
import java.io.RandomAccessFile;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@@ -144,7 +145,7 @@ public class FileHeader implements StructConverter {
|
|||||||
private short characteristics;
|
private short characteristics;
|
||||||
|
|
||||||
private SectionHeader [] sectionHeaders;
|
private SectionHeader [] sectionHeaders;
|
||||||
private List<DebugCOFFSymbol>symbols = new ArrayList<DebugCOFFSymbol>();
|
private List<DebugCOFFSymbol>symbols = new ArrayList<>();
|
||||||
|
|
||||||
private FactoryBundledWithBinaryReader reader;
|
private FactoryBundledWithBinaryReader reader;
|
||||||
private int startIndex;
|
private int startIndex;
|
||||||
@@ -560,8 +561,4 @@ public class FileHeader implements StructConverter {
|
|||||||
}
|
}
|
||||||
return PortableExecutable.computeAlignment( lastPos, optionalHeader.getFileAlignment( ) );
|
return PortableExecutable.computeAlignment( lastPos, optionalHeader.getFileAlignment( ) );
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream getDataStream() throws IOException {
|
|
||||||
return reader.getByteProvider().getInputStream(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -94,6 +94,9 @@ public class MachoLoader extends AbstractLibrarySupportLoader {
|
|||||||
MachoProgramBuilder.buildProgram(program, provider, fileBytes, log, monitor);
|
MachoProgramBuilder.buildProgram(program, provider, fileBytes, log, monitor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (IOException e) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
catch (Exception e) {
|
catch (Exception e) {
|
||||||
throw new IOException(e.getMessage());
|
throw new IOException(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|||||||
+55
-40
@@ -212,27 +212,35 @@ public class FileSystemFactoryMgr {
|
|||||||
try (ByteProvider bp = new RandomAccessByteProvider(containerFile, containerFSRL)) {
|
try (ByteProvider bp = new RandomAccessByteProvider(containerFile, containerFSRL)) {
|
||||||
byte[] startBytes = bp.readBytes(0, pboByteCount);
|
byte[] startBytes = bp.readBytes(0, pboByteCount);
|
||||||
for (FileSystemInfoRec fsir : sortedFactories) {
|
for (FileSystemInfoRec fsir : sortedFactories) {
|
||||||
if (fsir.getFactory() instanceof GFileSystemProbeBytesOnly) {
|
try {
|
||||||
GFileSystemProbeBytesOnly factoryProbe =
|
if (fsir.getFactory() instanceof GFileSystemProbeBytesOnly) {
|
||||||
(GFileSystemProbeBytesOnly) fsir.getFactory();
|
GFileSystemProbeBytesOnly factoryProbe =
|
||||||
if (factoryProbe.getBytesRequired() <= startBytes.length) {
|
(GFileSystemProbeBytesOnly) fsir.getFactory();
|
||||||
if (factoryProbe.probeStartBytes(containerFSRL, startBytes)) {
|
if (factoryProbe.getBytesRequired() <= startBytes.length) {
|
||||||
|
if (factoryProbe.probeStartBytes(containerFSRL, startBytes)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fsir.getFactory() instanceof GFileSystemProbeWithFile) {
|
||||||
|
GFileSystemProbeWithFile factoryProbe =
|
||||||
|
(GFileSystemProbeWithFile) fsir.getFactory();
|
||||||
|
if (factoryProbe.probe(containerFSRL, containerFile, fsService, monitor)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fsir.getFactory() instanceof GFileSystemProbeFull) {
|
||||||
|
GFileSystemProbeFull factoryProbe =
|
||||||
|
(GFileSystemProbeFull) fsir.getFactory();
|
||||||
|
if (factoryProbe.probe(containerFSRL, bp, containerFile, fsService,
|
||||||
|
monitor)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fsir.getFactory() instanceof GFileSystemProbeWithFile) {
|
catch (IOException e) {
|
||||||
GFileSystemProbeWithFile factoryProbe =
|
Msg.trace(this, "File system probe error for " + fsir.getDescription() +
|
||||||
(GFileSystemProbeWithFile) fsir.getFactory();
|
" with " + containerFSRL, e);
|
||||||
if (factoryProbe.probe(containerFSRL, containerFile, fsService, monitor)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fsir.getFactory() instanceof GFileSystemProbeFull) {
|
|
||||||
GFileSystemProbeFull factoryProbe = (GFileSystemProbeFull) fsir.getFactory();
|
|
||||||
if (factoryProbe.probe(containerFSRL, bp, containerFile, fsService, monitor)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -303,34 +311,41 @@ public class FileSystemFactoryMgr {
|
|||||||
byte[] startBytes = probeBP.readBytes(0, pboByteCount);
|
byte[] startBytes = probeBP.readBytes(0, pboByteCount);
|
||||||
List<FileSystemInfoRec> probeMatches = new ArrayList<>();
|
List<FileSystemInfoRec> probeMatches = new ArrayList<>();
|
||||||
for (FileSystemInfoRec fsir : sortedFactories) {
|
for (FileSystemInfoRec fsir : sortedFactories) {
|
||||||
if (fsir.getPriority() < priorityFilter) {
|
try {
|
||||||
break;
|
if (fsir.getPriority() < priorityFilter) {
|
||||||
}
|
break;
|
||||||
if (fsir.getFactory() instanceof GFileSystemProbeBytesOnly) {
|
}
|
||||||
GFileSystemProbeBytesOnly factoryProbe =
|
if (fsir.getFactory() instanceof GFileSystemProbeBytesOnly) {
|
||||||
(GFileSystemProbeBytesOnly) fsir.getFactory();
|
GFileSystemProbeBytesOnly factoryProbe =
|
||||||
if (factoryProbe.getBytesRequired() <= startBytes.length) {
|
(GFileSystemProbeBytesOnly) fsir.getFactory();
|
||||||
if (factoryProbe.probeStartBytes(containerFSRL, startBytes)) {
|
if (factoryProbe.getBytesRequired() <= startBytes.length) {
|
||||||
|
if (factoryProbe.probeStartBytes(containerFSRL, startBytes)) {
|
||||||
|
probeMatches.add(fsir);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fsir.getFactory() instanceof GFileSystemProbeWithFile) {
|
||||||
|
GFileSystemProbeWithFile factoryProbe =
|
||||||
|
(GFileSystemProbeWithFile) fsir.getFactory();
|
||||||
|
if (factoryProbe.probe(containerFSRL, containerFile, fsService, monitor)) {
|
||||||
|
probeMatches.add(fsir);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (fsir.getFactory() instanceof GFileSystemProbeFull) {
|
||||||
|
GFileSystemProbeFull factoryProbe =
|
||||||
|
(GFileSystemProbeFull) fsir.getFactory();
|
||||||
|
if (factoryProbe.probe(containerFSRL, probeBP, containerFile, fsService,
|
||||||
|
monitor)) {
|
||||||
probeMatches.add(fsir);
|
probeMatches.add(fsir);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fsir.getFactory() instanceof GFileSystemProbeWithFile) {
|
catch (IOException e) {
|
||||||
GFileSystemProbeWithFile factoryProbe =
|
Msg.trace(this, "File system probe error for " + fsir.getDescription() +
|
||||||
(GFileSystemProbeWithFile) fsir.getFactory();
|
" with " + containerFSRL, e);
|
||||||
if (factoryProbe.probe(containerFSRL, containerFile, fsService, monitor)) {
|
|
||||||
probeMatches.add(fsir);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fsir.getFactory() instanceof GFileSystemProbeFull) {
|
|
||||||
GFileSystemProbeFull factoryProbe = (GFileSystemProbeFull) fsir.getFactory();
|
|
||||||
if (factoryProbe.probe(containerFSRL, probeBP, containerFile, fsService,
|
|
||||||
monitor)) {
|
|
||||||
probeMatches.add(fsir);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,231 +0,0 @@
|
|||||||
/* ###
|
|
||||||
* 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 ghidra.program.examiner;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import ghidra.GhidraException;
|
|
||||||
import ghidra.GhidraTestApplicationLayout;
|
|
||||||
import ghidra.app.plugin.core.analysis.EmbeddedMediaAnalyzer;
|
|
||||||
import ghidra.app.util.bin.*;
|
|
||||||
import ghidra.app.util.importer.AutoImporter;
|
|
||||||
import ghidra.app.util.importer.MessageLog;
|
|
||||||
import ghidra.framework.Application;
|
|
||||||
import ghidra.framework.HeadlessGhidraApplicationConfiguration;
|
|
||||||
import ghidra.program.model.data.*;
|
|
||||||
import ghidra.program.model.lang.*;
|
|
||||||
import ghidra.program.model.listing.*;
|
|
||||||
import ghidra.program.model.mem.MemoryAccessException;
|
|
||||||
import ghidra.program.util.DefaultLanguageService;
|
|
||||||
import ghidra.util.exception.CancelledException;
|
|
||||||
import ghidra.util.task.TaskMonitorAdapter;
|
|
||||||
import utility.application.ApplicationLayout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wrapper for Ghidra code to find images (and maybe other artifacts later) in a program
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class ProgramExaminer {
|
|
||||||
|
|
||||||
private MessageLog messageLog;
|
|
||||||
private Program program;
|
|
||||||
|
|
||||||
private static Language defaultLanguage;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new ProgramExaminer.
|
|
||||||
* @param bytes the bytes of the potential program to be examined.
|
|
||||||
* @throws GhidraException if any exception occurs while processing the bytes.
|
|
||||||
*/
|
|
||||||
public ProgramExaminer(byte[] bytes) throws GhidraException {
|
|
||||||
this(createByteProvider(bytes));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a new ProgramExaminer.
|
|
||||||
* @param file file object containing the bytes to be examined.
|
|
||||||
* @throws GhidraException if any exception occurs while processing the bytes.
|
|
||||||
*/
|
|
||||||
public ProgramExaminer(File file) throws GhidraException {
|
|
||||||
this(createByteProvider(file));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a string indication the program format. i.e. PE, elf, raw
|
|
||||||
*/
|
|
||||||
public String getType() {
|
|
||||||
return program.getExecutableFormat();
|
|
||||||
}
|
|
||||||
|
|
||||||
private ProgramExaminer(ByteProvider provider) throws GhidraException {
|
|
||||||
initializeGhidra();
|
|
||||||
messageLog = new MessageLog();
|
|
||||||
try {
|
|
||||||
program =
|
|
||||||
AutoImporter.importByUsingBestGuess(provider, null, this, messageLog,
|
|
||||||
TaskMonitorAdapter.DUMMY_MONITOR);
|
|
||||||
|
|
||||||
if (program == null) {
|
|
||||||
program =
|
|
||||||
AutoImporter.importAsBinary(provider, null, defaultLanguage, null, this,
|
|
||||||
messageLog, TaskMonitorAdapter.DUMMY_MONITOR);
|
|
||||||
}
|
|
||||||
if (program == null) {
|
|
||||||
throw new GhidraException("Can't create program from input: " +
|
|
||||||
messageLog.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
messageLog.appendException(e);
|
|
||||||
throw new GhidraException(e);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
try {
|
|
||||||
provider.close();
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
// tried to close
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized static void initializeGhidra() throws GhidraException {
|
|
||||||
if (!Application.isInitialized()) {
|
|
||||||
ApplicationLayout layout;
|
|
||||||
try {
|
|
||||||
layout =
|
|
||||||
new GhidraTestApplicationLayout(new File(System.getProperty("java.io.tmpdir")));
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw new GhidraException(e);
|
|
||||||
}
|
|
||||||
HeadlessGhidraApplicationConfiguration config =
|
|
||||||
new HeadlessGhidraApplicationConfiguration();
|
|
||||||
config.setInitializeLogging(false);
|
|
||||||
Application.initializeApplication(layout, config);
|
|
||||||
}
|
|
||||||
if (defaultLanguage == null) {
|
|
||||||
LanguageService languageService = DefaultLanguageService.getLanguageService();
|
|
||||||
try {
|
|
||||||
defaultLanguage = languageService.getDefaultLanguage(
|
|
||||||
Processor.findOrPossiblyCreateProcessor("DATA"));
|
|
||||||
}
|
|
||||||
catch (LanguageNotFoundException e) {
|
|
||||||
throw new GhidraException("Can't load default language: DATA");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases file/database resources.
|
|
||||||
*/
|
|
||||||
public void dispose() {
|
|
||||||
program.release(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a list of byte[] containing image data. The bytes will be either a png, a gif, or
|
|
||||||
* a bitmap
|
|
||||||
*/
|
|
||||||
public List<byte[]> getImages() {
|
|
||||||
runImageAnalyzer();
|
|
||||||
|
|
||||||
List<byte[]> imageList = new ArrayList<byte[]>();
|
|
||||||
DataIterator it = program.getListing().getDefinedData(true);
|
|
||||||
while (it.hasNext()) {
|
|
||||||
accumulateImageData(imageList, it.next());
|
|
||||||
}
|
|
||||||
return imageList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void accumulateImageData(List<byte[]> imageList, Data data) {
|
|
||||||
if (!isImage(data)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
imageList.add(data.getBytes());
|
|
||||||
}
|
|
||||||
catch (MemoryAccessException e) {
|
|
||||||
// suppress (this shouldn't happen
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runImageAnalyzer() {
|
|
||||||
int txID = program.startTransaction("find images");
|
|
||||||
try {
|
|
||||||
EmbeddedMediaAnalyzer imageAnalyzer = new EmbeddedMediaAnalyzer();
|
|
||||||
imageAnalyzer.added(program, program.getMemory(), TaskMonitorAdapter.DUMMY_MONITOR,
|
|
||||||
messageLog);
|
|
||||||
}
|
|
||||||
catch (CancelledException e) {
|
|
||||||
// using Dummy, can't happen
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
program.endTransaction(txID, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isImage(Data data) {
|
|
||||||
DataType dataType = data.getDataType();
|
|
||||||
if (dataType instanceof PngDataType) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (dataType instanceof GifDataType) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (dataType instanceof BitmapResourceDataType) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (dataType instanceof IconResourceDataType) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (dataType instanceof JPEGDataType) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
// static methods
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
private static ByteProvider createByteProvider(byte[] bytes) throws GhidraException {
|
|
||||||
if (bytes == null) {
|
|
||||||
throw new GhidraException("Attempted to process a null byte[].");
|
|
||||||
}
|
|
||||||
if (bytes.length == 0) {
|
|
||||||
throw new GhidraException("Attempted to process an empty byte[].");
|
|
||||||
}
|
|
||||||
return new ByteArrayProvider("Bytes", bytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ByteProvider createByteProvider(File file) throws GhidraException {
|
|
||||||
if (file == null) {
|
|
||||||
throw new GhidraException("Attempted to process a null file");
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
return new RandomAccessByteProvider(file);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw new GhidraException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -271,7 +271,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setErrorsExpected(true);
|
setErrorsExpected(true);
|
||||||
runSwingWithExceptions(this::showProvider, true);
|
runSwingWithException(this::showProvider);
|
||||||
setErrorsExpected(false);
|
setErrorsExpected(false);
|
||||||
fail();
|
fail();
|
||||||
}
|
}
|
||||||
@@ -289,7 +289,7 @@ public class ComponentProviderActionsTest extends AbstractGhidraHeadedIntegratio
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
setErrorsExpected(true);
|
setErrorsExpected(true);
|
||||||
runSwingWithExceptions(() -> provider.setIcon(null), true);
|
runSwingWithException(() -> provider.setIcon(null));
|
||||||
setErrorsExpected(false);
|
setErrorsExpected(false);
|
||||||
fail("Expected an exception passing a null icon when specifying a toolbar action");
|
fail("Expected an exception passing a null icon when specifying a toolbar action");
|
||||||
}
|
}
|
||||||
|
|||||||
+82
-18
@@ -42,6 +42,7 @@ import ghidra.app.plugin.core.codebrowser.CodeBrowserPlugin;
|
|||||||
import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
|
import ghidra.app.plugin.core.gotoquery.GoToServicePlugin;
|
||||||
import ghidra.app.services.GoToService;
|
import ghidra.app.services.GoToService;
|
||||||
import ghidra.app.services.ProgramManager;
|
import ghidra.app.services.ProgramManager;
|
||||||
|
import ghidra.app.util.NamespaceUtils;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
import ghidra.framework.plugintool.PluginTool;
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
@@ -158,6 +159,10 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Function function(int addr) throws Exception {
|
||||||
|
return ensureFunction(addr);
|
||||||
|
}
|
||||||
|
|
||||||
private void function(int from, int to) throws Exception {
|
private void function(int from, int to) throws Exception {
|
||||||
ensureFunction(from);
|
ensureFunction(from);
|
||||||
ensureFunction(to);
|
ensureFunction(to);
|
||||||
@@ -168,16 +173,39 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ensureFunction(long from) throws Exception {
|
private Function function(String namespace, String name, int addr) throws Exception {
|
||||||
|
Function f = ensureFunction(addr);
|
||||||
|
tx(program, () -> {
|
||||||
|
|
||||||
|
f.setName(name, SourceType.ANALYSIS);
|
||||||
|
Namespace ns = NamespaceUtils.createNamespaceHierarchy(namespace, null, program,
|
||||||
|
SourceType.ANALYSIS);
|
||||||
|
f.setParentNamespace(ns);
|
||||||
|
});
|
||||||
|
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void function(Function from, Function to) {
|
||||||
|
|
||||||
|
// a bit of space so the function call is not at the entry point
|
||||||
|
int offset = (int) from.getEntryPoint().getOffset() + 5;
|
||||||
|
int toOffset = (int) to.getEntryPoint().getOffset();
|
||||||
|
while (!createReference(offset, toOffset)) {
|
||||||
|
offset++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Function ensureFunction(long from) throws Exception {
|
||||||
ProgramDB p = builder.getProgram();
|
ProgramDB p = builder.getProgram();
|
||||||
FunctionManager fm = p.getFunctionManager();
|
FunctionManager fm = p.getFunctionManager();
|
||||||
Function f = fm.getFunctionAt(addr(from));
|
Function f = fm.getFunctionAt(addr(from));
|
||||||
if (f != null) {
|
if (f != null) {
|
||||||
return;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
String a = Long.toHexString(from);
|
String a = Long.toHexString(from);
|
||||||
builder.createEmptyFunction("Function_" + a, "0x" + a, 50, DataType.DEFAULT);
|
return builder.createEmptyFunction("Function_" + a, "0x" + a, 50, DataType.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean createReference(long from, long to) {
|
private boolean createReference(long from, long to) {
|
||||||
@@ -580,12 +608,10 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
|
|
||||||
setProviderFunction("0x5000");
|
setProviderFunction("0x5000");
|
||||||
|
|
||||||
final ToggleDockingAction filterDuplicatesAction =
|
ToggleDockingAction filterDuplicatesAction =
|
||||||
(ToggleDockingAction) getAction("Filter Duplicates");
|
(ToggleDockingAction) getAction("Filter Duplicates");
|
||||||
|
|
||||||
if (!filterDuplicatesAction.isSelected()) {
|
setToggleActionSelected(filterDuplicatesAction, new ActionContext(), true);
|
||||||
performAction(filterDuplicatesAction, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
waitForTree(outgoingTree);
|
waitForTree(outgoingTree);
|
||||||
GTreeNode rootNode = getRootNode(outgoingTree);
|
GTreeNode rootNode = getRootNode(outgoingTree);
|
||||||
@@ -597,8 +623,7 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
// copy the names of the children into a map so that we can verify that there are
|
// copy the names of the children into a map so that we can verify that there are
|
||||||
// no duplicates
|
// no duplicates
|
||||||
boolean shouldHaveDuplicates = false;
|
boolean shouldHaveDuplicates = false;
|
||||||
Map<String, Integer> nameCountMap = createNameCountMap(rootNode);
|
Map<GTreeNode, Integer> nameCountMap = createNameCountMap(rootNode);
|
||||||
System.out.println("nameCountMap: before: = " + nameCountMap);
|
|
||||||
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
|
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
|
||||||
|
|
||||||
performAction(filterDuplicatesAction, true);// deselect
|
performAction(filterDuplicatesAction, true);// deselect
|
||||||
@@ -608,7 +633,47 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
rootNode = getRootNode(outgoingTree);
|
rootNode = getRootNode(outgoingTree);
|
||||||
nameCountMap = createNameCountMap(rootNode);
|
nameCountMap = createNameCountMap(rootNode);
|
||||||
shouldHaveDuplicates = true;
|
shouldHaveDuplicates = true;
|
||||||
System.out.println("nameCountMap: after: = " + nameCountMap);
|
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testFilterIncomingDuplicates_SameFunctionNameInDifferentNamespaces()
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Create a function call tree that looks like:
|
||||||
|
|
||||||
|
|
||||||
|
NS1::Foo
|
||||||
|
root
|
||||||
|
NS2::Foo
|
||||||
|
root
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
Function f1 = function("NS1", "Foo", 0x11000);
|
||||||
|
Function f2 = function("NS2", "Foo", 0x12000);
|
||||||
|
Function root = function(0x0000);
|
||||||
|
function(f1, root);
|
||||||
|
function(f2, root);
|
||||||
|
|
||||||
|
setProviderFunction("0x0000");
|
||||||
|
|
||||||
|
ToggleDockingAction filterDuplicatesAction =
|
||||||
|
(ToggleDockingAction) getAction("Filter Duplicates");
|
||||||
|
setToggleActionSelected(filterDuplicatesAction, new ActionContext(), true);
|
||||||
|
waitForTree(incomingTree);
|
||||||
|
|
||||||
|
GTreeNode rootNode = getRootNode(incomingTree);
|
||||||
|
List<GTreeNode> children = rootNode.getChildren();
|
||||||
|
assertTrue(
|
||||||
|
"Outgoing tree does not have callers as expected for function: " + getListingFunction(),
|
||||||
|
children.size() > 0);
|
||||||
|
|
||||||
|
// copy the names of the children into a map so that we can verify that there are
|
||||||
|
// no duplicates
|
||||||
|
boolean shouldHaveDuplicates = false;
|
||||||
|
Map<GTreeNode, Integer> nameCountMap = createNameCountMap(rootNode);
|
||||||
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
|
assertDuplicateChildStatus(nameCountMap, shouldHaveDuplicates);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1071,27 +1136,26 @@ public class CallTreePluginTest extends AbstractGhidraHeadedIntegrationTest {
|
|||||||
return codeBrowserPlugin.getCurrentAddress();
|
return codeBrowserPlugin.getCurrentAddress();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Integer> createNameCountMap(GTreeNode node) {
|
private Map<GTreeNode, Integer> createNameCountMap(GTreeNode node) {
|
||||||
Map<String, Integer> map = new HashMap<>();
|
Map<GTreeNode, Integer> map = new HashMap<>();
|
||||||
List<GTreeNode> children = node.getChildren();
|
List<GTreeNode> children = node.getChildren();
|
||||||
for (GTreeNode child : children) {
|
for (GTreeNode child : children) {
|
||||||
String name = child.getName();
|
Integer integer = map.get(child);
|
||||||
Integer integer = map.get(name);
|
|
||||||
if (integer == null) {
|
if (integer == null) {
|
||||||
integer = 0;
|
integer = 0;
|
||||||
}
|
}
|
||||||
int asInt = integer;
|
int asInt = integer;
|
||||||
asInt++;
|
asInt++;
|
||||||
map.put(name, asInt);
|
map.put(child, asInt);
|
||||||
}
|
}
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertDuplicateChildStatus(Map<String, Integer> childCountMap,
|
private void assertDuplicateChildStatus(Map<GTreeNode, Integer> childCountMap,
|
||||||
boolean shouldHaveDuplicates) {
|
boolean shouldHaveDuplicates) {
|
||||||
boolean foundDuplicates = false;
|
boolean foundDuplicates = false;
|
||||||
Set<Entry<String, Integer>> entrySet = childCountMap.entrySet();
|
Set<Entry<GTreeNode, Integer>> entrySet = childCountMap.entrySet();
|
||||||
for (Entry<String, Integer> entry : entrySet) {
|
for (Entry<GTreeNode, Integer> entry : entrySet) {
|
||||||
int value = entry.getValue();
|
int value = entry.getValue();
|
||||||
if (value != 1) {
|
if (value != 1) {
|
||||||
foundDuplicates = true;
|
foundDuplicates = true;
|
||||||
|
|||||||
+3
-3
@@ -166,7 +166,7 @@ public class StructureEditorUnlockedActions2Test
|
|||||||
assertEquals(getDataType(4), dt3);
|
assertEquals(getDataType(4), dt3);
|
||||||
|
|
||||||
invoke(action);
|
invoke(action);
|
||||||
dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000);
|
dialog = waitForDialogComponent(NumberInputDialog.class);
|
||||||
assertNotNull(dialog);
|
assertNotNull(dialog);
|
||||||
okInput(dialog, 2);
|
okInput(dialog, 2);
|
||||||
dialog = null;
|
dialog = null;
|
||||||
@@ -180,7 +180,7 @@ public class StructureEditorUnlockedActions2Test
|
|||||||
|
|
||||||
setSelection(new int[] { 2 });
|
setSelection(new int[] { 2 });
|
||||||
invoke(action);
|
invoke(action);
|
||||||
dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000);
|
dialog = waitForDialogComponent(NumberInputDialog.class);
|
||||||
assertNotNull(dialog);
|
assertNotNull(dialog);
|
||||||
okInput(dialog, 2);
|
okInput(dialog, 2);
|
||||||
dialog = null;
|
dialog = null;
|
||||||
@@ -223,7 +223,7 @@ public class StructureEditorUnlockedActions2Test
|
|||||||
assertEquals(getDataType(1), dt1);
|
assertEquals(getDataType(1), dt1);
|
||||||
assertEquals(getDataType(2), dt2);
|
assertEquals(getDataType(2), dt2);
|
||||||
assertEquals(getDataType(3), dt3);
|
assertEquals(getDataType(3), dt3);
|
||||||
assertTrue(!"".equals(model.getStatus()));
|
assertNotEquals("", model.getStatus());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
+7
-7
@@ -45,7 +45,7 @@ public class StructureEditorUnlockedActions3Test
|
|||||||
DataType dt7 = getDataType(7);// SimpleUnion
|
DataType dt7 = getDataType(7);// SimpleUnion
|
||||||
|
|
||||||
invoke(duplicateMultipleAction);
|
invoke(duplicateMultipleAction);
|
||||||
dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000);
|
dialog = waitForDialogComponent(NumberInputDialog.class);
|
||||||
assertNotNull(dialog);
|
assertNotNull(dialog);
|
||||||
okInput(dialog, 2);
|
okInput(dialog, 2);
|
||||||
dialog = null;
|
dialog = null;
|
||||||
@@ -102,7 +102,7 @@ public class StructureEditorUnlockedActions3Test
|
|||||||
public void testEditFieldOnBlankLine() throws Exception {
|
public void testEditFieldOnBlankLine() throws Exception {
|
||||||
init(emptyStructure, pgmTestCat);
|
init(emptyStructure, pgmTestCat);
|
||||||
|
|
||||||
assertTrue(!model.isEditingField());
|
assertFalse(model.isEditingField());
|
||||||
triggerActionKey(getTable(), editFieldAction);
|
triggerActionKey(getTable(), editFieldAction);
|
||||||
assertTrue(model.isEditingField());
|
assertTrue(model.isEditingField());
|
||||||
assertEquals(0, model.getRow());
|
assertEquals(0, model.getRow());
|
||||||
@@ -116,7 +116,7 @@ public class StructureEditorUnlockedActions3Test
|
|||||||
init(complexStructure, pgmTestCat);
|
init(complexStructure, pgmTestCat);
|
||||||
|
|
||||||
setSelection(new int[] { 3 });
|
setSelection(new int[] { 3 });
|
||||||
assertTrue(!model.isEditingField());
|
assertFalse(model.isEditingField());
|
||||||
invoke(editFieldAction);
|
invoke(editFieldAction);
|
||||||
JTable table = getTable();
|
JTable table = getTable();
|
||||||
Container component = (Container) table.getEditorComponent();
|
Container component = (Container) table.getEditorComponent();
|
||||||
@@ -140,10 +140,10 @@ public class StructureEditorUnlockedActions3Test
|
|||||||
|
|
||||||
DataTypeComponent dtc = model.getComponent(3);
|
DataTypeComponent dtc = model.getComponent(3);
|
||||||
assertNotNull(dtc);
|
assertNotNull(dtc);
|
||||||
assertTrue(!dtc.isBitFieldComponent());
|
assertFalse(dtc.isBitFieldComponent());
|
||||||
|
|
||||||
setSelection(new int[] { 3 });
|
setSelection(new int[] { 3 });
|
||||||
assertTrue(!model.isEditingField());
|
assertFalse(model.isEditingField());
|
||||||
invoke(editFieldAction);
|
invoke(editFieldAction);
|
||||||
JTable table = getTable();
|
JTable table = getTable();
|
||||||
Container component = (Container) table.getEditorComponent();
|
Container component = (Container) table.getEditorComponent();
|
||||||
@@ -156,7 +156,7 @@ public class StructureEditorUnlockedActions3Test
|
|||||||
|
|
||||||
waitForSwing();
|
waitForSwing();
|
||||||
|
|
||||||
assertTrue(!model.isEditingField());
|
assertFalse(model.isEditingField());
|
||||||
assertEquals(3, model.getRow());
|
assertEquals(3, model.getRow());
|
||||||
assertNotEditingField();
|
assertNotEditingField();
|
||||||
|
|
||||||
@@ -192,7 +192,7 @@ public class StructureEditorUnlockedActions3Test
|
|||||||
|
|
||||||
int num = model.getNumComponents();
|
int num = model.getNumComponents();
|
||||||
setSelection(new int[] { 3 });
|
setSelection(new int[] { 3 });
|
||||||
assertTrue(!getDataType(3).isEquivalent(dt));
|
assertFalse(getDataType(3).isEquivalent(dt));
|
||||||
invoke(fav);// replacing dword with byte followed by 3 undefineds
|
invoke(fav);// replacing dword with byte followed by 3 undefineds
|
||||||
assertEquals(num + 3, model.getNumComponents());
|
assertEquals(num + 3, model.getNumComponents());
|
||||||
assertTrue(getDataType(3).isEquivalent(dt));
|
assertTrue(getDataType(3).isEquivalent(dt));
|
||||||
|
|||||||
+45
-77
@@ -25,8 +25,7 @@ import org.junit.Test;
|
|||||||
|
|
||||||
import docking.widgets.dialogs.NumberInputDialog;
|
import docking.widgets.dialogs.NumberInputDialog;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.util.InvalidNameException;
|
import ghidra.util.exception.UsrException;
|
||||||
import ghidra.util.exception.*;
|
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
public class StructureEditorUnlockedActions4Test
|
public class StructureEditorUnlockedActions4Test
|
||||||
@@ -76,7 +75,7 @@ public class StructureEditorUnlockedActions4Test
|
|||||||
|
|
||||||
// Make array of 3 pointers
|
// Make array of 3 pointers
|
||||||
invoke(arrayAction);
|
invoke(arrayAction);
|
||||||
dialog = env.waitForDialogComponent(NumberInputDialog.class, 1000);
|
dialog = waitForDialogComponent(NumberInputDialog.class);
|
||||||
assertNotNull(dialog);
|
assertNotNull(dialog);
|
||||||
okInput(dialog, 3);
|
okInput(dialog, 3);
|
||||||
dialog = null;
|
dialog = null;
|
||||||
@@ -103,25 +102,12 @@ public class StructureEditorUnlockedActions4Test
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDuplicateAction() throws Exception {
|
public void testDuplicateAction() throws Throwable {
|
||||||
init(complexStructure, pgmTestCat);
|
init(complexStructure, pgmTestCat);
|
||||||
runSwing(() -> {
|
runSwingWithException(() -> {
|
||||||
try {
|
model.setComponentName(1, "comp1");
|
||||||
model.setComponentName(1, "comp1");
|
model.setComponentComment(1, "comment 1");
|
||||||
model.setComponentComment(1, "comment 1");
|
model.clearComponent(2);
|
||||||
model.clearComponent(2);
|
|
||||||
}
|
|
||||||
catch (InvalidInputException e) {
|
|
||||||
failWithException("Unexpected error", e);
|
|
||||||
}
|
|
||||||
catch (InvalidNameException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
catch (DuplicateNameException e) {
|
|
||||||
// TODO Auto-generated catch block
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
int len = model.getLength();
|
int len = model.getLength();
|
||||||
int num = model.getNumComponents();
|
int num = model.getNumComponents();
|
||||||
@@ -150,7 +136,7 @@ public class StructureEditorUnlockedActions4Test
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testEditComponentAction() throws Exception {
|
public void testEditComponentAction() throws Exception {
|
||||||
// init(complexStructure, pgmTestCat);
|
|
||||||
runSwing(() -> {
|
runSwing(() -> {
|
||||||
installProvider(new StructureEditorProvider(plugin, complexStructure, false));
|
installProvider(new StructureEditorProvider(plugin, complexStructure, false));
|
||||||
model = provider.getModel();
|
model = provider.getModel();
|
||||||
@@ -159,12 +145,46 @@ public class StructureEditorUnlockedActions4Test
|
|||||||
getActions();
|
getActions();
|
||||||
|
|
||||||
assertEquals("", model.getStatus());
|
assertEquals("", model.getStatus());
|
||||||
setSelection(new int[] { 21 });
|
setSelection(new int[] { 21 }); // 'simpleStructure'
|
||||||
String complexSubTitle = getProviderSubTitle(complexStructure);
|
String complexSubTitle = getProviderSubTitle(complexStructure);
|
||||||
String simpleSubTitle = getProviderSubTitle(simpleStructure);
|
String simpleSubTitle = getProviderSubTitle(simpleStructure);
|
||||||
assertTrue("Couldn't find editor = " + complexSubTitle,
|
assertTrue("Couldn't find editor = " + complexSubTitle,
|
||||||
isProviderShown(tool.getToolFrame(), "Structure Editor", complexSubTitle));
|
isProviderShown(tool.getToolFrame(), "Structure Editor", complexSubTitle));
|
||||||
assertTrue(!isProviderShown(tool.getToolFrame(), "Structure Editor", simpleSubTitle));
|
assertFalse(isProviderShown(tool.getToolFrame(), "Structure Editor", simpleSubTitle));
|
||||||
|
|
||||||
|
invoke(editComponentAction);
|
||||||
|
assertEquals("", model.getStatus());
|
||||||
|
assertTrue("Couldn't find editor = " + complexSubTitle,
|
||||||
|
isProviderShown(tool.getToolFrame(), "Structure Editor", complexSubTitle));
|
||||||
|
assertTrue("Couldn't find editor = " + simpleSubTitle,
|
||||||
|
isProviderShown(tool.getToolFrame(), "Structure Editor", simpleSubTitle));
|
||||||
|
|
||||||
|
runSwing(() -> provider.closeComponent());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEditComponentAction_ComplexStructure() throws Exception {
|
||||||
|
|
||||||
|
//
|
||||||
|
// Test that the Edit Component action will work when the type is multi-layered, like
|
||||||
|
// a pointer to a pointer to a structure
|
||||||
|
//
|
||||||
|
|
||||||
|
runSwing(() -> {
|
||||||
|
installProvider(new StructureEditorProvider(plugin, complexStructure, false));
|
||||||
|
model = provider.getModel();
|
||||||
|
});
|
||||||
|
waitForSwing();
|
||||||
|
getActions();
|
||||||
|
|
||||||
|
assertEquals("", model.getStatus());
|
||||||
|
setSelection(new int[] { 20 }); // 'simpleStructureTypedef * *[2][3]'
|
||||||
|
String complexSubTitle = getProviderSubTitle(complexStructure);
|
||||||
|
String simpleSubTitle = getProviderSubTitle(simpleStructure);
|
||||||
|
assertTrue("Couldn't find editor = " + complexSubTitle,
|
||||||
|
isProviderShown(tool.getToolFrame(), "Structure Editor", complexSubTitle));
|
||||||
|
assertFalse(isProviderShown(tool.getToolFrame(), "Structure Editor", simpleSubTitle));
|
||||||
|
|
||||||
invoke(editComponentAction);
|
invoke(editComponentAction);
|
||||||
assertEquals("", model.getStatus());
|
assertEquals("", model.getStatus());
|
||||||
assertTrue("Couldn't find editor = " + complexSubTitle,
|
assertTrue("Couldn't find editor = " + complexSubTitle,
|
||||||
@@ -194,58 +214,6 @@ public class StructureEditorUnlockedActions4Test
|
|||||||
assertEquals(num + 13, model.getNumComponents());
|
assertEquals(num + 13, model.getNumComponents());
|
||||||
}
|
}
|
||||||
|
|
||||||
// public void testCancelPointerOnFixedDt() throws Exception {
|
|
||||||
// // FUTURE
|
|
||||||
// init(complexStructure, pgmTestCat);
|
|
||||||
// NumberInputDialog dialog;
|
|
||||||
// int num = model.getNumComponents();
|
|
||||||
//
|
|
||||||
// setSelection(new int[] {2});
|
|
||||||
// DataType dt2 = getDataType(2);
|
|
||||||
// assertTrue(getDataType(2).isEquivalent(new WordDataType()));
|
|
||||||
// invoke(pointerAction);
|
|
||||||
// dialog = (NumberInputDialog)env.waitForDialog(NumberInputDialog.class, 1000);
|
|
||||||
// assertNotNull(dialog);
|
|
||||||
// cancelInput(dialog);
|
|
||||||
// dialog.dispose();
|
|
||||||
// dialog = null;
|
|
||||||
// assertEquals(num, model.getNumComponents());
|
|
||||||
// assertEquals("word", getDataType(2).getDisplayName());
|
|
||||||
// assertTrue(getDataType(2).isEquivalent(dt2));
|
|
||||||
// assertEquals(4, model.getComponent(2).getLength());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// @Test
|
|
||||||
// public void testCreatePointerOnArray() throws Exception {
|
|
||||||
// init(complexStructure, pgmTestCat);
|
|
||||||
// int num = model.getNumComponents();
|
|
||||||
//
|
|
||||||
// setSelection(new int[] { 14 });
|
|
||||||
// DataType dt14 = getDataType(14);
|
|
||||||
// assertEquals("byte[7]", dt14.getDisplayName());
|
|
||||||
// invoke(pointerAction);
|
|
||||||
// assertEquals(num + 3, model.getNumComponents());
|
|
||||||
// assertEquals("byte[7] *", getDataType(14).getDisplayName());
|
|
||||||
// assertTrue(((Pointer) getDataType(14)).getDataType().isEquivalent(dt14));
|
|
||||||
// assertEquals(4, getDataType(14).getLength());
|
|
||||||
// assertEquals(4, model.getComponent(14).getLength());
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Test
|
|
||||||
// public void testCreatePointerOnTypedef() throws Exception {
|
|
||||||
// init(complexStructure, pgmTestCat);
|
|
||||||
// int num = model.getNumComponents();
|
|
||||||
//
|
|
||||||
// setSelection(new int[] { 19 });
|
|
||||||
// DataType dt19 = getDataType(19);
|
|
||||||
// assertEquals("simpleStructureTypedef", dt19.getDisplayName());
|
|
||||||
// invoke(pointerAction);
|
|
||||||
// assertEquals(num + 25, model.getNumComponents());
|
|
||||||
// assertEquals("simpleStructureTypedef *", getDataType(19).getDisplayName());
|
|
||||||
// assertTrue(((Pointer) getDataType(19)).getDataType().isEquivalent(dt19));
|
|
||||||
// assertEquals(4, model.getComponent(19).getLength());
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testApplyComponentChange() throws Exception {
|
public void testApplyComponentChange() throws Exception {
|
||||||
init(complexStructure, pgmTestCat);
|
init(complexStructure, pgmTestCat);
|
||||||
@@ -262,7 +230,7 @@ public class StructureEditorUnlockedActions4Test
|
|||||||
});
|
});
|
||||||
DataType viewCopy = model.viewComposite.clone(null);
|
DataType viewCopy = model.viewComposite.clone(null);
|
||||||
|
|
||||||
assertTrue(!complexStructure.isEquivalent(model.viewComposite));
|
assertFalse(complexStructure.isEquivalent(model.viewComposite));
|
||||||
assertTrue(viewCopy.isEquivalent(model.viewComposite));
|
assertTrue(viewCopy.isEquivalent(model.viewComposite));
|
||||||
invoke(applyAction);
|
invoke(applyAction);
|
||||||
assertTrue(viewCopy.isEquivalent(complexStructure));
|
assertTrue(viewCopy.isEquivalent(complexStructure));
|
||||||
|
|||||||
+2
-1
@@ -76,7 +76,8 @@ public class PrelinkFileSystem extends GFileSystemBase implements GFileSystemPro
|
|||||||
@Override
|
@Override
|
||||||
public boolean isValid(TaskMonitor monitor) throws IOException {
|
public boolean isValid(TaskMonitor monitor) throws IOException {
|
||||||
try {
|
try {
|
||||||
return !MachoPrelinkUtils.parsePrelinkXml(provider, monitor).isEmpty();
|
return MachHeader.isMachHeader(provider) &&
|
||||||
|
!MachoPrelinkUtils.parsePrelinkXml(provider, monitor).isEmpty();
|
||||||
}
|
}
|
||||||
catch (JDOMException e) {
|
catch (JDOMException e) {
|
||||||
Msg.warn(this, e.getMessage());
|
Msg.warn(this, e.getMessage());
|
||||||
|
|||||||
+29
-17
@@ -1109,8 +1109,11 @@ public class GnuDemanglerParser {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
|
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
|
||||||
return builder.append("name", name).append("prefix", prefix).append("type",
|
return builder.append("name", name)
|
||||||
type).append("demangled", demangled).toString();
|
.append("prefix", prefix)
|
||||||
|
.append("type", type)
|
||||||
|
.append("demangled", demangled)
|
||||||
|
.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1246,31 +1249,36 @@ public class GnuDemanglerParser {
|
|||||||
//
|
//
|
||||||
String operatorChars = matcher.group(2);
|
String operatorChars = matcher.group(2);
|
||||||
String templates = matcher.group(3);
|
String templates = matcher.group(3);
|
||||||
templates = templates == null ? "" : templates;
|
int start = matcher.start(2); // operator chars start
|
||||||
|
int end = matcher.end(3); // templates end
|
||||||
|
|
||||||
|
if (templates == null) {
|
||||||
|
templates = "";
|
||||||
|
end = matcher.end(2); // no templates; end of the operator chars
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// The 'operator' functions have symbols that confuse our default function parsing.
|
// The 'operator' functions have symbols that confuse our default function parsing.
|
||||||
// Specifically, operators that use shift symbols (<, <<, >, >>) will cause our
|
// Specifically, operators that use shift symbols (<, <<, >, >>) will cause our
|
||||||
// template parsing to fail. To default the failure, we will install a temporary
|
// template parsing to fail. To defeat the failure, we will install a temporary
|
||||||
// function name here and then restore it after parsing is finished.
|
// function name here and then restore it after parsing is finished.
|
||||||
//
|
//
|
||||||
String originalPrefix = OPERATOR + operatorChars + templates;
|
String rawPrefix = OPERATOR + demangled.substring(start, end);
|
||||||
String placeholder = "TEMPNAMEPLACEHOLDERVALUE";
|
String placeholder = "TEMPNAMEPLACEHOLDERVALUE";
|
||||||
String tempName = demangled.replace(originalPrefix, placeholder);
|
String tempName = demangled.replace(rawPrefix, placeholder);
|
||||||
|
|
||||||
DemangledFunction function = (DemangledFunction) parseFunctionOrVariable(tempName);
|
DemangledFunction function = (DemangledFunction) parseFunctionOrVariable(tempName);
|
||||||
function.setOverloadedOperator(true);
|
function.setOverloadedOperator(true);
|
||||||
function.setName(originalPrefix);
|
|
||||||
|
|
||||||
if (!StringUtils.isBlank(templates)) {
|
String simpleName = OPERATOR + operatorChars;
|
||||||
String escapedPrefix = removeBadSpaces(originalPrefix);
|
if (StringUtils.isBlank(templates)) {
|
||||||
|
function.setName(simpleName);
|
||||||
|
}
|
||||||
|
else {
|
||||||
String escapedTemplates = removeBadSpaces(templates);
|
String escapedTemplates = removeBadSpaces(templates);
|
||||||
int templateIndex = escapedPrefix.indexOf(escapedTemplates);
|
|
||||||
String operatorName = escapedPrefix.substring(0, templateIndex);
|
|
||||||
DemangledTemplate demangledTemplate = parseTemplate(escapedTemplates);
|
DemangledTemplate demangledTemplate = parseTemplate(escapedTemplates);
|
||||||
|
|
||||||
function.setTemplate(demangledTemplate);
|
function.setTemplate(demangledTemplate);
|
||||||
function.setName(operatorName);
|
function.setName(simpleName);
|
||||||
}
|
}
|
||||||
|
|
||||||
return function;
|
return function;
|
||||||
@@ -1431,8 +1439,10 @@ public class GnuDemanglerParser {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
|
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
|
||||||
return builder.append("text", text).append("paramStart", paramStart).append("paramEnd",
|
return builder.append("text", text)
|
||||||
paramEnd).toString();
|
.append("paramStart", paramStart)
|
||||||
|
.append("paramEnd", paramEnd)
|
||||||
|
.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isContainedWithinNamespace() {
|
private boolean isContainedWithinNamespace() {
|
||||||
@@ -1490,8 +1500,10 @@ public class GnuDemanglerParser {
|
|||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
|
ToStringBuilder builder = new ToStringBuilder(this, ToStringStyle.JSON_STYLE);
|
||||||
return builder.append("fullText", fullText).append("params", params).append("trailing",
|
return builder.append("fullText", fullText)
|
||||||
trailing).toString();
|
.append("params", params)
|
||||||
|
.append("trailing", trailing)
|
||||||
|
.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+19
-1
@@ -843,7 +843,7 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOverloadedShiftOperatorParsingBug() {
|
public void testOverloadedShiftOperatorTemplated_RightShift() {
|
||||||
parser = new GnuDemanglerParser();
|
parser = new GnuDemanglerParser();
|
||||||
DemangledObject object = parser.parse("fakemangled",
|
DemangledObject object = parser.parse("fakemangled",
|
||||||
"std::basic_istream<char, std::char_traits<char> >& " +
|
"std::basic_istream<char, std::char_traits<char> >& " +
|
||||||
@@ -858,6 +858,24 @@ public class GnuDemanglerParserTest extends AbstractGenericTest {
|
|||||||
object.getSignature());
|
object.getSignature());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOverloadedShiftOperatorTemplated_LeftShift() {
|
||||||
|
|
||||||
|
String raw =
|
||||||
|
"std::basic_ostream<char, std::char_traits<char> >& " +
|
||||||
|
"std::operator<< <std::char_traits<char> >" +
|
||||||
|
"(std::basic_ostream<char, std::char_traits<char> >&, char const*)";
|
||||||
|
String formatted = "std::basic_ostream<char,std::char_traits<char>> & " +
|
||||||
|
"std::operator<<<std::char_traits<char>>" +
|
||||||
|
"(std::basic_ostream<char,std::char_traits<char>> &,char const *)";
|
||||||
|
DemangledObject object = parser.parse(
|
||||||
|
"_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc",
|
||||||
|
raw);
|
||||||
|
String name = object.getName();
|
||||||
|
assertEquals("operator<<", name);
|
||||||
|
assertEquals(formatted, object.getSignature());
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testOverloadedLeftShiftOperatorWithFunctionPointer() {
|
public void testOverloadedLeftShiftOperatorWithFunctionPointer() {
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ import sun.awt.AppContext;
|
|||||||
import utilities.util.FileUtilities;
|
import utilities.util.FileUtilities;
|
||||||
import utilities.util.reflection.ReflectionUtilities;
|
import utilities.util.reflection.ReflectionUtilities;
|
||||||
import utility.application.ApplicationLayout;
|
import utility.application.ApplicationLayout;
|
||||||
|
import utility.function.ExceptionalCallback;
|
||||||
|
|
||||||
public abstract class AbstractGenericTest extends AbstractGTest {
|
public abstract class AbstractGenericTest extends AbstractGTest {
|
||||||
|
|
||||||
@@ -1115,23 +1116,32 @@ public abstract class AbstractGenericTest extends AbstractGTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Call this version of {@link #runSwing(Runnable)} when you expect your runnable to throw
|
* Call this version of {@link #runSwing(Runnable)} when you expect your runnable <b>may</b>
|
||||||
* an exception
|
* throw exceptions
|
||||||
* @param runnable the runnable
|
*
|
||||||
* @param wait true signals to wait for the Swing operation to finish
|
* @param callback the runnable code snippet to call
|
||||||
* @throws Throwable any exception that is thrown on the Swing thread
|
* @throws Exception any exception that is thrown on the Swing thread
|
||||||
*/
|
*/
|
||||||
public static void runSwingWithExceptions(Runnable runnable, boolean wait) throws Throwable {
|
public static <E extends Exception> void runSwingWithException(ExceptionalCallback<E> callback)
|
||||||
|
throws Exception {
|
||||||
|
|
||||||
if (Swing.isSwingThread()) {
|
if (Swing.isSwingThread()) {
|
||||||
throw new AssertException("Unexpectedly called from the Swing thread");
|
throw new AssertException("Unexpectedly called from the Swing thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
ExceptionHandlingRunner exceptionHandlingRunner = new ExceptionHandlingRunner(runnable);
|
ExceptionHandlingRunner exceptionHandlingRunner = new ExceptionHandlingRunner(callback);
|
||||||
Throwable throwable = exceptionHandlingRunner.getException();
|
Throwable throwable = exceptionHandlingRunner.getException();
|
||||||
if (throwable != null) {
|
if (throwable == null) {
|
||||||
throw throwable;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (throwable instanceof Exception) {
|
||||||
|
// this is what the client expected
|
||||||
|
throw (Exception) throwable;
|
||||||
|
}
|
||||||
|
|
||||||
|
// a runtime exception; re-throw
|
||||||
|
throw new AssertException(throwable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void runSwing(Runnable runnable, boolean wait) {
|
public static void runSwing(Runnable runnable, boolean wait) {
|
||||||
@@ -1170,11 +1180,18 @@ public abstract class AbstractGenericTest extends AbstractGTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected static class ExceptionHandlingRunner {
|
protected static class ExceptionHandlingRunner {
|
||||||
private final Runnable delegateRunnable;
|
private final ExceptionalCallback<? extends Exception> delegateCallback;
|
||||||
private Throwable exception;
|
private Throwable exception;
|
||||||
|
|
||||||
ExceptionHandlingRunner(Runnable delegateRunnable) {
|
ExceptionHandlingRunner(Runnable delegateRunnable) {
|
||||||
this.delegateRunnable = delegateRunnable;
|
this.delegateCallback = () -> {
|
||||||
|
delegateRunnable.run();
|
||||||
|
};
|
||||||
|
run();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExceptionHandlingRunner(ExceptionalCallback<? extends Exception> delegateCallback) {
|
||||||
|
this.delegateCallback = delegateCallback;
|
||||||
run();
|
run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1213,7 +1230,7 @@ public abstract class AbstractGenericTest extends AbstractGTest {
|
|||||||
|
|
||||||
Runnable swingExceptionCatcher = () -> {
|
Runnable swingExceptionCatcher = () -> {
|
||||||
try {
|
try {
|
||||||
delegateRunnable.run();
|
delegateCallback.call();
|
||||||
}
|
}
|
||||||
catch (Throwable t) {
|
catch (Throwable t) {
|
||||||
exception = t;
|
exception = t;
|
||||||
|
|||||||
@@ -96,14 +96,21 @@ public class GhidraApplicationLayout extends ApplicationLayout {
|
|||||||
// Application properties
|
// Application properties
|
||||||
applicationProperties = new ApplicationProperties(applicationRootDirs);
|
applicationProperties = new ApplicationProperties(applicationRootDirs);
|
||||||
|
|
||||||
// Modules
|
|
||||||
modules = findGhidraModules();
|
|
||||||
|
|
||||||
// User directories
|
// User directories
|
||||||
userTempDir = ApplicationUtilities.getDefaultUserTempDir(getApplicationProperties());
|
userTempDir = ApplicationUtilities.getDefaultUserTempDir(getApplicationProperties());
|
||||||
userCacheDir = ApplicationUtilities.getDefaultUserCacheDir(getApplicationProperties());
|
userCacheDir = ApplicationUtilities.getDefaultUserCacheDir(getApplicationProperties());
|
||||||
userSettingsDir = ApplicationUtilities.getDefaultUserSettingsDir(getApplicationProperties(),
|
userSettingsDir = ApplicationUtilities.getDefaultUserSettingsDir(getApplicationProperties(),
|
||||||
getApplicationInstallationDir());
|
getApplicationInstallationDir());
|
||||||
|
|
||||||
|
// Extensions
|
||||||
|
extensionInstallationDirs = findExtensionInstallationDirectories();
|
||||||
|
extensionArchiveDir = findExtensionArchiveDirectory();
|
||||||
|
|
||||||
|
// Patch directory
|
||||||
|
patchDir = findPatchDirectory();
|
||||||
|
|
||||||
|
// Modules
|
||||||
|
modules = findGhidraModules();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -145,7 +152,7 @@ public class GhidraApplicationLayout extends ApplicationLayout {
|
|||||||
|
|
||||||
// Find standard module root directories from within the application root directories
|
// Find standard module root directories from within the application root directories
|
||||||
Collection<ResourceFile> moduleRootDirectories =
|
Collection<ResourceFile> moduleRootDirectories =
|
||||||
ModuleUtilities.findModuleRootDirectories(applicationRootDirs, new ArrayList<>());
|
ModuleUtilities.findModuleRootDirectories(applicationRootDirs, new LinkedHashSet<>());
|
||||||
|
|
||||||
// Find installed extension modules
|
// Find installed extension modules
|
||||||
for (ResourceFile extensionInstallDir : extensionInstallationDirs) {
|
for (ResourceFile extensionInstallDir : extensionInstallationDirs) {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ Module.manifest||GHIDRA||||END|
|
|||||||
build.gradle||GHIDRA||||END|
|
build.gradle||GHIDRA||||END|
|
||||||
data/languages/ARM.cspec||GHIDRA||||END|
|
data/languages/ARM.cspec||GHIDRA||||END|
|
||||||
data/languages/ARM.dwarf||GHIDRA||||END|
|
data/languages/ARM.dwarf||GHIDRA||||END|
|
||||||
|
data/languages/ARM.gdis||GHIDRA||||END|
|
||||||
data/languages/ARM.ldefs||GHIDRA||||END|
|
data/languages/ARM.ldefs||GHIDRA||||END|
|
||||||
data/languages/ARM.opinion||GHIDRA||||END|
|
data/languages/ARM.opinion||GHIDRA||||END|
|
||||||
data/languages/ARM.sinc||GHIDRA||||END|
|
data/languages/ARM.sinc||GHIDRA||||END|
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
<!--
|
||||||
|
This file determines the disassembler options sent to the GNU external disassembler.
|
||||||
|
You can see which options are available for all architectures in the objdump man page.
|
||||||
|
Given a version of objdump compiled for a specific architecture, you can see what options
|
||||||
|
are available with "objdump -i -m"
|
||||||
|
|
||||||
|
The options for ARM are:
|
||||||
|
"reg-names-std" (the default)
|
||||||
|
"reg-names-apcs"
|
||||||
|
"reg-names-raw"
|
||||||
|
"reg-names-apcs"
|
||||||
|
"reg-names-special-apcs"
|
||||||
|
"force-thumb" (force thumb disassembly)
|
||||||
|
"no-force-thumb" (force arm disassembly)
|
||||||
|
(see the objdump manpage for the precise details about the different register
|
||||||
|
naming options)
|
||||||
|
|
||||||
|
"optstring" is the string of options, and "display_prefix" is an optional prefix prepended to the
|
||||||
|
external disassembly field. In this file, "A: " is prepended to ARM disassembly, and
|
||||||
|
"T: " is prepended to Thumb disassembly.
|
||||||
|
|
||||||
|
To send multiple options, concatenate them into a CSV list. For example, "no-force-thumb,reg-names-raw"
|
||||||
|
is a valid optstring. You can also have a "global" element whose "optstring" attribute is
|
||||||
|
always sent to the GNU disassembler.
|
||||||
|
|
||||||
|
If there is not a context register defined, the global optstring is sent by itself (see x86-16.gdis for
|
||||||
|
an example). If there is a context register defined, the global optstring is prepended to the
|
||||||
|
optstring determined by a context register value.
|
||||||
|
|
||||||
|
This file must be listed in the .ldefs file for the processor, e.g.
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="ARM.gdis"/>
|
||||||
|
-->
|
||||||
|
|
||||||
|
<gdis>
|
||||||
|
<context_register>TMode</context_register>
|
||||||
|
<options>
|
||||||
|
<option value="0x0" optstring="no-force-thumb" display_prefix = "A: "/>
|
||||||
|
<option value="0x1" optstring="force-thumb" display_prefix = "T: "/>
|
||||||
|
</options>
|
||||||
|
</gdis>
|
||||||
|
|
||||||
@@ -14,6 +14,7 @@
|
|||||||
<compiler name="default" spec="ARM.cspec" id="default"/>
|
<compiler name="default" spec="ARM.cspec" id="default"/>
|
||||||
<compiler name="Visual Studio" spec="ARM_win.cspec" id="windows"/>
|
<compiler name="Visual Studio" spec="ARM_win.cspec" id="windows"/>
|
||||||
<external_name tool="gnu" name="iwmmxt"/>
|
<external_name tool="gnu" name="iwmmxt"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="ARM.gdis"/>
|
||||||
<external_name tool="IDA-PRO" name="arm"/>
|
<external_name tool="IDA-PRO" name="arm"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="ARMneon.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="ARMneon.dwarf"/>
|
||||||
</language>
|
</language>
|
||||||
@@ -31,6 +32,7 @@
|
|||||||
<compiler name="default" spec="ARM.cspec" id="default"/>
|
<compiler name="default" spec="ARM.cspec" id="default"/>
|
||||||
<compiler name="Visual Studio" spec="ARM_win.cspec" id="windows"/>
|
<compiler name="Visual Studio" spec="ARM_win.cspec" id="windows"/>
|
||||||
<external_name tool="gnu" name="iwmmxt"/>
|
<external_name tool="gnu" name="iwmmxt"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="ARM.gdis"/>
|
||||||
<external_name tool="IDA-PRO" name="arm"/>
|
<external_name tool="IDA-PRO" name="arm"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="ARMneon.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="ARMneon.dwarf"/>
|
||||||
</language>
|
</language>
|
||||||
@@ -63,6 +65,7 @@
|
|||||||
<description>Generic ARM/Thumb v8 big endian</description>
|
<description>Generic ARM/Thumb v8 big endian</description>
|
||||||
<compiler name="default" spec="ARM.cspec" id="default"/>
|
<compiler name="default" spec="ARM.cspec" id="default"/>
|
||||||
<external_name tool="gnu" name="iwmmxt"/>
|
<external_name tool="gnu" name="iwmmxt"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="ARM.gdis"/>
|
||||||
<external_name tool="IDA-PRO" name="armb"/>
|
<external_name tool="IDA-PRO" name="armb"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="ARMneon.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="ARMneon.dwarf"/>
|
||||||
</language>
|
</language>
|
||||||
@@ -96,6 +99,7 @@
|
|||||||
<compiler name="default" spec="ARM.cspec" id="default"/>
|
<compiler name="default" spec="ARM.cspec" id="default"/>
|
||||||
<compiler name="Visual Studio" spec="ARM_win.cspec" id="windows"/>
|
<compiler name="Visual Studio" spec="ARM_win.cspec" id="windows"/>
|
||||||
<external_name tool="gnu" name="iwmmxt"/>
|
<external_name tool="gnu" name="iwmmxt"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="ARM.gdis"/>
|
||||||
<external_name tool="IDA-PRO" name="arm"/>
|
<external_name tool="IDA-PRO" name="arm"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="ARMneon.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="ARMneon.dwarf"/>
|
||||||
</language>
|
</language>
|
||||||
@@ -128,6 +132,7 @@
|
|||||||
<description>Generic ARM/Thumb v7 big endian</description>
|
<description>Generic ARM/Thumb v7 big endian</description>
|
||||||
<compiler name="default" spec="ARM.cspec" id="default"/>
|
<compiler name="default" spec="ARM.cspec" id="default"/>
|
||||||
<external_name tool="gnu" name="iwmmxt"/>
|
<external_name tool="gnu" name="iwmmxt"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="ARM.gdis"/>
|
||||||
<external_name tool="IDA-PRO" name="armb"/>
|
<external_name tool="IDA-PRO" name="armb"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="ARMneon.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="ARMneon.dwarf"/>
|
||||||
</language>
|
</language>
|
||||||
@@ -174,6 +179,7 @@
|
|||||||
<description>Generic ARM/Thumb v6 little endian</description>
|
<description>Generic ARM/Thumb v6 little endian</description>
|
||||||
<compiler name="default" spec="ARM_v45.cspec" id="default"/>
|
<compiler name="default" spec="ARM_v45.cspec" id="default"/>
|
||||||
<external_name tool="gnu" name="xscale"/>
|
<external_name tool="gnu" name="xscale"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="ARM.gdis"/>
|
||||||
<external_name tool="IDA-PRO" name="arm"/>
|
<external_name tool="IDA-PRO" name="arm"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="ARM.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="ARM.dwarf"/>
|
||||||
<!-- change DWARF register mapping to ARMneon.dwarf if VFPv2 is enabled -->
|
<!-- change DWARF register mapping to ARMneon.dwarf if VFPv2 is enabled -->
|
||||||
@@ -192,6 +198,7 @@
|
|||||||
<description>Generic ARM/Thumb v6 big endian</description>
|
<description>Generic ARM/Thumb v6 big endian</description>
|
||||||
<compiler name="default" spec="ARM_v45.cspec" id="default"/>
|
<compiler name="default" spec="ARM_v45.cspec" id="default"/>
|
||||||
<external_name tool="gnu" name="xscale"/>
|
<external_name tool="gnu" name="xscale"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="ARM.gdis"/>
|
||||||
<external_name tool="IDA-PRO" name="armb"/>
|
<external_name tool="IDA-PRO" name="armb"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="ARM.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="ARM.dwarf"/>
|
||||||
<!-- change DWARF register mapping to ARMneon.dwarf if VFPv2 is enabled -->
|
<!-- change DWARF register mapping to ARMneon.dwarf if VFPv2 is enabled -->
|
||||||
@@ -210,6 +217,7 @@
|
|||||||
<description>Generic ARM/Thumb v5 little endian (T-variant)</description>
|
<description>Generic ARM/Thumb v5 little endian (T-variant)</description>
|
||||||
<compiler name="default" spec="ARM_v45.cspec" id="default"/>
|
<compiler name="default" spec="ARM_v45.cspec" id="default"/>
|
||||||
<external_name tool="gnu" name="armv5t"/>
|
<external_name tool="gnu" name="armv5t"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="ARM.gdis"/>
|
||||||
<external_name tool="IDA-PRO" name="arm"/>
|
<external_name tool="IDA-PRO" name="arm"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="ARM.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="ARM.dwarf"/>
|
||||||
</language>
|
</language>
|
||||||
@@ -226,6 +234,7 @@
|
|||||||
<description>Generic ARM/Thumb v5 big endian (T-variant)</description>
|
<description>Generic ARM/Thumb v5 big endian (T-variant)</description>
|
||||||
<compiler name="default" spec="ARM_v45.cspec" id="default"/>
|
<compiler name="default" spec="ARM_v45.cspec" id="default"/>
|
||||||
<external_name tool="gnu" name="armv5t"/>
|
<external_name tool="gnu" name="armv5t"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="ARM.gdis"/>
|
||||||
<external_name tool="IDA-PRO" name="armb"/>
|
<external_name tool="IDA-PRO" name="armb"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="ARM.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="ARM.dwarf"/>
|
||||||
</language>
|
</language>
|
||||||
@@ -274,6 +283,7 @@
|
|||||||
<description>Generic ARM/Thumb v4 little endian (T-variant)</description>
|
<description>Generic ARM/Thumb v4 little endian (T-variant)</description>
|
||||||
<compiler name="default" spec="ARM_v45.cspec" id="default"/>
|
<compiler name="default" spec="ARM_v45.cspec" id="default"/>
|
||||||
<external_name tool="gnu" name="armv4t"/>
|
<external_name tool="gnu" name="armv4t"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="ARM.gdis"/>
|
||||||
<external_name tool="IDA-PRO" name="arm"/>
|
<external_name tool="IDA-PRO" name="arm"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="ARM.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="ARM.dwarf"/>
|
||||||
</language>
|
</language>
|
||||||
@@ -290,6 +300,7 @@
|
|||||||
<description>Generic ARM/Thumb v4 big endian (T-variant)</description>
|
<description>Generic ARM/Thumb v4 big endian (T-variant)</description>
|
||||||
<compiler name="default" spec="ARM_v45.cspec" id="default"/>
|
<compiler name="default" spec="ARM_v45.cspec" id="default"/>
|
||||||
<external_name tool="gnu" name="armv4t"/>
|
<external_name tool="gnu" name="armv4t"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="ARM.gdis"/>
|
||||||
<external_name tool="IDA-PRO" name="armb"/>
|
<external_name tool="IDA-PRO" name="armb"/>
|
||||||
<external_name tool="DWARF.register.mapping.file" name="ARM.dwarf"/>
|
<external_name tool="DWARF.register.mapping.file" name="ARM.dwarf"/>
|
||||||
</language>
|
</language>
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ data/languages/sha.sinc||GHIDRA||||END|
|
|||||||
data/languages/smx.sinc||GHIDRA||||END|
|
data/languages/smx.sinc||GHIDRA||||END|
|
||||||
data/languages/x86-16-real.pspec||GHIDRA||||END|
|
data/languages/x86-16-real.pspec||GHIDRA||||END|
|
||||||
data/languages/x86-16.cspec||GHIDRA||||END|
|
data/languages/x86-16.cspec||GHIDRA||||END|
|
||||||
|
data/languages/x86-16.gdis||GHIDRA||||END|
|
||||||
data/languages/x86-16.pspec||GHIDRA||||END|
|
data/languages/x86-16.pspec||GHIDRA||||END|
|
||||||
data/languages/x86-64-gcc.cspec||GHIDRA||||END|
|
data/languages/x86-64-gcc.cspec||GHIDRA||||END|
|
||||||
data/languages/x86-64-win.cspec||GHIDRA||||END|
|
data/languages/x86-64-win.cspec||GHIDRA||||END|
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<gdis>
|
||||||
|
<global optstring="intel"/>
|
||||||
|
</gdis>
|
||||||
@@ -59,6 +59,8 @@
|
|||||||
<external_name tool="IDA-PRO" name="80486r"/>
|
<external_name tool="IDA-PRO" name="80486r"/>
|
||||||
<external_name tool="IDA-PRO" name="80586r"/>
|
<external_name tool="IDA-PRO" name="80586r"/>
|
||||||
<external_name tool="IDA-PRO" name="metapc"/>
|
<external_name tool="IDA-PRO" name="metapc"/>
|
||||||
|
<external_name tool="gnu" name="i8086"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="x86-16.gdis"/>
|
||||||
</language>
|
</language>
|
||||||
<language processor="x86"
|
<language processor="x86"
|
||||||
endian="little"
|
endian="little"
|
||||||
@@ -72,6 +74,8 @@
|
|||||||
<description>Intel/AMD 16-bit x86 Protected Mode</description>
|
<description>Intel/AMD 16-bit x86 Protected Mode</description>
|
||||||
<compiler name="default" spec="x86-16.cspec" id="default"/>
|
<compiler name="default" spec="x86-16.cspec" id="default"/>
|
||||||
<external_name tool="IDA-PRO" name="8086p"/>
|
<external_name tool="IDA-PRO" name="8086p"/>
|
||||||
|
<external_name tool="gnu" name="i8086"/>
|
||||||
|
<external_name tool="gdis.disassembler.options.file" name="x86-16.gdis"/>
|
||||||
</language>
|
</language>
|
||||||
<language processor="x86"
|
<language processor="x86"
|
||||||
endian="little"
|
endian="little"
|
||||||
|
|||||||
Reference in New Issue
Block a user