-1

I want to make my program more strong in front of hackers, so i have a program::validator class which validate my environment by some parameters. I :

  • Compile program::validator shared library.
  • Compile program using -O2 and --ffast-math and link to libprogramvalidator.so.
  • Run the program with the GDB.
  • Find the line which actually call program::validator::is_valid_system().

and i want know that i can ignore execution of those line ?

Jeffrey
  • 11,063
  • 1
  • 21
  • 42
Kate
  • 561
  • 4
  • 11
  • Do you want to run gdb and ignore the line during normal execution or once for testing? – Sebastian Jun 16 '20 at 13:30
  • Yes, you can skip instructions and change registers using a debugger. You can also patch the binary itself to do this. – Jester Jun 16 '20 at 13:32
  • When you say "ignore execution of those line", do you want to avoid the execution of the entire line that calls is_valid_system, including any other side effects made by any expression in that line, or do you want to just avoid the execution of is_valid_system? – Mark Plotnick Jun 16 '20 at 14:18
  • The question as stated is exceedingly unclear. What are you actually trying to achieve? – Employed Russian Jun 16 '20 at 17:09
  • @Sebastian, Yes. – Kate Jun 17 '20 at 02:29
  • @Jester, So how i can do it ? please give me a document. – Kate Jun 17 '20 at 02:30
  • @MarkPlotnick, I just want to avoid execution of `is_valid_system`. – Kate Jun 17 '20 at 02:30
  • @EmployedRussian, Sorry i thought it was clear, i just want avoid call to `is_valid_system` function in my ELF executable binary file. – Kate Jun 17 '20 at 02:32
  • If it is so easy to circumvent your hurdles against hackers, you should consider making them stronger ... – Sebastian Jun 17 '20 at 14:11

2 Answers2

4

i just want avoid call to is_valid_system function in my ELF executable binary file.

There are several easy ways. You can use GDB jump $address, or return commands to achieve this. Example:

#include <stdio.h>

int is_valid_system()
{
  return 0;
}

int main()
{
  if (is_valid_system()) {
    printf("Life is good\n");
    return 0;
  }
  printf("Invalid system detected\n");
  return 1;
}

As you can see, running above program will always print Invalid system and exit with error code 1. Let's confirm that:

gcc t.c && gdb -q ./a.out
(gdb) run
Starting program: /tmp/a.out 
Invalid system detected
[Inferior 1 (process 180727) exited with code 01]

Ok, now let's make the program print Life is good. Let's do that via return. To achieve that, set a breakpoint on the desired function, set return register ($rax on x86_64) to desired value, and return to force the function to immediately return:

(gdb) b is_valid_system
Breakpoint 1 at 0x1139
(gdb) run
Starting program: /tmp/a.out 

Breakpoint 1, 0x0000555555555139 in is_valid_system ()
(gdb) set $rax = 1
(gdb) return
#0  0x000055555555514e in main ()
(gdb) c
Continuing.
Life is good
[Inferior 1 (process 196141) exited normally]

Alternatively, you can "jump over" the function. Disasemble the caller, break on the CALL instruction, set return register to desired value, and jump to next instruction:

(gdb) disas main
Dump of assembler code for function main:
   0x0000555555555140 <+0>:     push   %rbp
   0x0000555555555141 <+1>:     mov    %rsp,%rbp
   0x0000555555555144 <+4>:     mov    $0x0,%eax
   0x0000555555555149 <+9>:     callq  0x555555555135 <is_valid_system>
   0x000055555555514e <+14>:    test   %eax,%eax
   0x0000555555555150 <+16>:    je     0x555555555165 <main+37>
   0x0000555555555152 <+18>:    lea    0xeab(%rip),%rdi        # 0x555555556004
   0x0000555555555159 <+25>:    callq  0x555555555030 <puts@plt>
   0x000055555555515e <+30>:    mov    $0x0,%eax
   0x0000555555555163 <+35>:    jmp    0x555555555176 <main+54>
   0x0000555555555165 <+37>:    lea    0xea5(%rip),%rdi        # 0x555555556011
   0x000055555555516c <+44>:    callq  0x555555555030 <puts@plt>
   0x0000555555555171 <+49>:    mov    $0x1,%eax
   0x0000555555555176 <+54>:    pop    %rbp
   0x0000555555555177 <+55>:    retq   
End of assembler dump.
(gdb) b *0x0000555555555149
Breakpoint 2 at 0x555555555149
(gdb) run
Starting program: /tmp/a.out 

Breakpoint 2, 0x0000555555555149 in main ()
(gdb) set $rax = 1
(gdb) jump *0x000055555555514e
Continuing at 0x55555555514e.
Life is good
[Inferior 1 (process 205378) exited normally]

You could also use GDB to temporarily or permanently patch the is_valid_system out. Details in this answer.

Employed Russian
  • 199,314
  • 34
  • 295
  • 362
  • Wow, that is great. But i did it in another way. First i take a dump of binary file with `objdump` and find the line which check system validation and use `dd` to write `\x90` at that section and system validation was gone. – Kate Jun 17 '20 at 03:38
  • 2
    @Kate The _other_ answer I linked to shows how to patch the binary using GDB, which is a bit easier than using `dd`. – Employed Russian Jun 17 '20 at 03:39
3

This is another variant of the common mistake "Thinking that you can trust your environment, even when you cannot trust your environment".

You implicitly trust that the compiler is a real compiler, the linker a real linker, GDB the real GDB, and the disassembler a real disassembler. You have given hackers not one but four ways to attack your program.

MSalters
  • 173,980
  • 10
  • 155
  • 350