4

Basically here's what I want to know, if you have a disassembled instruction say:

jz 0x8048e1a

And, you also have the source code. How do you go about figuring out what this instruction relates to in the source code ? At the very least, would it be possible to figure out the basic block that this instruction comes from ?

Basically I have an application that spits out a disassembled instruction for an ELF binary that I have compiled myself (source available), and now I have to find out what/where this disassembled instruction maps to in the source code.

Let's also assume that I do not use any of the optimization flags while compiling.

I should probably mention that I am using Intel's PIN as the application that spits out these disassembled instructions. So, I know which function/routine these instructions belong to. But, I just want to be a bit more precise at the assembly level.

Thanks for any help/hints !

5
  • My old question might relate to yours somehow, not saying it's a duplicate, just a heads up : reverseengineering.stackexchange.com/questions/3166/… Commented Jul 2, 2014 at 15:21
  • Is the instruction address available as well? This is not obvious from the example above. Commented Jul 2, 2014 at 15:48
  • @DmitryYanushkevich Yes, all the addresses are available.
    – Faheem
    Commented Jul 2, 2014 at 15:50
  • Have you checked this yet? Commented Jul 2, 2014 at 15:57
  • May I suggest you a very helpful training website with plenty of small examples binary-auditing.com : With a very slowly increasing difficulty, you'll manage to extract various HLL (High Level Language) patterns from ASM. It starts with variable assignations, goes through loops, conditions, objects, structures, and so on.
    – Hackndo
    Commented Aug 28, 2015 at 8:19

6 Answers 6

4

You have to read and interpret the assembly. There is no substitute for reading the assembly. If you know what the assembly instructions mean, and you have the source code the relationship between the two should be quite clear. There will be no direct marker that shall tell what line of the source the assembly came from.

So learn how your basic structures in C/C++ look in ASM.

What does a loop look like? (Hint: There are several ways to write loops in ASM)

What does a struct look like?

What does a function look like?

What does a pointer look like?

If you learn ASM well, this will all be clear.

Also, learn how compilers work, and learn to debug running code.

Edit: You can actually generate debugger symmbols to relate code to individual line numbers, so what I said above is not exactly true.

2
  • "There will be no direct marker that shall tell what line of the source the assembly came from." -- That's not quite true given that he can generate symbols from his source code. Commented Jul 2, 2014 at 15:34
  • He could. I was assuming that just wanted to work with the assembly without giving himself hints. I edited the post to reflect the truth. Commented Jul 2, 2014 at 15:41
4

EDIT: The answer below is specific to PE files, which doesn't answer the question above now that @Achilles specified that his question is in regard to ELF files.

Given that you have the source code and can produce symbols for your program, you can use the Debug Interface Access SDK to map assembly code instruction addresses to source code line numbers. In particular, you would want to use the IDiaLineNumber class.

The following function displays line numbers used in a function (represented by pSymbol).

void dumpFunctionLines( IDiaSymbol* pSymbol, IDiaSession* pSession ) {
ULONGLONG length = 0;
DWORD     isect  = 0;
DWORD     offset = 0;

pSymbol->get_addressSection( &isect );
pSymbol->get_addressOffset( &offset );
pSymbol->get_length( &length );
if ( isect != 0 && length > 0 )
{
    CComPtr< IDiaEnumLineNumbers > pLines;
    if ( SUCCEEDED( pSession->findLinesByAddr(
                                  isect,
                                  offset,
                                  static_cast<DWORD>( length ),
                                  &pLines)
                  )
       )
    {
        CComPtr< IDiaLineNumber > pLine;
        DWORD celt      = 0;
        bool  firstLine = true;

        while ( SUCCEEDED( pLines->Next( 1, &pLine, &celt ) ) &&
                celt == 1 )
        {
            DWORD offset;
            DWORD seg;
            DWORD linenum;
            CComPtr< IDiaSymbol >     pComp;
            CComPtr< IDiaSourceFile > pSrc;

            pLine->get_compiland( &pComp );
            pLine->get_sourceFile( &pSrc );
            pLine->get_addressSection( &seg );
            pLine->get_addressOffset( &offset );
            pLine->get_lineNumber( &linenum );
            printf( "\tline %d at 0x%x:0x%x\n", linenum, seg, offset );
            pLine = NULL;
            if ( firstLine )
            {
                // sanity check
                CComPtr< IDiaEnumLineNumbers > pLinesByLineNum;
                if ( SUCCEEDED( pSession->findLinesByLinenum(
                                              pComp,
                                              pSrc,
                                              linenum,
                                              0,
                                              &pLinesByLineNum)
                              )
                   )
                {
                    CComPtr< IDiaLineNumber > pLine;
                    DWORD celt;
                    while ( SUCCEEDED( pLinesByLineNum->Next( 1, &pLine, &celt ) ) &&
                            celt == 1 )
                    {
                        DWORD offset;
                        DWORD seg;
                        DWORD linenum;

                        pLine->get_addressSection( &seg );
                        pLine->get_addressOffset( &offset );
                        pLine->get_lineNumber( &linenum );
                        printf( "\t\tfound line %d at 0x%x:0x%x\n", linenum, seg, offset );
                        pLine = NULL;
                   }
                }
                firstLine = false;
            }
        }
    }
} }
3
  • Sorry, I forgot to mention, I need to do this on Linux.
    – Faheem
    Commented Jul 2, 2014 at 15:35
  • This is not completely true and depends on whether one can reproduce the build perfectly. In case the binary was PGO-optimised, I don't think there is a chance for that. Not to mention having the same version of compiler and build environment. Commented Jul 2, 2014 at 15:36
  • I assumed he was looking at the build he himself created. I also assumed this was on Windows :) Commented Jul 2, 2014 at 16:13
2

how do you go about figuring out what this instruction relates to in the source code?

Mostly intuition and experience. Given the high-level overview from the source code, one could spot the function and possibly the line where the instruction belongs, but so far I know no program that could do the same. Having debug info certainly helps a lot, but is not a panacea, and in general, not always available.

At the very least, would it be possible to figure out the basic block that this instruction comes from?

In general, no. In certain cases, yes. Modern compilers tend to optimize code too aggressively. Even having all the debug info the compiler produces is sometimes not enough to map the precise location of the given insn, which can be demonstrated by trying to do source-level debugging on such an application.

EDIT: Didn't properly notice you build the binary by yourself. Then yes, it should be possible to at least map the instruction to a function given the debug info.

2

Which compiler are you using? My background is more in Windows development (as opposed to Linux/BSD/OSX), so I can only speak to that case.

But in Visual Studio, you can see the generated assembly while debugging C/C++. See this MSDN article for detail.

If you are only interested in the ASM, you may want to change some of the compilation settings within your Visual Studio project so that it reduces debug information included with the project. Detail on how to do that is here.

Primarily involves:

  • Disabling C++ exceptions
  • Debug information format set to Program Database (/Zi)
  • Turning off buffer security check (/GS-)
  • Turn off incremental linking (/NCREMENTAL:NO)

enter image description here

Embarcadero C++ Builder and Delphi (formerly Borland) offer the same functionality as well. Usage detail here. enter image description here

2

If you compiled it yourself, and can compile it with dwarf information, then you can use addr2line from gnu binutils.

If you have less information, I just try to get the lay of the land, often using references to arguments or function calls as major landmarks. Obviously, that's not applicable to every binary, though.

1

If you have the full source available. Here's what I propose (and currently do):

  1. Compile the binary with debugging information and disable optimisations. Also make sure to disable stripping the binary if you are compiling some distributed code. This would mean: Compile with the following flags with gcc(depending on the level of debugging information you need, I usually compile with ggdb/ggdb3):

    -g -O0 OR -ggdb -O0 OR -ggdb3 -O0 
    
  2. Next, create a dump of the binary with objdump:

    objdump -dS myBinary > dump
    
  3. Now you can just get the instruction address, search for it in the dump and match it to source code.

1
  • 1
    Awesome! Thanks! This solved my problem perfectly and easily. Commented Nov 25, 2018 at 0:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.