Search code examples
c++macosassemblyx86-64

MacOS LLDB | Thread 1: EXC_BAD_ACCESS (code=1 & 2, address=0x123456789)


So to start, I am not too knowledageble in C++, I know very little from a class I took a while back. Almost everything I have so far is from this very generous and knowledgable community.

I have to use a wack piece of custom software for work on MacOS and it doesn't have any form autosave and it occasionally likes to crash. Though I regularly save, its annoying loosing any amount of work because it crashes.

What I am trying to do is use the save function address to save and repeat every 5 or so minutes. So far I have the process id, the runtime base memory address for the process, the offset of the save function address(I have subtracted the disassembler base address from the address I found with debugging and disassembling), and the arguments it requires. When I try to run the save function I get the lldb error Thread 1: EXC_BAD_ACCESS (code=1, address=0x10a106ecf) and sometimes it changes to Thread 1: EXC_BAD_ACCESS (code=2, address=0x10a106ecf). I have found other questions about this error on this website, but it either doesn't apply to my situation, or due to my limited knowledge I just don't understand how to solve it for my case. If anyone could explain in detail what I am doing wrong in detail along with a solution, it would be much appreciated because I would like to get an understanding and learn.

The project is a Command Line Utility in Xcode. I'm not sure if it would need to be a .dylib and added into the process, but if it could be an external command line utility, that would be more optimal for me.

Here is the problem code:

mach_vm_address_t runtimeBase = 0; // Base process memory address. Set in the getProc() function
mach_vm_address_t funcBase = 0x973ECF; // Function Address from disassembling. (0x100973ECF - 0x100000000 = 0x973ECF)

int main()
{
    if (!getProc()) throw std::logic_error("Failed to get Process.");
    
    typedef mach_vm_address_t(__cdecl* autoSave)(int saveType, const char* saveMessage);
    autoSave save = (autoSave)(runtimeBase + funcBase);
    for(;;)
    {
        save(0, "AutoSave");//    <-- Error Here: Thread 1: EXC_BAD_ACCESS (code=1/2, address=0x10a106ecf)
        //sleep(5); //add sleep after I get save to work.
    }
}

/*
 saveType:
    0 = Save local.
    1 = Save server.
 
 saveMessage: Message for history/save log.
 */

More detail I get from code=1 error is:

error: memory read failed for 0x10a106e00  <-- Thread 1: EXC_BAD_ACCESS (code=1, address=0x10a106ecf)

And for code=2 is:

0x10a106ecf: addb   %al, (%rax)            <-- Thread 1: EXC_BAD_ACCESS (code=2, address=0x10a106ecf)
0x10a106ed1: addb   %al, (%rax)
0x10a106ed3: addb   %al, (%rax)
0x10a106ed5: addb   %al, (%rax)
0x10a106ed7: addb   %al, (%rax)
0x10a106ed9: addb   %al, (%rax)
0x10a106edb: addb   %al, (%rax)
0x10a106edd: addb   %al, (%rax)
0x10a106edf: addb   %al, (%rax)
0x10a106ee1: addb   %al, (%rax)
0x10a106ee3: addb   %al, (%rax)
0x10a106ee5: addb   %al, (%rax)
0x10a106ee7: addb   %al, (%rax)
0x10a106ee9: addb   %al, (%rax)
0x10a106eeb: addb   %al, (%rax)
0x10a106eed: addb   %al, (%rax)

Solution

  • With assembly explanations from @Peter Cordes I managed to solve my issue.

    @Peter Cordes: addb %al, (%rax) is how 00 00 decodes. Crashing there indicates that execution jumped to some memory that's all zeros, perhaps due to overwriting a return address or function pointer. Or if you have any hand-written asm, due to getting something wrong with the stack.

    @Peter Cordes: The EXC_BAD_ACCESS (code=2, address=0x10a106ecf) is from trying to deref whatever garbage is in RAX, for read+write. The 00 00 garbage machine code decodes as instructions that try to access that "bad pointer". I haven't used LLDB much, or MacOS for development at all, but I'm guessing code=1 vs 2 might be read vs. write. (And I haven't really looked at the details of your question, just wanted to help out by explaining that seeing execution in a whole block of addb %al, (%rax) instructions means you jumped to a region full of zeros; any mem access it attempts is already bogus.)


    So, as @Peter Cordes said addb %al, (%rax) is how 00 00 decodes. Crashing there indicates that execution jumped to some memory that's all zeros.

    From this we can infer that when Thread 1: EXC_BAD_ACCESS (code=2, address=0x10a106ecf) happens it is because we jumped to a place in memory that is all zeros. But why are we jumping to a place in memory with all zeros? I am thinking it's because of how MacOS handles memory. Each process has its own memory space, and I was trying to jump to the address 0x10a106e00 in MY OWN process' memory, which does not exist. There are a multitude of resources explain how process memory works on modern OS's, just do a quick google search if you want to learn more.

    So what we know now is:

    • Thread 1: EXC_BAD_ACCESS (code=1, address=0x10a106ecf) happens because my program fails to read the memory for the specified address.
    • Thread 1: EXC_BAD_ACCESS (code=2, address=0x10a106ecf) happens because we are jumping an invalid place in memory, and for the wrong process.

    There was also some issues with the code, I was using code better suited for Windows, To create a function based on the memory address I did,

    typedef mach_vm_address_t(__cdecl* autoSave)(int saveType, const char* saveMessage);
        autoSave save = (autoSave)(runtimeBase + funcBase);
    

    And this is what I'm thinking a better/correct way of creating a function based on the memory address is:

    int (*autoSave)(int saveType, const char* saveMessage) = (int (*)(int , const char *))(runtimeBase + funcBase);
    

    The last thing I did was create a Dylib instead of a Command Line Utility. I did this because it is MUCH easier to access the target process' memory. Though what I am trying to achieve is probably possible through a Command Line Utility(CLU) and attaching to the target process as if the CLU was a debugger or some other method, for me it was much simpler and easier to just create a dylib.

    If anyone reading this has any questions about my explanation, or want more detailed explanation/example of my solution. Feel free to send me a message or comment.

    I once again want to thank @Peter Cordes for his explanations. They helped even though they said `I haven't used LLDB much, or MacOS for development at all'. The explanations were still extremely helpful and were explained in an easy to understand way.