2012-02-03 14 views
12

मैं एक उपकरण लिख रहा हूं जो libbfd और libopcodes x86-32 और x86-64 लिनक्स में डिस्सेप्लर करने के लिए उपयोग करता है। समस्या यह है कि जब तक मैं libopcodes को अलग करने में सक्षम हूं, मैं कोई निर्देश जानकारी प्राप्त करने में असमर्थ हूं। प्रदर्शन के प्रयोजनों के लिए, मैंने एक न्यूनतम उदाहरण बनाया है जो मेरी समस्या को दोहराता है। कार्यक्रम को प्रवेश बिंदु से पहले RET/RETQ पर स्वयं को अलग करना चाहिए।libopcodes से निर्देश जानकारी कैसे प्राप्त करें?

कोड ग्लोबल्स के साथ थोड़ा हैक किया गया है और त्रुटि जांच के लिए त्रुटि जांच को छोड़ दिया गया है, लेकिन इस मुद्दे को स्पष्ट रूप से स्पष्ट करना चाहिए।

#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; 
} 

यह स्रोत निम्नलिखित makefile के साथ बनाया जा सकता है।

all: 
    gcc -Wall disasm.c -o disasm -lbfd -lopcodes 

clean: 
    rm -f disasm 

जब चलाने के लिए, उत्पादन यह है:: एक सफल लिंक करने के लिए, binutils-dev पैकेज स्थानीय मशीन पर स्थापित किए जाने की आवश्यकता

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 

क्या मैं उम्मीद कर रहा हूँ पढ़ने के लिए सक्षम होने के लिए है dinfo->insn_type, target आदि के माध्यम से प्रत्येक निर्देश के लिए निर्देश जानकारी। व्यवहार x86-32 और x86-64 दोनों पर प्रदर्शित होता है। अगर मैं कम से कम पुष्टि कर सकता हूं कि यह इन दो आर्किटेक्चर पर लागू नहीं है तो मैं इस जानकारी को स्वयं भरने के बारे में जा सकता हूं।

+1

तुम सिर्फ यह आसान beaengine की तरह एक बहु मंच disassembler का उपयोग करें और छोड़ने के लिए मिल सकती है सभी सिरदर्द: http://www.beaengine.org/ – Necrolis

+0

दुर्भाग्यवश, इस परियोजना के लिए ये आवश्यकताएं हैं जिन पर मैं काम कर रहा हूं। कुछ दिलचस्प बात यह है कि opdis निर्देश जानकारी का उपयोग करता है, या कम से कम इसे एक बफर में प्रतिलिपि बनाता है, यह बताता है कि जानकारी सुलभ है। मुझे यह देखने में परेशानी हो रही है कि ओपिस क्या कर रहा है कि मैं नहीं हूं। –

+1

वैसे, आपके स्रोत कोड में कोई समस्या है: 'readlink' स्ट्रिंग में पिछली '\ 0' संलग्न नहीं करता है। –

उत्तर

9

दुर्भाग्य से, binutils libopcodes 2.22, insn_type के रूप में या तो I386 या x86_64 पर भरा नहीं है। केवल व्यापक रूप से समर्थित आर्किटेक्चर एमआईपीएस, स्पार्क, और सेल के एसपीयू हैं। यह अभी भी मौजूदा सीवीएस हेड के रूप में सच है।

यह साबित होता है कि कुछ मौजूद नहीं है कठिन है, लेकिन उदाहरण के लिए, the Sparc disassembler source में आप उदाहरण info->insn_type = dis_branch के लिए देख सकते हैं insn_type के कई घटनाओं स्थापित किया जा रहा, जबकि the i386 disassembler source में वहाँ insn_type का कोई घटनाओं और न ही मान यह होगा की किसी भी कर रहे हैं होने की उम्मीद है (dis_branch, dis_nonbranch आदि)।

कि समर्थन insn_type सभी libopcodes फ़ाइलों के लिए जाँच हो रही है आपको मिलता है:

  • opcodes/mips-dis.c
  • opcodes/spu-dis.c
  • opcodes/microblaze-dis.c
  • opcodes/cris-dis.c
  • opcodes/sparc-dis.c
  • opcodes/mmix-dis.c
+0

यह वही जवाब है जिसे मैं ढूंढ रहा था! हालांकि इस जानकारी के लिए कुछ उद्धरण या दस्तावेज़ीकरण है? –

+0

@ माइककवान: मैंने जवाब के रूप में अधिक जानकारी जोड़ दी है क्योंकि मैं इकट्ठा कर सकता हूं; समर्थित या नहीं के बारे में आधिकारिक दस्तावेज प्रतीत नहीं होता है। लेकिन 'dis-asm.h' शीर्षलेख स्पष्ट रूप से कहता है _ अभी तक सभी डिकोडर्स इस जानकारी का समर्थन नहीं करते हैं। –

+0

मेरे लिए यह देखने के लिए धन्यवाद। मैं i386-dis.c को भी देख रहा हूं, जो आप जो कह रहे हैं उसका समर्थन करता है। अब मुझे बक्षीस से सम्मानित किया गया है। –

3

केवल उन पुस्तकालयों के साथ ऐसा करना बेहद दर्दनाक और कठिन प्रक्रिया होगी। मैं सोचता हूं आपको नेक्रोलिस सुनना चाहिए और एक पुस्तकालय का उपयोग करना चाहिए जो पहले से ही ऐसा करता है। मैंने अतीत में Dyninst का उपयोग किया है (अर्थात्, निर्देशक + पारसेपी)। वे बहुत अच्छी तरह से प्रलेखित हैं, और ठीक करेंगे जो आप करने की कोशिश कर रहे हैं।कम से कम, इस पुस्तकालय के साथ एक घंटे बिताते हुए और मैनुअल में उनके उदाहरणों को संकलित करने से आपको एक ऐसा एप्लिकेशन मिलेगा जो आपको प्रत्येक निर्देश के ऑपकोड, प्रत्येक निर्देश की लंबाई, प्रत्येक निर्देश के लिए तर्कों की संख्या इत्यादि जैसी चीजों की जांच करने देगा। ये चीजें हैं जो libopcodes आपको नहीं बताती हैं और न ही संभालती हैं (यह एक समय में पते को डीकोड करता है, जिन्हें निर्देशों की गारंटी नहीं है)।

यहाँ Opdis के डेवलपर्स है कि मैं से ले लिया से एक टुकड़ा है उनके manual (जो मैं पढ़ सुझाव है कि अगर आप नहीं, वहाँ में अच्छी चीजें के बहुत सारे के बारे में libopcodes है):

libopcodes पुस्तकालय है

  1. यह है के तहत दस्तावेज, यह मुश्किल नए उपयोगकर्ताओं को समझने के लिए
  2. अपनी सुविधा सेट के लिए सीमित है: एक बहुत उपयोगी disassembler है, लेकिन यह तीन कमियों है
  3. यह एक धारा

अन्य बातों के अलावा करने के लिए मुख्य रूप से disassembled मुद्रित करने के लिए दिए गए निर्देशों का बनाया गया है एक ही पते के disassembly के लिए, मैं तुम्हें उस सूची में दूसरे मद से डंक मार रही हो सकता है लगता है। अर्थात्, तथ्य यह है कि अधिकांश (सभी?) ऑपोड्स एक ही पते में फिट होंगे और मनाए गए आउटपुट से सहमत होंगे (उदाहरण के लिए, आपको mov और pop और कुछ पंजीकरण तर्क मिल रहे हैं)। लेकिन परिवर्तनीय लंबाई निर्देशों या निर्देशों जैसी मुश्किल चीजों के बारे में क्या है जो 4-बाइट सीमाओं पर बिल्कुल ठीक नहीं हैं? आप उनको संभालने के लिए कुछ भी नहीं कर रहे हैं।

लिबोपोड्स द्वारा उत्पन्न डिस्सेप्लर स्ट्रिंग्स का एक अनुक्रम है जो स्ट्रीम को लिखने के लिए है। कोई मेटाडाटा नहीं है, इसलिए स्ट्रिंग्स को यह निर्धारित करने के लिए जांच की जानी चाहिए कि कौन से निमोनिक्स हैं और ऑपरेंड हैं, और इनमें से कौन सा शाखा/कूद/वापसी निर्देश और उनके लक्ष्य क्या हैं।

मुझे लगता है कि ओपिसिस आपके प्रोग्राम से ज्यादा चालाक है - यह जानता है कि स्ट्रीम में कैसे और क्या देखना है। शायद कभी-कभी यह जानता है कि इसे अलग करने से पहले सिर्फ एक के बजाय दो पते पढ़ने की जरूरत है। आपके कोड से, और libopcodes का विवरण, न ही यह कर रहा है।

शुभकामनाएं! उस मैनुअल को पढ़ना याद रखें, और शायद इसके बजाय libopdis का उपयोग करने पर विचार करें!

+0

मैं मानता हूं कि 'libopcodes' का उपयोग करने के लिए एक दर्द है। इसका मुख्य कारण यह है कि मैं इसका उपयोग कर रहा हूं क्योंकि बीएफडी अमूर्तता के शीर्ष पर काम करने की आवश्यकता है। वास्तव में, लक्ष्य का पृथक्करण परियोजना का केवल एक छोटा सा हिस्सा है। अंतिम दृष्टि मनमाने ढंग से निष्पादन योग्य संपादन प्रदान करने में सक्षम होना है। बीएफडी अतिरिक्त कोड के उपकरण के लिए सुविधाजनक सुविधाएं प्रदान करता है। –

+0

उन मुद्दों के संबंध में जिन्हें आप 'लिबोपोड्स' के साथ वर्णित करते हैं, वे निश्चित रूप से वास्तविक चिंताओं हैं लेकिन सभी को चारों ओर काम किया जा सकता है। विशेष रूप से, यह देखकर निर्देश की लंबाई बताना संभव है कि कितने बाइट अलग किए गए हैं। स्वाभाविक रूप से, एक शर्त यह है कि आप एक निर्देश सीमा से शुरू कर रहे हैं। मैं लक्ष्य के प्रवेश बिंदु से पृथक्करण के नियंत्रण प्रवाह विश्लेषण शुरू करके यह सुनिश्चित करता हूं। –

+0

'ओपिसिस' द्वारा सूचीबद्ध समस्याएं भी वैध चिंताओं हैं और इन्हें निम्नानुसार काम किया जा सकता है। 1) हाँ ... यह एक समस्या है और यह सवाल यह दर्शाता है: पी 2) नियंत्रण प्रवाह विश्लेषण दृष्टिकोण के साथ, मुझे इस बारे में चिंता करने की आवश्यकता नहीं है, हम केवल अगले निर्देश को अलग करते हैं और शाखा को उचित रूप से रोकते हैं जेएमपीएस/कॉल्स/रीट्स इत्यादि। 3) प्रिंट स्ट्रीम को रीडायरेक्ट से रीडायरेक्ट करने के लिए एक कस्टम फ़ंक्शन (जिसे ओपडीस स्वयं करता है) से रीडायरेक्ट करना और ओवरराइड करना संभव है। –

0

लिबोपोड्स स्ट्रीम में अलग-अलग निर्देशों को मुद्रित करता है जो आपके custom_printf फ़ंक्शन द्वारा अवरुद्ध किया जाता है। आपकी गलती यह है कि आप मानते हैं कि कस्टम_प्रिंटफ को प्रत्येक बार एक ही निर्देश को अलग करने के लिए बुलाया जाता है, हालांकि, इसे प्रत्येक निमोनिक, ऑपरेंड, पता या विभाजक मुद्रित करने के लिए, अक्सर, अक्सर कहा जाता है।

तो, अपने द्विआधारी के परिणामस्वरूप disassembly है

xor %ebp, %ebp 

mov %rdx, %r9 

pop %rsi 

mov %rsp, %rdx 

and $0xfffffffffffffff0, %rsp 

push %rax 

push %rsp 

mov $0x401450,%r8 

... 
+0

हाय अलेक्जेंड्रा। उत्तर के लिए धन्यवाद। मुझे लगता है कि आपने हालांकि मेरे प्रश्न को गलत समझा। मुझे पता है कि libopcodes कैसे काम करता है। चीयर्स! –

संबंधित मुद्दे