情况如下:
我正在尝试在github中执行攻击内核的project。内核版本是linux-3.18.6。
QEMU用于模拟环境。
在我的应用程序中,我尝试通过遵循它们来理解系统调用过程。完成我的目标的方法就像shell程序一样。我只是创建一些命令来运行相对的系统调用。也许它在图片中很简单。 some commands
代码很简单如下:
1使用API getpid。
int Getpid(int argc, char **argv)
{
pid_t pid;
pid = getpid();
printf("current process's pid:%d\n",pid);
return 0;
}
2直接使用int $ 0x80。
int GetpidAsm(int argc, char **argv)
{
pid_t pid;
asm volatile(
"mov $20, %%eax\n\t"
"int $0x80\n\t"
"mov %%eax, %0\n\t"
:"=m"(pid)
);
printf("current process's pid(ASM):%d\n",pid);
return 0;
}
因为我的应用程序只是在pid 1的进程中运行,所以每次输入命令getpid时,它都会返回1.当然这是真的。
奇怪的是,当我使用gdb来调试syscall进程时,当我输入getpid来执行时,它只在berakpoint sys_getpid停止一次。当我一次又一次地这样做时,它只是输出而不停止。
显然,正如我所理解的那样,使用int $ 0x80绝对是正确的。
为解决这个问题,我做了一些研究。 我下载了glibc源代码(glibc-2.25),看看api getpid是如何包装int $ 0x80的。不幸的是,它没有,或者我没有找到合适的位置。
glibc中的一些代码。
pid_t getpid(void)
{
pid_t (*f)(void);
f = (pid_t (*)(void)) dlsym (RTLD_NEXT, "getpid");
if (f == NULL)
error (EXIT_FAILURE, 0, "dlsym (RTLD_NEXT, \"getpid\"): %s", dlerror ());
return (pid2 = f()) + 26;
}
如果我输错了代码,请告诉我,tks。
如代码所示,glibc中不包含getpid的定义。在阅读了一些数据后,有人说the VDSO...。
请注意,AFAIK是简单系统调用成本的重要组成部分 从用户空间到内核并返回。因此,对于一些系统调用 (可能是gettimeofday,getpid ......)VDSO可能会避免这种情况 (技术上可能会避免做真正的系统调用)。
男人getpid pgae:
C库/内核差异 自glibc 2.3.4版以来,getpid()的glibc包装函数 缓存PID,以避免在进程时进行额外的系统调用 反复调用getpid()。通常这种缓存是不可见的,但它是 正确的操作依赖于包装函数中的支持 fork(2),vfork(2)和clone(2):如果应用程序绕过glibc 这些系统调用的包装器使用syscall(2),然后调用 子进程中的getpid()将返回错误的值(确切地说:它 将返回父进程的PID)。另见clone(2)for dis- 激发getpid()可能返回错误值的情况 通过glibc包装函数调用clone(2)。
虽然退出了这么多解释,但我无法弄清楚API getpid的工作程序。
作为对比,API时间很容易理解。 时间的定义:
time_t
time (time_t *t)
{
INTERNAL_SYSCALL_DECL (err);
time_t res = INTERNAL_SYSCALL (time, err, 1, NULL);
/* There cannot be any error. */
if (t != NULL)
*t = res;
return res;
}
然后,
#define INTERNAL_SYSCALL(name, err, nr, args...) \
internal_syscall##nr ("li\t%0, %2\t\t\t# " #name "\n\t", \
"IK" (SYS_ify (name)), \
0, err, args)
最后,它嵌入了asm,这是使用内核源代码的常用方法。
#define internal_syscall1(v0_init, input, number, err, arg1) \
({ \
long _sys_result; \
\
{ \
register long __s0 asm ("$16") __attribute__ ((unused)) \
= (number); \
register long __v0 asm ("$2"); \
register long __a0 asm ("$4") = (long) (arg1); \
register long __a3 asm ("$7"); \
__asm__ volatile ( \
".set\tnoreorder\n\t" \
v0_init \
"syscall\n\t" \
".set reorder" \
: "=r" (__v0), "=r" (__a3) \
: input, "r" (__a0) \
: __SYSCALL_CLOBBERS); \
err = __a3; \
_sys_result = __v0; \
} \
_sys_result; \
})
有人可以清楚地解释API getpid是如何工作的吗?为什么getpid只陷入syscall sys_getpid一次?如果可能的话,一些参考文献是值得赞赏的。
感谢您的帮助。
答案 0 :(得分:2)
首先请注意,glibc源代码几乎不可能导航。
文档说明getpid()会缓存其结果,正如您所注意到的那样。 您找到的代码看起来像
pid_t getpid(void)
{
pid_t (*f)(void);
f = (pid_t (*)(void)) dlsym (RTLD_NEXT, "getpid");
if (f == NULL)
error (EXIT_FAILURE, 0, "dlsym (RTLD_NEXT, \"getpid\"): %s", dlerror ());
return (pid2 = f()) + 26;
}
仅仅是一个包装器。它会查找getpid
符号,并调用该函数。
该功能是您需要找到的。它是__getpid()函数的别名,你可以在sysdeps/unix/sysv/linux/getpid.c
文件中找到它,并且也会显示在这篇文章的底部。
现在 - 您可能正在查看与您当前的glibc不匹配的glibc源代码 - 就this commit中2016年11月的getpid()缓存而言,有一个很大的变化,据我所知变更将是2017年2月发布的glibc-2.25的一部分
缓存其值以避免多次调用getpid()系统调用的较旧的getpid()实现可以在这里看到: http://repo.or.cz/glibc.git/blob/93eb85ceb25ee7aff432ddea0abf559f53d7a5fc:/sysdeps/unix/sysv/linux/getpid.c看起来像
static inline __attribute__((always_inline)) pid_t
really_getpid (pid_t oldval)
{
if (__glibc_likely (oldval == 0))
{
pid_t selftid = THREAD_GETMEM (THREAD_SELF, tid);
if (__glibc_likely (selftid != 0))
return selftid;
}
INTERNAL_SYSCALL_DECL (err);
pid_t result = INTERNAL_SYSCALL (getpid, err, 0);
/* We do not set the PID field in the TID here since we might be
called from a signal handler while the thread executes fork. */
if (oldval == 0)
THREAD_SETMEM (THREAD_SELF, tid, result);
return result;
}
#endif
pid_t
__getpid (void)
{
#if !IS_IN (libc)
INTERNAL_SYSCALL_DECL (err);
pid_t result = INTERNAL_SYSCALL (getpid, err, 0);
#else
pid_t result = THREAD_GETMEM (THREAD_SELF, pid);
if (__glibc_unlikely (result <= 0))
result = really_getpid (result);
#endif
return result;
}
libc_hidden_def (__getpid)
weak_alias (__getpid, getpid)
libc_hidden_def (getpid)