神刀安全网

Defusing a binary bomb with – Part 4

Defusing a binary bomb with gdb – Part 4

25 Apr 2016

This post is part of a series where I show how to defuse a binary bomb by reading assembly code and using gdb . You might want to readthe first part if you haven’t yet.

We are back on defusing the fourth phase of the binary bomb.

The code for phase_4 is the following:

000000000040100c <phase_4>:   40100c:   48 83 ec 18             sub    $0x18,%rsp   401010:   48 8d 4c 24 0c          lea    0xc(%rsp),%rcx   401015:   48 8d 54 24 08          lea    0x8(%rsp),%rdx   40101a:   be cf 25 40 00          mov    $0x4025cf,%esi   40101f:   b8 00 00 00 00          mov    $0x0,%eax   401024:   e8 c7 fb ff ff          callq  400bf0 <__isoc99_sscanf@plt>   401029:   83 f8 02                cmp    $0x2,%eax   40102c:   75 07                   jne    401035 <phase_4+0x29>   40102e:   83 7c 24 08 0e          cmpl   $0xe,0x8(%rsp)   401033:   76 05                   jbe    40103a <phase_4+0x2e>   401035:   e8 00 04 00 00          callq  40143a <explode_bomb>   40103a:   ba 0e 00 00 00          mov    $0xe,%edx   40103f:   be 00 00 00 00          mov    $0x0,%esi   401044:   8b 7c 24 08             mov    0x8(%rsp),%edi   401048:   e8 81 ff ff ff          callq  400fce <func4>   40104d:   85 c0                   test   %eax,%eax   40104f:   75 07                   jne    401058 <phase_4+0x4c>   401051:   83 7c 24 0c 00          cmpl   $0x0,0xc(%rsp)   401056:   74 05                   je     40105d <phase_4+0x51>   401058:   e8 dd 03 00 00          callq  40143a <explode_bomb>   40105d:   48 83 c4 18             add    $0x18,%rsp   401061:   c3                      retq 

Exactly as happened onphase 3 we can see that this phase is expecting two integers as input. On 0x40101a the same format used before is stored on esi which is then used by sscanf on 0x401024 .

(gdb) p (char *) 0x4025cf $1 = 0x4025cf "%d %d" 

On 0x401029 we can also confirm that if we enter more than 2 integers the code will jump to 0x401035 that calls explode_bomb :

  401029:   83 f8 02                cmp    $0x2,%eax   40102c:   75 07                   jne    401035 <phase_4+0x29> 

So which exact numbers should we enter? It must be a number that is less than 15:

  40102e:   83 7c 24 08 0e          cmpl   $0xe,0x8(%rsp)   401033:   76 05                   jbe    40103a <phase_4+0x2e>   401035:   e8 00 04 00 00          callq  40143a <explode_bomb> 

cmpl compares 0xe which is 14 to the first integerwe entered for this phase. Then jbe ("jump below or equal") will skip exploding the bomb if the value is less than or equal to 14.

After that we can see that some setup is done before calling a new function, func4 :

  40103a:   ba 0e 00 00 00          mov    $0xe,%edx   40103f:   be 00 00 00 00          mov    $0x0,%esi   401044:   8b 7c 24 08             mov    0x8(%rsp),%edi   401048:   e8 81 ff ff ff          callq  400fce <func4> 

edi is usually used as the register to hold the first argument, esi holds the second and edx holds the third argument. The first argument is the first number we provided, the second and third are 0 and 14 , respectively.

Let’s take a look at func4 :

0000000000400fce <func4>:   400fce:   48 83 ec 08             sub    $0x8,%rsp   400fd2:   89 d0                   mov    %edx,%eax   400fd4:   29 f0                   sub    %esi,%eax   400fd6:   89 c1                   mov    %eax,%ecx   400fd8:   c1 e9 1f                shr    $0x1f,%ecx   400fdb:   01 c8                   add    %ecx,%eax   400fdd:   d1 f8                   sar    %eax   400fdf:   8d 0c 30                lea    (%rax,%rsi,1),%ecx   400fe2:   39 f9                   cmp    %edi,%ecx   400fe4:   7e 0c                   jle    400ff2 <func4+0x24>   400fe6:   8d 51 ff                lea    -0x1(%rcx),%edx   400fe9:   e8 e0 ff ff ff          callq  400fce <func4>   400fee:   01 c0                   add    %eax,%eax   400ff0:   eb 15                   jmp    401007 <func4+0x39>   400ff2:   b8 00 00 00 00          mov    $0x0,%eax   400ff7:   39 f9                   cmp    %edi,%ecx   400ff9:   7d 0c                   jge    401007 <func4+0x39>   400ffb:   8d 71 01                lea    0x1(%rcx),%esi   400ffe:   e8 cb ff ff ff          callq  400fce <func4>   401003:   8d 44 00 01             lea    0x1(%rax,%rax,1),%eax   401007:   48 83 c4 08             add    $0x8,%rsp   40100b:   c3                      retq 

Looking at the first few instructions we can try to write what exactly is happening with the arguments that were given to this function. The instructions until 0x400fe2 are actually doing something like the following:

int func4(int a, int b, int c) {     int x = c - b; // 0x400fd2 and 0x400fd4     int y = x >> 31; // 0x400fd6 and 0x400fd8     x = x + y; // 0x400fdb     x = x >> 1; // 0x400fdd     y = x + b; // 0x400fdf } 

Then it compares y with a (our first input number for this phase) and if y <= a it will jump to 0x400ff2 . Otherwise it calls func4 again but this time c will be y - 1 (set at 0x400fe6 ). So on 0x400fe9 we can think of the invocation as:

func4(a, b, y - 1); 

After calling it you can see that 0x400fee will double the result from that "inner" invocation ( eax holds the return value from that execution) and then jump to 0x401007 which cleanups the stack frame for this invocation. So the result from this recursive call is actually:

return 2 * func4(a, b, y - 1); 

If y >= a the code will continue execution on 0x400ff2 . At that line it sets the possible result as 0 and then compares the same y with a again. This time if y >= a it jumps to 0x401007 and returns 0 , that’s why eax got the value 0 by the instruction before that. If y < a then func4 will be called again but in this case b will be y + 1 (this assignment happens on 0x400ffb ) and the following invocation happens on 0x400ffe :

func4(a, y + 1, c); 

After returning from this recursive call the return value is set on 0x401003 using the result from the recursive call so the result is actually:

return 2 * func4(a, y + 1, c) + 1; 

With all this context we can now try to guess what exactly is going on with this function:

int func4(int a, int b, int c) {     int x = c - b;     int y = x >> 31;     x = x + y;     x = x >> 1;     y = x + b;      if (y <= a) {         if (y >= a) {             return 0;         } else {             return 2 * func4(a, y + 1, c) + 1;         }     } else {         return 2 * func4(a, b, y - 1);     } } 

Remember that the initial call to func4 is func4(ourFirstInputNumber, 0, 14) and that ourFirstInputNumber <= 14 . Let’s try to see what happens if we input 1 as our first number:

func4(1, 0, 14) {     int x = 14 - 0;     int y = 14 >> 31;     x = 14 + 0;     x = 14 >> 1;     y = 7 + 0; } 

Then we can see that func4 will be called by the else clause of the first if :

return 2 * func4(1, 0, 7 - 1); 

The first recursive call will then be:

func4(1, 0, 6) {     int x = 6 - 0;     int y = 6 >> 31;     x = 6 + 0;     x = 6 >> 1;     y = 3 + 0; } 

Again the same branch will be taken:

return 2 * func4(1, 0, 3 - 1); 

The execution will then be:

func4(1, 0, 2) {     int x = 2 - 0;     int y = 2 >> 31;     x = 2 + 0;     x = 2 >> 1;     y = 1 + 0; } 

This time y == 1 so both if branches will be taken and 0 will be returned from this recursive invocation. Remember that we invoked this function twice so the final result will be:

return func4(1, 0, 14);     return 2 * func4(1, 0, 6);         return 2 * func4(1, 0, 2);             return 0; 

Since the last call to func4 returned 0 the final result will also be 0 and the execution will continue this time on phase_4 at 0x40104d :

  401048:   e8 81 ff ff ff          callq  400fce <func4>   40104d:   85 c0                   test   %eax,%eax   40104f:   75 07                   jne    401058 <phase_4+0x4c>   401051:   83 7c 24 0c 00          cmpl   $0x0,0xc(%rsp)   401056:   74 05                   je     40105d <phase_4+0x51>   401058:   e8 dd 03 00 00          callq  40143a <explode_bomb>   40105d:   48 83 c4 18             add    $0x18,%rsp   401061:   c3                      retq 

This line and the line below test whether or not the result of that func4 invocation returned 0 , if it did our first input is correct and execution continues at 0x401051 . This instruction then simply checks if our second input is 0 and if it is the function returns and phase 4 is defused:

1 0 So you got that one.  Try this one. 

We have something interesting here, remember that there are two checks about y and a ? The second check uses the jge instruction that checks whether the value is greater than or equal to (kind of obvious if you think about what ge might mean in the instruction name). The interesting fact here is that the previous check also tested for equality with jle . So, if y <= a and then you check whether y >= a there’s only one case that would satisfy both conditions and that is y == a . I’m not sure if the compiler chose jge even though the code was written as if (y == a) or if the code was actually written as if (y >= a) .

Another interesting thing about this phase is how the compiler manages the results of the recursive calls. Since eax is a register the results of each recursive invocation will be available to the stack frame that invoked it and then it can simply be returned without saving the result in some other place. Also you see that after calling func4 again there’s nothing managing the local variables x and y which is why they are also stored in registers instead of being saved inside each stack frame.

An exercise left to the reader is trying to find the other possible solutions for this phase including one input that doesn’t even call func4 recursively. Also try to see which numbers are invalid for this phase and the result that func4 returns when these numbers are used.

The fifth phase will be covered in another post soon.

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Defusing a binary bomb with – Part 4

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址