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
$
参考:Linux上main()是如何执行的
这是一种快速而肮脏的方式。如果您只想在调用之前调用某些内容,main
则可以function
使用 GCC 属性创建一个构造函数,__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
$
参考:声明函数的属性