Binary bomb phase 3 issue - c

I am trying to complete a binary bomb exercise, and I have made it through the first 2 phases but I am stuck on how to even being solving it, as all the guides I have found feature a different assembly code and none of the same variables. I'm not asking for someone to solve the problem for me or anything, just how to go about actually finding the answer (i.e. what commands I would need to try or variables to focus on).
I've tried finding variables that correspond to hexadecimal letters when using x/s ___ but I've had no luck getting it correspond to lowercase letters. I've tried on my own for 3 hours to figure out this phase and I've had no luck. Here is the assembly language for phase 3:
08048c17 <phase_3>:
8048c17: 55 push %ebp
8048c18: 89 e5 mov %esp,%ebp
8048c1a: 83 ec 28 sub $0x28,%esp
8048c1d: 8d 45 f0 lea -0x10(%ebp),%eax
8048c20: 89 44 24 0c mov %eax,0xc(%esp)
8048c24: 8d 45 f4 lea -0xc(%ebp),%eax
8048c27: 89 44 24 08 mov %eax,0x8(%esp)
8048c2b: c7 44 24 04 7e 94 04 movl $0x804947e,0x4(%esp)
8048c32: 08
8048c33: 8b 45 08 mov 0x8(%ebp),%eax
8048c36: 89 04 24 mov %eax,(%esp)
8048c39: e8 a2 f9 ff ff call 80485e0 <__isoc99_sscanf#plt>
8048c3e: 83 f8 01 cmp $0x1,%eax
8048c41: 7f 05 jg 8048c48 <phase_3+0x31>
8048c43: e8 09 02 00 00 call 8048e51 <explode_bomb>
8048c48: 83 7d f4 07 cmpl $0x7,-0xc(%ebp)
8048c4c: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
8048c50: 77 6a ja 8048cbc <phase_3+0xa5>
8048c52: 8b 45 f4 mov -0xc(%ebp),%eax
8048c55: ff 24 85 e0 93 04 08 jmp *0x80493e0(,%eax,4)
8048c5c: b8 00 00 00 00 mov $0x0,%eax
8048c61: eb 52 jmp 8048cb5 <phase_3+0x9e>
8048c63: b8 00 00 00 00 mov $0x0,%eax
8048c68: eb 46 jmp 8048cb0 <phase_3+0x99>
8048c6a: b8 00 00 00 00 mov $0x0,%eax
8048c6f: 90 nop
8048c70: eb 39 jmp 8048cab <phase_3+0x94>
8048c72: b8 00 00 00 00 mov $0x0,%eax
8048c77: eb 2d jmp 8048ca6 <phase_3+0x8f>
8048c79: b8 00 00 00 00 mov $0x0,%eax
8048c7e: 66 90 xchg %ax,%ax
8048c80: eb 1f jmp 8048ca1 <phase_3+0x8a>
8048c82: b8 00 00 00 00 mov $0x0,%eax
8048c87: eb 13 jmp 8048c9c <phase_3+0x85>
8048c89: b8 6d 03 00 00 mov $0x36d,%eax
8048c8e: 66 90 xchg %ax,%ax
8048c90: eb 05 jmp 8048c97 <phase_3+0x80>
8048c92: b8 00 00 00 00 mov $0x0,%eax
8048c97: 2d c5 02 00 00 sub $0x2c5,%eax
8048c9c: 05 94 03 00 00 add $0x394,%eax
8048ca1: 2d e2 00 00 00 sub $0xe2,%eax
8048ca6: 05 e2 00 00 00 add $0xe2,%eax
8048cab: 2d e2 00 00 00 sub $0xe2,%eax
8048cb0: 05 e2 00 00 00 add $0xe2,%eax
8048cb5: 2d e2 00 00 00 sub $0xe2,%eax
8048cba: eb 0a jmp 8048cc6 <phase_3+0xaf>
8048cbc: e8 90 01 00 00 call 8048e51 <explode_bomb>
8048cc1: b8 00 00 00 00 mov $0x0,%eax
8048cc6: 83 7d f4 05 cmpl $0x5,-0xc(%ebp)
8048cca: 7f 06 jg 8048cd2 <phase_3+0xbb>
8048ccc: 3b 45 f0 cmp -0x10(%ebp),%eax
8048ccf: 90 nop
8048cd0: 74 05 je 8048cd7 <phase_3+0xc0>
8048cd2: e8 7a 01 00 00 call 8048e51 <explode_bomb>
8048cd7: c9 leave
8048cd8: c3 ret
Here is the guide I have been provided for phase 3, it seems very valuable but I am having trouble understanding how to apply it to my code given the different variables/values. Here are screen caps of what I think are the most relevant parts, and the full link to the guide below those screen caps: Introductory guide
Guide part 1
Guide part 2
Here is a full link to the guide
The goal is to have a number followed by a lowercase letter and then followed by a number (like 1 v 240 or 4 b 60). But I haven't been able to find any numbers or a letter.

Turns out I was mistaken on the method, and the problem was actually looking for a pair of numbers without a letter. For those wondering, I found the solution by following the scanf assembly sections like was suggested by De Dycker, and ended up charting the first number followed by finding the second pair through trial and error.

Related

How to prevent gcc from reordering x86 frame pointer saving/setup instructions?

During my profiling with flamegraph, I found that callstacks are sometimes broken even when all codebases are compiled with the -fno-omit-frame-pointer flag. By checking the binary generated by gcc, I noticed gcc may reorder x86 frame pointer saving/setup instructions (i.e., push %rbp; move %rsp, %rbp), sometimes even after ret instructions of some branches. As shown in the example below, push %rbp; move %rsp, %rbp are put at the bottom of the function. It leads to incomplete and misleading callstacks when perf happens to sample instructions in the function before frame pointers are properly set.
C code:
int flextcp_fd_slookup(int fd, struct socket **ps)
{
struct socket *s;
if (fd >= MAXSOCK || fhs[fd].type != FH_SOCKET) {
errno = EBADF;
return -1;
}
uint32_t lock_val = 1;
s = fhs[fd].data.s;
asm volatile (
"1:\n"
"xchg %[locked], %[lv]\n"
"test %[lv], %[lv]\n"
"jz 3f\n"
"2:\n"
"pause\n"
"cmpl $0, %[locked]\n"
"jnz 2b\n"
"jmp 1b\n"
"3:\n"
: [locked] "=m" (s->sp_lock), [lv] "=q" (lock_val)
: "[lv]" (lock_val)
: "memory");
*ps = s;
return 0;
}
CMake Debug Profile:
0000000000007c73 <flextcp_fd_slookup>:
7c73: f3 0f 1e fa endbr64
7c77: 55 push %rbp
7c78: 48 89 e5 mov %rsp,%rbp
7c7b: 48 83 ec 20 sub $0x20,%rsp
7c7f: 89 7d ec mov %edi,-0x14(%rbp)
7c82: 48 89 75 e0 mov %rsi,-0x20(%rbp)
7c86: 81 7d ec ff ff 0f 00 cmpl $0xfffff,-0x14(%rbp)
7c8d: 7f 1b jg 7caa <flextcp_fd_slookup+0x37>
7c8f: 8b 45 ec mov -0x14(%rbp),%eax
7c92: 48 98 cltq
7c94: 48 c1 e0 04 shl $0x4,%rax
7c98: 48 89 c2 mov %rax,%rdx
7c9b: 48 8d 05 86 86 00 00 lea 0x8686(%rip),%rax # 10328 <fhs+0x8>
7ca2: 0f b6 04 02 movzbl (%rdx,%rax,1),%eax
7ca6: 3c 01 cmp $0x1,%al
7ca8: 74 12 je 7cbc <flextcp_fd_slookup+0x49>
7caa: e8 31 b9 ff ff callq 35e0 <__errno_location#plt>
7caf: c7 00 09 00 00 00 movl $0x9,(%rax)
7cb5: b8 ff ff ff ff mov $0xffffffff,%eax
7cba: eb 53 jmp 7d0f <flextcp_fd_slookup+0x9c>
7cbc: c7 45 f4 01 00 00 00 movl $0x1,-0xc(%rbp)
7cc3: 8b 45 ec mov -0x14(%rbp),%eax
7cc6: 48 98 cltq
7cc8: 48 c1 e0 04 shl $0x4,%rax
7ccc: 48 89 c2 mov %rax,%rdx
7ccf: 48 8d 05 4a 86 00 00 lea 0x864a(%rip),%rax # 10320 <fhs>
7cd6: 48 8b 04 02 mov (%rdx,%rax,1),%rax
7cda: 48 89 45 f8 mov %rax,-0x8(%rbp)
7cde: 48 8b 55 f8 mov -0x8(%rbp),%rdx
7ce2: 8b 45 f4 mov -0xc(%rbp),%eax
7ce5: 87 82 c0 00 00 00 xchg %eax,0xc0(%rdx)
7ceb: 85 c0 test %eax,%eax
7ced: 74 0d je 7cfc <flextcp_fd_slookup+0x89>
7cef: f3 90 pause
7cf1: 83 ba c0 00 00 00 00 cmpl $0x0,0xc0(%rdx)
7cf8: 75 f5 jne 7cef <flextcp_fd_slookup+0x7c>
7cfa: eb e9 jmp 7ce5 <flextcp_fd_slookup+0x72>
7cfc: 89 45 f4 mov %eax,-0xc(%rbp)
7cff: 48 8b 45 e0 mov -0x20(%rbp),%rax
7d03: 48 8b 55 f8 mov -0x8(%rbp),%rdx
7d07: 48 89 10 mov %rdx,(%rax)
7d0a: b8 00 00 00 00 mov $0x0,%eax
7d0f: c9 leaveq
7d10: c3 retq
CMake Release Profile:
0000000000007d80 <flextcp_fd_slookup>:
7d80: f3 0f 1e fa endbr64
7d84: 81 ff ff ff 0f 00 cmp $0xfffff,%edi
7d8a: 7f 44 jg 7dd0 <flextcp_fd_slookup+0x50>
7d8c: 48 63 ff movslq %edi,%rdi
7d8f: 48 8d 05 6a 85 00 00 lea 0x856a(%rip),%rax # 10300 <fhs>
7d96: 48 c1 e7 04 shl $0x4,%rdi
7d9a: 48 01 c7 add %rax,%rdi
7d9d: 80 7f 08 01 cmpb $0x1,0x8(%rdi)
7da1: 75 2d jne 7dd0 <flextcp_fd_slookup+0x50>
7da3: 48 8b 17 mov (%rdi),%rdx
7da6: b8 01 00 00 00 mov $0x1,%eax
7dab: 87 82 c0 00 00 00 xchg %eax,0xc0(%rdx)
7db1: 85 c0 test %eax,%eax
7db3: 74 0d je 7dc2 <flextcp_fd_slookup+0x42>
7db5: f3 90 pause
7db7: 83 ba c0 00 00 00 00 cmpl $0x0,0xc0(%rdx)
7dbe: 75 f5 jne 7db5 <flextcp_fd_slookup+0x35>
7dc0: eb e9 jmp 7dab <flextcp_fd_slookup+0x2b>
7dc2: 31 c0 xor %eax,%eax
7dc4: 48 89 16 mov %rdx,(%rsi)
7dc7: c3 retq
7dc8: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)
7dcf: 00
7dd0: 55 push %rbp
7dd1: 48 89 e5 mov %rsp,%rbp
7dd4: e8 b7 b7 ff ff callq 3590 <__errno_location#plt>
7dd9: c7 00 09 00 00 00 movl $0x9,(%rax)
7ddf: b8 ff ff ff ff mov $0xffffffff,%eax
7de4: 5d pop %rbp
7de5: c3 retq
7de6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
7ded: 00 00 00
Is there any way to prevent gcc from reordering these two instructions?
Edit: I use the default toolchain (gcc-11.2.0 + glibc 2.35) on Ubuntu 22.04. Sorry that a reproducible example is not available.
Edit: Add source code of the example function.
Try -fno-shrink-wrap
This looks like "shrink-wrap" optimization: only doing the function prologue in a code path where it's needed. The usual benefit is to run an early-out check before the prologue, not saving/restoring a bunch of registers on that path through the function.
But here, GCC decided to only do the prologue (setting up a frame pointer) if it had to call another function. That function is __errno_location in the error-return path. Oops. :P (And GCC correctly realized that's the uncommon case, and put it out-of-line after the ret through the fast path. So the fast path can be a straight line with no taken branches, other than inside your asm(). It's not a separate function, it's just tail-duplication of the one you showed source for.)
The main path through the function is very tiny, just a few C assignment statements and an asm() statement. GCC doesn't have a clear idea of how big an asm block is (although I think has some heuristics, but is still rather willing to inline one). And it has no idea if there might be loops or any significant time spent in an asm block.
This is a known issue, GCC bug #98018 suggested that GCC should have an option to force frame-pointer setup at the actual top of a function. Because there currently isn't an option that's 100% reliable, other than disabling optimization which is not usable. (Thanks to #Margaret Bloom for finding & linking this.)
As comment 6 on that GCC bug mentions, disabling shrink-wrapping is part of what's necessary to make sure GCC sets up the frame pointer at the top of the function itself, not just inside some if that needs the prologue.
That GCC issue seems to be considering a feature that would stop function inlining, so backtraces would fully reflect the C abstract machine's nesting of function calls. That goes beyond what you're looking for, which I think is just to have frame pointers set up on entry to functions that exist in the asm after optimization.
Disabling shrink-wrapping will force the whole prologue to happen there, including push of other regs, if there were any. Not just the frame pointer.
But here there aren't any others. Still, with optimization enabled in general, losing shrink-wrapping is probably pretty minor.

Assembly Interpretation - Register confusion

I'm working on an assignment for class where I have to interpret assembly. I know the input to defuse the bomb is 442, but I'm not exactly sure why.
8048c80: 83 ec 2c sub $0x2c,%esp
8048c83: c7 44 24 1c 00 00 00 movl $0x0,0x1c(%esp)
8048c8a: 00
8048c8b: 8d 44 24 1c lea 0x1c(%esp),%eax
8048c8f: 89 44 24 08 mov %eax,0x8(%esp)
8048c93: c7 44 24 04 64 a7 04 movl $0x804a764,0x4(%esp)
8048c9a: 08
8048c9b: 8b 44 24 30 mov 0x30(%esp),%eax
8048c9f: 89 04 24 mov %eax,(%esp)
8048ca2: e8 59 fc ff ff call 8048900 <__isoc99_sscanf#plt>
8048ca7: 83 f8 01 cmp $0x1,%eax
8048caa: 74 05 je 8048cb1 <phase_1+0x31>
8048cac: e8 e4 07 00 00 call 8049495 <explode_bomb>
8048cb1: 81 7c 24 1c ba 01 00 cmpl $0x1ba,0x1c(%esp)
8048cb8: 00
8048cb9: 74 05 je 8048cc0 <phase_1+0x40>
8048cbb: e8 d5 07 00 00 call 8049495 <explode_bomb>
8048cc0: 83 c4 2c add $0x2c,%esp
8048cc3: c3 ret
Sscanf takes two values, "%d" and my inputted value, but I'm not sure where it stores the value or why %eax is 1 or why 0x1c(%esp) has the value. We store 0x0 there at the beginning, and then move 0x30(%esp), %eax, so shouldn't it be 0? Any help understanding this would be very much appreciated.
To be clear, this is x86 in at&t syntax.

Deciphering x86 assembly function

I am currently working on phase 2 of the binary bomb assignment. I'm having trouble deciphering exactly what a certain function does when called. I've been stuck on it for days.
The function is:
0000000000400f2a <func2a>:
400f2a: 85 ff test %edi,%edi
400f2c: 74 1d je 400f4b <func2a+0x21>
400f2e: b9 cd cc cc cc mov $0xcccccccd,%ecx
400f33: 89 f8 mov %edi,%eax
400f35: f7 e1 mul %ecx
400f37: c1 ea 03 shr $0x3,%edx
400f3a: 8d 04 92 lea (%rdx,%rdx,4),%eax
400f3d: 01 c0 add %eax,%eax
400f3f: 29 c7 sub %eax,%edi
400f41: 83 04 be 01 addl $0x1,(%rsi,%rdi,4)
400f45: 89 d7 mov %edx,%edi
400f47: 85 d2 test %edx,%edx
400f49: 75 e8 jne 400f33 <func2a+0x9>
400f4b: f3 c3 repz retq
It gets called in the larger function "phase_2":
0000000000400f4d <phase_2>:
400f4d: 53 push %rbx
400f4e: 48 83 ec 60 sub $0x60,%rsp
400f52: 48 c7 44 24 30 00 00 movq $0x0,0x30(%rsp)
400f59: 00 00
400f5b: 48 c7 44 24 38 00 00 movq $0x0,0x38(%rsp)
400f62: 00 00
400f64: 48 c7 44 24 40 00 00 movq $0x0,0x40(%rsp)
400f6b: 00 00
400f6d: 48 c7 44 24 48 00 00 movq $0x0,0x48(%rsp)
400f74: 00 00
400f76: 48 c7 44 24 50 00 00 movq $0x0,0x50(%rsp)
400f7d: 00 00
400f7f: 48 c7 04 24 00 00 00 movq $0x0,(%rsp)
400f86: 00
400f87: 48 c7 44 24 08 00 00 movq $0x0,0x8(%rsp)
400f8e: 00 00
400f90: 48 c7 44 24 10 00 00 movq $0x0,0x10(%rsp)
400f97: 00 00
400f99: 48 c7 44 24 18 00 00 movq $0x0,0x18(%rsp)
400fa0: 00 00
400fa2: 48 c7 44 24 20 00 00 movq $0x0,0x20(%rsp)
400fa9: 00 00
400fab: 48 8d 4c 24 58 lea 0x58(%rsp),%rcx
400fb0: 48 8d 54 24 5c lea 0x5c(%rsp),%rdx
400fb5: be 9e 26 40 00 mov $0x40269e,%esi
400fba: b8 00 00 00 00 mov $0x0,%eax
400fbf: e8 6c fc ff ff callq 400c30 <__isoc99_sscanf#plt>
400fc4: 83 f8 02 cmp $0x2,%eax
400fc7: 74 05 je 400fce <phase_2+0x81>
400fc9: e8 c1 06 00 00 callq 40168f <explode_bomb>
400fce: 83 7c 24 5c 64 cmpl $0x64,0x5c(%rsp)
400fd3: 76 07 jbe 400fdc <phase_2+0x8f>
400fd5: 83 7c 24 58 64 cmpl $0x64,0x58(%rsp)
400fda: 77 05 ja 400fe1 <phase_2+0x94>
400fdc: e8 ae 06 00 00 callq 40168f <explode_bomb>
400fe1: 48 8d 74 24 30 lea 0x30(%rsp),%rsi
400fe6: 8b 7c 24 5c mov 0x5c(%rsp),%edi
400fea: e8 3b ff ff ff callq 400f2a <func2a>
400fef: 48 89 e6 mov %rsp,%rsi
400ff2: 8b 7c 24 58 mov 0x58(%rsp),%edi
400ff6: e8 2f ff ff ff callq 400f2a <func2a>
400ffb: bb 00 00 00 00 mov $0x0,%ebx
401000: 8b 04 1c mov (%rsp,%rbx,1),%eax
401003: 39 44 1c 30 cmp %eax,0x30(%rsp,%rbx,1)
401007: 74 05 je 40100e <phase_2+0xc1>
401009: e8 81 06 00 00 callq 40168f <explode_bomb>
40100e: 48 83 c3 04 add $0x4,%rbx
401012: 48 83 fb 28 cmp $0x28,%rbx
401016: 75 e8 jne 401000 <phase_2+0xb3>
401018: 48 83 c4 60 add $0x60,%rsp
40101c: 5b pop %rbx
40101d: c3 retq
I completely understand what phase_2 is doing, I just don't understand what func2a is doing and how it affects the values at 0x30(%rsp) and so on. Because of this I always get to the comparison statement at 0x401003, and the bomb eventually explodes there.
My problem is I don't understand how the input (phase solution) is affecting the values at 0x30(%rsp) via func2a.
400f2a: 85 ff test %edi,%edi
400f2c: 74 1d je 400f4b <func2a+0x21>
This is just an early exit for when edi is zero (je is the same as jz).
400f2e: b9 cd cc cc cc mov $0xcccccccd,%ecx
400f33: 89 f8 mov %edi,%eax
400f35: f7 e1 mul %ecx
400f37: c1 ea 03 shr $0x3,%edx
This is a classic optimization trick; it is the integer arithmetic equivalent of dividing by multiplying by the inverse (see here for details); in practice, here it's the same as saying edx = edi / 10;
400f3a: 8d 04 92 lea (%rdx,%rdx,4),%eax
400f3d: 01 c0 add %eax,%eax
Here it is exploiting lea to perform arithmetic (and it's way clearer in Intel syntax, where it is lea eax,[rdx+rdx*4] => eax = edx*5), then sums the result with itself. It all boils down to eax = edx*10.
400f3f: 29 c7 sub %eax,%edi
Then, subtract it back to edi.
So, all in all this is a complicated (but fast) way to compute the last decimal digit of edi; what we have until now is something like:
void func2a(unsigned edi) {
if(edi==0) return;
label1:
edx=edi/10;
edi%=10;
// ...
}
(label1: is there because 400f33 is a jump target later)
Going on:
400f41: 83 04 be 01 addl $0x1,(%rsi,%rdi,4)
Again, this is way clearer to me in Intel syntax - add dword [rsi+rdi*4],byte +0x1. It is a regular increment into an array of 32-bit int (rdi is multiplied by 4); so, we can imagine that rsi points to an array of integers, indexed with the just-calculated last digit of edi.
void func2a(unsigned edi, int rsi[]) {
if(edi==0) return;
label1:
edx=edi/10;
edi%=10;
rsi[edi]++;
}
Then:
400f45: 89 d7 mov %edx,%edi
400f47: 85 d2 test %edx,%edx
400f49: 75 e8 jne 400f33 <func2a+0x9>
Move the result of the division we calculated above to edi, and loop if it's different from zero.
400f4b: f3 c3 repz retq
Return (using an unusual encoding of the instruction that is optimal for certain AMD processors).
So, by rewriting the jumps with a while loop and giving some meaningful names...
// number is edi, digits_count is rsi, as per regular
// x64 SystemV calling convention
void count_digits(unsigned number, int digits_count[]) {
while(number) {
digits_count[number%10]++;
number/=10;
}
}
I.e., this is a function that, given an integer, counts the occurrences of the single decimal digits, by incrementing the corresponding buckets in the digits_count array.
Fun fact: if we give the C code above to gcc (almost any recent version at -O1) we obtain back exactly the assembly you provided.

Manual decompilation of asm snippet

I've been trying to decompile the following asm snippet(that's all I have):
55 push %rbp
48 89 e5 mov %rsp,%rbp
48 81 ec d0 00 00 00 sub $0xd0,%rsp
64 48 8b 04 25 28 00 mov %fs:0x28,%rax
00 00
48 89 45 f8 mov %rax,-0x8(%rbp)
31 c0 xor %eax,%eax
48 c7 85 30 ff ff ff movq $0x0,-0xd0(%rbp)
00 00 00 00
48 8d b5 38 ff ff ff lea -0xc8(%rbp),%rsi
b8 00 00 00 00 mov $0x0,%eax
ba 18 00 00 00 mov $0x18,%edx
48 89 f7 mov %rsi,%rdi
48 89 d1 mov %rdx,%rcx
f3 48 ab rep stos %rax,%es:(%rdi)
48 8b 15 19 06 20 00 mov 0x200619(%rip),%rdx
48 8d 85 30 ff ff ff lea -0xd0(%rbp),%rax
be ce 0f 40 00 mov $0x400fce,%esi
48 89 c7 mov %rax,%rdi
b8 00 00 00 00 mov $0x0,%eax
e8 4e fc ff ff callq 4008a0 <sprintf#plt>
Here is my attempt:
char buf[192] = {0};
sprintf(buf, "hello %s", name);
I've compiled this with gcc 4.8.5, and it gave me:
55 push %rbp
48 89 e5 mov %rsp,%rbp
48 81 ec d0 00 00 00 sub $0xd0,%rsp
64 48 8b 04 25 28 00 mov %fs:0x28,%rax
00 00
48 89 45 f8 mov %rax,-0x8(%rbp)
31 c0 xor %eax,%eax
48 8d b5 30 ff ff ff lea -0xd0(%rbp),%rsi
b8 00 00 00 00 mov $0x0,%eax
ba 18 00 00 00 mov $0x18,%edx
48 89 f7 mov %rsi,%rdi
48 89 d1 mov %rdx,%rcx
f3 48 ab rep stos %rax,%es:(%rdi)
48 8b 15 14 14 20 00 mov 0x201414(%rip),%rdx
48 8d 85 30 ff ff ff lea -0xd0(%rbp),%rax
be 2e 10 40 00 mov $0x40102e,%esi
48 89 c7 mov %rax,%rdi
b8 00 00 00 00 mov $0x0,%eax
e8 cb fb ff ff callq 4008a0 <sprintf#plt>
I'm struggling to figure out why this exists:
movq $0x0,-0xd0(%rbp)
and also the subsequent usage of -0xd0(%rbp) as a pointer for the argument to sprintf. I'm puzzled because the rep stos begin at -0xc8(%rbp) and not -0xd0(%rbp).
This is probably compiler specific, but still I'm curious what could possibly be the original code that produced that asm.
I imagine something like:
char buf[192] = {0, 0, 0, 0, 0, 0, 0, 0};
sprintf(buf + 8, "hello %s", name);
... would give you that output.
The movq instruction you refer to stores 0 (an 8-byte quantity) at the beginning of an array. The -0xc8(%rbp) comes from copying a string to an offset within the array.

How do I disassemble an object program made on C?

How do I disassemble an object program made on C(linux)?
Can anyone please help me with the command line.
Use objdump:
objdump -d -C file.o
Sample output:
...
0000015e <add_exclude>:
15e: 55 push %ebp
15f: 89 e5 mov %esp,%ebp
161: 83 ec 08 sub $0x8,%esp
164: a1 10 00 00 00 mov 0x10,%eax
169: 3b 05 14 00 00 00 cmp 0x14,%eax
16f: 7f 54 jg 1c5 <add_exclude+0x67>
171: 83 3d 10 00 00 00 00 cmpl $0x0,0x10
178: 75 1f jne 199 <add_exclude+0x3b>
17a: 83 ec 0c sub $0xc,%esp
17d: c7 05 10 00 00 00 40 movl $0x40,0x10
184: 00 00 00
187: 68 00 01 00 00 push $0x100
18c: e8 fc ff ff ff call 18d <add_exclude+0x2f>
191: 83 c4 10 add $0x10,%esp
...

Resources