Sto scrivendo uno strumento che utilizza libbfd
e libopcodes
in x86-32 e x86-64 Linux per eseguire il disassemblaggio. Il problema è che mentre sono in grado di far smontare i libopcode, non sono in grado di ottenere alcuna informazione di istruzioni. Ai fini della dimostrazione, ho fatto un esempio minimale che riproduce il mio problema. Il programma dovrebbe smontarsi dal punto di ingresso al primo RET
/RETQ
.Come ottenere informazioni di istruzioni da libopcodes?
Il codice è un po 'compromesso con i globali e il controllo degli errori è stato omesso per brevità, ecc., Ma dovrebbe illustrare chiaramente il problema.
#include <bfd.h>
#include <dis-asm.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#include <libiberty.h>
/*
* Holds state for BFD and libopcodes.
*/
bfd * abfd = NULL;
disassemble_info dinfo = {0};
/*
* Temporary hack to signal when disassembling should stop.
*/
static bool stop_disassembling = FALSE;
/*
* Gets path to currently running executable.
*/
bool get_target_path(char * target_path, size_t size)
{
char * path;
ssize_t len;
pid_t pid = getpid();
sprintf(target_path, "/proc/%d/exe", (int)pid);
path = strdup(target_path);
len = readlink(path, target_path, size);
target_path[len] = '\0';
free(path);
return TRUE;
}
/*
* libopcodes appends spaces on the end of some instructions so for
* comparisons, we want to strip those first.
*/
void strip_tail(char * str, unsigned int size)
{
int i;
for(i = 0; i < size; i++) {
if(!isgraph(str[i])) {
str[i] = '\0';
break;
}
}
}
/*
* Checks whether the current instruction will cause the control flow to not
* proceed to the linearly subsequent instruction (e.g. ret, jmp, etc.)
*/
bool breaks_control_flow(char * str)
{
if(abfd->arch_info->bits_per_address == 64) {
if(strcmp(str, "retq") == 0) {
return TRUE;
}
} else {
if(strcmp(str, "ret") == 0) {
return TRUE;
}
}
return FALSE;
}
/*
* Used as a callback for libopcodes so we can do something useful with the
* disassembly. Currently this just outputs to stdout.
*/
int custom_fprintf(void * stream, const char * format, ...)
{
/* silly amount */
char str[128] = {0};
int rv;
va_list args;
va_start(args, format);
rv = vsnprintf(str, ARRAY_SIZE(str) - 1, format, args);
va_end(args);
puts(str);
strip_tail(str, ARRAY_SIZE(str));
if(breaks_control_flow(str)) {
puts("Stopped disassembly");
stop_disassembling = TRUE;
}
if(dinfo.insn_info_valid) {
switch(dinfo.insn_type) {
case dis_noninsn:
printf("not an instruction\n");
break;
case dis_nonbranch:
printf("not a branch\n");
break;
case dis_branch:
printf("is a branch\n");
break;
case dis_condbranch:
printf("is a conditional branch\n");
break;
case dis_jsr:
printf("jump to subroutine\n");
break;
case dis_condjsr:
printf("conditional jump to subroutine\n");
break;
case dis_dref:
printf("data reference in instruction\n");
break;
case dis_dref2:
printf("two data references in instruction\n");
break;
default:
printf("not enumerated\n");
break;
}
} else {
printf("insn_info not valid\n");
}
return rv;
}
/*
* Initialises libopcodes disassembler and returns an instance of it.
*/
disassembler_ftype init_disasm(bfd * abfd, disassemble_info * dinfo)
{
/* Override the stream the disassembler outputs to */
init_disassemble_info(dinfo, NULL, custom_fprintf);
dinfo->flavour = bfd_get_flavour(abfd);
dinfo->arch = bfd_get_arch(abfd);
dinfo->mach = bfd_get_mach(abfd);
dinfo->endian = abfd->xvec->byteorder;
disassemble_init_for_target(dinfo);
return disassembler(abfd);
}
/*
* Method of locating section from VMA taken from opdis.
*/
typedef struct {
bfd_vma vma;
asection * sec;
} BFD_VMA_SECTION;
/*
* Loads section and fills in dinfo accordingly. Since this function allocates
* memory in dinfo->buffer, callers need to call free once they are finished.
*/
bool load_section(bfd * abfd, disassemble_info * dinfo, asection * s)
{
int size = bfd_section_size(s->owner, s);
unsigned char * buf = xmalloc(size);
if(!bfd_get_section_contents(s->owner, s, buf, 0, size)) {
free(buf);
return FALSE;
}
dinfo->section = s;
dinfo->buffer = buf;
dinfo->buffer_length = size;
dinfo->buffer_vma = bfd_section_vma(s->owner, s);
printf("Allocated %d bytes for %s section\n: 0x%lX", size, s->name,
dinfo->buffer_vma);
return TRUE;
}
/*
* Used to locate section for a vma.
*/
void vma_in_section(bfd * abfd, asection * s, void * data)
{
BFD_VMA_SECTION * req = data;
if(req && req->vma >= s->vma &&
req->vma < (s->vma + bfd_section_size(abfd, s))) {
req->sec = s;
}
}
/*
* Locate and load section containing vma.
*/
bool load_section_for_vma(bfd * abfd, disassemble_info * dinfo,
bfd_vma vma)
{
BFD_VMA_SECTION req = {vma, NULL};
bfd_map_over_sections(abfd, vma_in_section, &req);
if(!req.sec) {
return FALSE;
} else {
return load_section(abfd, dinfo, req.sec);
}
}
/*
* Start disassembling from entry point.
*/
bool disassemble_entry(bfd * abfd, disassemble_info * dinfo,
disassembler_ftype disassembler)
{
bfd_vma vma = bfd_get_start_address(abfd);
/* First locate and load the section containing the vma */
if(load_section_for_vma(abfd, dinfo, vma)) {
int size;
/* Keep disassembling until signalled otherwise or error */
while(true) {
dinfo->insn_info_valid = 0;
size = disassembler(vma, dinfo);
printf("Disassembled %d bytes at 0x%lX\n", size, vma);
if(size == 0 || size == -1 || stop_disassembling) {
break;
}
vma += size;
}
free(dinfo->buffer);
return TRUE;
}
return FALSE;
}
int main(void)
{
char target_path[PATH_MAX] = {0};
bfd_init();
/* Get path for the running instance of this program */
get_target_path(target_path, ARRAY_SIZE(target_path));
abfd = bfd_openr(target_path, NULL);
if(abfd != NULL && bfd_check_format(abfd, bfd_object)) {
disassembler_ftype disassembler = init_disasm(abfd, &dinfo);
disassemble_entry(abfd, &dinfo, disassembler);
bfd_close(abfd);
}
return EXIT_SUCCESS;
}
Questa fonte può essere creata con il seguente makefile
. Per eseguire un collegamento con successo, il pacchetto binutils-dev
deve essere installato sulla macchina locale:
all:
gcc -Wall disasm.c -o disasm -lbfd -lopcodes
clean:
rm -f disasm
Quando eseguito, l'output è questo:
Allocated 2216 bytes for .text section
: 0x400BF0xor
insn_info not valid
%ebp
insn_info not valid
,
insn_info not valid
%ebp
insn_info not valid
Disassembled 2 bytes at 0x400BF0
mov
insn_info not valid
%rdx
insn_info not valid
,
insn_info not valid
%r9
insn_info not valid
Disassembled 3 bytes at 0x400BF2
pop
insn_info not valid
%rsi
insn_info not valid
Disassembled 1 bytes at 0x400BF5
mov
insn_info not valid
%rsp
insn_info not valid
,
insn_info not valid
%rdx
insn_info not valid
Disassembled 3 bytes at 0x400BF6
and
insn_info not valid
$0xfffffffffffffff0
insn_info not valid
,
insn_info not valid
%rsp
insn_info not valid
Disassembled 4 bytes at 0x400BF9
push
insn_info not valid
%rax
insn_info not valid
Disassembled 1 bytes at 0x400BFD
push
insn_info not valid
%rsp
insn_info not valid
Disassembled 1 bytes at 0x400BFE
mov
insn_info not valid
$0x401450
insn_info not valid
,
insn_info not valid
%r8
insn_info not valid
Disassembled 7 bytes at 0x400BFF
mov
insn_info not valid
$0x4013c0
insn_info not valid
,
insn_info not valid
%rcx
insn_info not valid
Disassembled 7 bytes at 0x400C06
mov
insn_info not valid
$0x4012ce
insn_info not valid
,
insn_info not valid
%rdi
insn_info not valid
Disassembled 7 bytes at 0x400C0D
callq
insn_info not valid
0x0000000000400ad8
insn_info not valid
Disassembled 5 bytes at 0x400C14
hlt
insn_info not valid
Disassembled 1 bytes at 0x400C19
nop
insn_info not valid
Disassembled 1 bytes at 0x400C1A
nop
insn_info not valid
Disassembled 1 bytes at 0x400C1B
sub
insn_info not valid
$0x8
insn_info not valid
,
insn_info not valid
%rsp
insn_info not valid
Disassembled 4 bytes at 0x400C1C
mov
insn_info not valid
0x2013b9(%rip)
insn_info not valid
,
insn_info not valid
%rax
insn_info not valid
#
insn_info not valid
0x0000000000601fe0
insn_info not valid
Disassembled 7 bytes at 0x400C20
test
insn_info not valid
%rax
insn_info not valid
,
insn_info not valid
%rax
insn_info not valid
Disassembled 3 bytes at 0x400C27
je
insn_info not valid
0x0000000000400c2e
insn_info not valid
Disassembled 2 bytes at 0x400C2A
callq
insn_info not valid
*%rax
insn_info not valid
Disassembled 2 bytes at 0x400C2C
add
insn_info not valid
$0x8
insn_info not valid
,
insn_info not valid
%rsp
insn_info not valid
Disassembled 4 bytes at 0x400C2E
retq
Stopped disassembly
insn_info not valid
Disassembled 1 bytes at 0x400C32
Quello che mi aspetto è quello di essere in grado di leggere informazioni di istruzioni per ogni istruzione tramite dinfo->insn_type
, target
, ecc. Il comportamento è esposto sia su x86-32 che su x86-64. Se posso almeno avere conferma che questo non è implementato su queste due architetture, allora posso andare a compilare queste informazioni da solo.
si potrebbe trovare solo più facile da usare un disassembler multi-piattaforma come beaengine e saltare tutto il mal di testa: http://www.beaengine.org/ – Necrolis
Sfortunatamente, questi sono i requisiti per il progetto su cui sto lavorando. Qualcosa di interessante è che opdis usa le informazioni dell'istruzione, o almeno lo copia in un buffer, suggerendo che l'informazione è accessibile. Ho difficoltà a vedere che cosa sta facendo opdis che io non sono però. –
a proposito, c'è un problema con il tuo codice sorgente: 'readlink' non aggiunge un trailing' \ 0' alla stringa. –