Modify ELF executable entry to infect a program

When introducing how to inject code into Linux kernel module in the previous article, I mentioned that it is easy to "modify the entry of ELF file or PE file to jump to its own logic".

Is it really easy? Yes, it's really easy. This article is to demonstrate this.

Remember the panda burning incense virus. Early computer viruses, including it, injected their own code in this way and realized self replication. Of course, it does not necessarily modify the entry address, but it must have modified the ELF/PE file.

If you want to modify the ELF file, we first need to understand the structure of the ELF file, which only takes about 10 minutes to browse. This article will not take a long time to introduce the relevant concepts of elf.

The < ELF. H > header file already contains enough data structures and API s for us to modify the ELF executable file, which we can use.

The example shown in this article is very simple. It is to infect an existing LEF executable. First, we provide the code of the executable:

// hello.cintmain(){printf("aaaaaaaaaaaaa\n");}

We compile it into a hello executable.

Next, we try to use another program to modify its entry. The new entry logic is as follows:

if(fork()==0){exec("/bin/aa");}else{goto orig_entry;}

We definitely can't inject C code directly into ELF file, just like we can't inject Ramen soup into blood vessel. So we must get the assembly instruction code of the above logic.

How to get the instruction code?

We manually write the above C logic into an inline assembly, and then compile it into an executable file. Through objdump, we can find the assembly instruction code:

The raw material is ready, waiting to inject the bytecode in the above array into the hello program.

Before implementing injection, explain two points.

First, note the instructions above:

movabs $0x61612f6e69622f,%r11
push %r11
mov %rsp,%rdi

Obviously, according to the function call parameter specification of X86 ʄ, rdi register is the first parameter of Exec System call, that is "/ bin/aa" , but the parameter preparation of exec is extremely troublesome and requires a string. We know that the string is saved in a separate section of the ELF file. I don't want to be so troublesome. To inject another string, I just want to inject a piece of code, just the code. So I take a trick here:

// I encoded the string into a long number.char name[8]={'/','b','i','n','/','a','a',0};char*pname;unsignedlong pv =*(unsignedlong*)&name[0];// 0x61612f6e69622f, i.e. aa/nib /, small end converted to / bin/aa
pname =(char*)&pv;// pname is aa

At the same time, I use push to save the pointer of the long number in the rsp. In this way, only the following operations are required. The rdi register is the first parameter of exec

push %r11
mov %rsp,%rdi

In this way, the complex string saving and operation are omitted. Is it fun? Before going on, / bin/aa needs to be revealed. It's actually very simple, that is, to print a sentence:

The effect we want is that all infected programs (in our case, hello) will print the sentence "call now" when they are executing.

OK, let's move on.

It's time to give the code to modify the entry, or that sentence, I can't guarantee that this code is completely bug free, but it's simple enough and can work. In order to show the effect, simplicity is the most important.

The infection code above is very simple. You may think it's wrong. Yes, it is wrong, because it hopes there is free space behind the program. I haven't even modified the size of the section and the size of the file. We found that the size of the file hasn't changed before and after the injection of infection, and there are better side effects:

We see that there is no difference in the results of objdump. If we improve the program, it will be easier to expose it. If I add the adjust sections size operation in model. C, then after the executable is infected, the result of objdump will be more than the following: