我有一个普通的C程序。我已经制作了它的可执行文件。如果我读了一个elf文件,它会告诉我入口点是入口点地址:0x80482e0。在跟踪入口点后,我看到最后的呼叫如下。
080482b0 <__gmon_start__@plt-0x10>:
80482b0: ff 35 50 96 04 08 pushl 0x8049650
80482b6: ff 25 54 96 04 08 jmp *0x8049654
80482bc: 00 00 add %al,(%eax)
我怎样才能破解0x8049654的值来调用其他函数而不是main?我相信主函数地址将存储在地址 - 0x8049654?我对么?我想要做的是,而不是调用main(),我想破解它来调用其他函数?有可能吗?
主函数地址是否应包含在* 0x8049654?
中答案 0 :(得分:9)
main
未从__gmon_start__
调用:
(gdb) disassemble main
Dump of assembler code for function main:
0x080483d8 <main+0>: push %ebp // main() address
0x080483d9 <main+1>: mov %esp,%ebp
0x080483db <main+3>: and $0xfffffff0,%esp
0x080483de <main+6>: sub $0x10,%esp
0x080483e1 <main+9>: movl $0x80484c9,(%esp)
0x080483e8 <main+16>: call 0x80482f8 <puts@plt>
0x080483ed <main+21>: mov $0x0,%eax
0x080483f2 <main+26>: leave
0x080483f3 <main+27>: ret
End of assembler dump.
(gdb) disassemble __gmon_start__
Dump of assembler code for function __gmon_start__@plt:
0x080482d8 <__gmon_start__@plt+0>: jmp *0x80495c8
0x080482de <__gmon_start__@plt+6>: push $0x0
0x080482e3 <__gmon_start__@plt+11>: jmp 0x80482c8
End of assembler dump.
(gdb) # no call to main
它已从函数_start
传递:
(gdb) disassemble _start
Dump of assembler code for function _start:
0x08048310 <_start+0>: xor %ebp,%ebp
0x08048312 <_start+2>: pop %esi
0x08048313 <_start+3>: mov %esp,%ecx
0x08048315 <_start+5>: and $0xfffffff0,%esp
0x08048318 <_start+8>: push %eax
0x08048319 <_start+9>: push %esp
0x0804831a <_start+10>: push %edx
0x0804831b <_start+11>: push $0x8048400
0x08048320 <_start+16>: push $0x8048410
0x08048325 <_start+21>: push %ecx
0x08048326 <_start+22>: push %esi
0x08048327 <_start+23>: push $0x80483d8
0x0804832c <_start+28>: call 0x80482e8 <__libc_start_main@plt>
0x08048331 <_start+33>: hlt
0x08048332 <_start+34>: nop
...
您可以阅读ELF标题,您会在_start
中找到e_entry
的地址:
e_entry This member gives the virtual address to which the system
first transfers control, thus starting the process. If
the file has no associated entry point, this member holds
zero.
这是一个获取地址的简单程序:
#include <stdio.h>
#include <elf.h>
int main(int argc, char **argv) {
FILE *file;
Elf32_Ehdr hdr;
if( argc < 2 ) {
printf("uage: %s [FILE]\n", argv[0]);
return -1;
}
if( (file = fopen(argv[1], "r")) == NULL ) {
perror("Error");
return -1;
}
fread(&hdr, sizeof(Elf32_Ehdr), 1, file);
fclose(file);
if( (hdr.e_ident[EI_MAG0] != ELFMAG0) ||
(hdr.e_ident[EI_MAG1] != ELFMAG1) ||
(hdr.e_ident[EI_MAG2] != ELFMAG2) ||
(hdr.e_ident[EI_MAG3] != ELFMAG3) ) {
printf("Error: Error: Not a valid ELF file.\n");
return -1;
}
printf("Entry: 0x%.8x\n", hdr.e_entry);
return 0;
}
因此,如果您想将main
重定向到其他功能,则需要修补此部分:
0x08048327 <_start+23>: push $0x80483d8
并将其替换为您的功能。在这里,我有一个简单的程序:
#include <stdio.h>
void function(void) {
puts("Function");
}
int main(int argc, char **argv) {
puts("Main");
return 0;
}
将打印:
$ ./prog1
Main
$
我们需要使用main
找出function
和readelf
的地址:
$ readelf -s prog1
Symbol table '.dynsym' contains 5 entries:
...
Symbol table '.symtab' contains 66 entries:
Num: Value Size Type Bind Vis Ndx Name
...
61: 080483c4 20 FUNC GLOBAL DEFAULT 14 function
...
64: 080483d8 28 FUNC GLOBAL DEFAULT 14 main
...
$
现在修补push $0x80483d8
并用main = 080483d8
替换function = 080483c4
的地址,我使用了十六进制编辑器,不要忘记按照尊重的顺序翻转字节。它将成为:
0x08048327 <_start+23>: push $0x80483c4
现在测试一下:
$ ./prog1
Function
$
参考:How main() is executed on Linux
这是一种快速而肮脏的方式。如果您只是想在调用main
之前调用某些内容,则可以使用GCC属性function
使__attribute__((constructor))
成为构造函数,如下所示:
#include <stdio.h>
__attribute__((constructor)) void function(void) {
puts("Function");
}
int main(int argc, char **argv) {
puts("Main");
return 0;
}
现在它将在main之前调用:
$ gcc -Wall prog.c -o prog
$ ./prog
Function
Main
$
答案 1 :(得分:4)
Elf文件中描述的入口点是而不是您的main()
函数。就{C>语言而言,main()
是第一个,但操作系统还有其他需求(取决于操作系统和编译器)。例如,对于GCC,您的初始入口点可能来自crt0.o
中的汇编代码;此代码处理所需的任何基本初始化,然后调用main()
。
虽然可以执行二进制编辑,但它肯定不是微不足道的,并且假设您拥有代码的来源,那么这样做会带来什么好处,这是非常值得怀疑的。