main()
时,argv[0]
是否总是可执行文件的名称?还是这只是一种常见的约定,不能保证100%的准确性?main()
时,argv[0]
是否总是可执行文件的名称?还是这只是一种常见的约定,不能保证100%的准确性?argc
的值大于零,则argv[0]
指向的字符串表示程序名称;如果主机环境不能提供程序名称,则argv[0][0]
应是空字符。argc
的值大于零,则数组元素argv[0]
到argv[argc-1]
(包括)将包含指向字符串的指针,这些字符串在程序启动前由主机环境赋予实现定义的值。exec
系列调用将任何内容放入argv[0]
中,它也必须(并且已经)记录它。argv[0]
值的不可靠性适用于现实世界中的编程。 - dmckee --- ex-moderator kitten在使用带有exec*()
调用的Unix/Linux系统中,argv[0]
将是调用者放入exec*()
调用中的argv0
位置的内容。
Shell使用这个约定来表示程序名,大多数其他程序也遵循相同的约定,因此argv[0]
通常是程序名。
但是,一个恶意的Unix程序可以调用exec()
并将argv[0]
设置为任何它想要的值,所以无论C标准说什么,你不能百分之百地保证这一点。
argv[0]
来传达其状态:https://askubuntu.com/questions/868742 - undefinedISO-IEC 9899规定:
5.1.2.2.1 程序启动
如果
argc
的值大于零,则由argv[0]
指向的字符串表示程序名称;如果程序名称无法从主机环境中获得,则argv[0][0]
应为null字符。 如果argc
的值大于一,则由argv[1]
到argv[argc-1]
指向的字符串表示程序参数。
我还使用了:
#if defined(_WIN32)
static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
{
return GetModuleFileNameA(NULL, pathName, (DWORD)pathNameCapacity);
}
#elif defined(__linux__) /* elif of: #if defined(_WIN32) */
#include <unistd.h>
static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
{
size_t pathNameSize = readlink("/proc/self/exe", pathName, pathNameCapacity - 1);
pathName[pathNameSize] = '\0';
return pathNameSize;
}
#elif defined(__APPLE__) /* elif of: #elif defined(__linux__) */
#include <mach-o/dyld.h>
static size_t getExecutablePathName(char* pathName, size_t pathNameCapacity)
{
uint32_t pathNameSize = 0;
_NSGetExecutablePath(NULL, &pathNameSize);
if (pathNameSize > pathNameCapacity)
pathNameSize = pathNameCapacity;
if (!_NSGetExecutablePath(pathName, &pathNameSize))
{
char real[PATH_MAX];
if (realpath(pathName, real) != NULL)
{
pathNameSize = strlen(real);
strncpy(pathName, real, pathNameSize);
}
return pathNameSize;
}
return 0;
}
#else /* else of: #elif defined(__APPLE__) */
#error provide your own implementation
#endif /* end of: #if defined(_WIN32) */
然后您只需要解析该字符串以从路径中提取可执行文件名。
/proc/self/path/a.out
符号链接在 Solaris 10 及以上版本上可用。 - ephemient具有argv[0] !=
可执行文件名称的应用
许多shell通过检查argv[0][0] == '-'
来确定它们是否是登录shell。登录shell具有不同的属性,特别是它们会源一些默认文件,如/etc/profile
。
通常是init本身或getty
添加了前导-
,另请参见:https://unix.stackexchange.com/questions/299408/how-to-login-automatically-without-typing-the-root-username-or-password-in-build/300152#300152
多调用二进制文件,可能是最著名的是Busybox。这些符号链接多个名称,例如/bin/sh
和/bin/ls
到单个可执行文件/bin/busybox
,该文件从argv[0]
中识别使用哪个工具。
这使得可以拥有一个表示多个工具的单个小型静态链接可执行文件,并且将在基本上任何Linux环境中工作。
另请参见:https://unix.stackexchange.com/questions/315812/why-does-argv-include-the-program-name/315817
可运行的POSIX execve
示例,其中argv[0] !=
可执行文件名称
其他人提到了exec
,但这里是一个可运行的示例。
a.c
#define _XOPEN_SOURCE 700
#include <unistd.h>
int main(void) {
char *argv[] = {"yada yada", NULL};
char *envp[] = {NULL};
execve("b.out", argv, envp);
}
b.c
#include <stdio.h>
int main(int argc, char **argv) {
puts(argv[0]);
}
然后:
gcc a.c -o a.out
gcc b.c -o b.out
./a.out
提供:
yada yada
是的,argv[0]
也可能是:
在Ubuntu 16.10上测试通过。
argv[0]="-/bin/sh"
开始的?无论如何,在我使用过的所有机器上都是这种情况。 - ephemient
execl("/home/hacker/.hidden/malicious", "/bin/ls", "-s", (char *)0);
。可执行文件的名称与argv[0]
中的值无关。 - Jonathan LefflerCreateProcess
:CreateProcess("a.exe", "/b", ...)
,那么argv[0]
将是/b
,而不是a.exe
。 - Andry