Process Environment
0x00 Outline
0x01 Process start and termination
Process Start
int main (int argc, char *argv[])
int main (int argc, char *argv[], char *envp[])
argc 為 argument count,紀錄參數個數,程式名稱本身為第一個參數
argv 為 argument vector,會包含各個參數,最後一項是NULL
envp 為 environment variables,包含此程式的環境變數,最後一項也是NULL
Process Termination
atexit and exit Function
int atexit(void (*function)(void));
void exit(int status);
atexit
註冊自定義的 function,再呼叫 exit 前會先執行被註冊的 function
最多可註冊 32 個 function,不過 Linux 有將上限再提高
註冊的 function 是採 stack 機制,後註冊的先執行
exit
_exit and _Exit
/* atexit example */
#include <stdio.h> /* puts */
#include <stdlib.h> /* atexit */
void fnExit1 (void)
{
puts ("Exit function 1.");
}
void fnExit2 (void)
{
puts ("Exit function 2.");
}
int main ()
{
atexit (fnExit1);
atexit (fnExit2);
puts ("Main function.");
return 0;
}
/*
result:
Main function.
Exit function 2.
Exit function 1.
*/
0x02 Environment Variables
Environment Variables
一般格式為 name=value
相關指令有 env、export
使用 $
從 shell 讀取環境變數
Environment List
extern char **environ;
int main(int argc, char *argv[])
{
int count = 0;
printf("\n");
while(environ[count] != NULL)
{
printf("[%s] :: ", environ[count]);
count++;
}
return 0;
}
Environment Function
#include <stdlib.h>
char *getenv(const char *name);
int putenv(char *string);
int setenv(const char *name, const char *value, int overwrite);
int unsetenv(const char *name);
int clearenv(void);
getenv 給環境變數名稱,取得值,error on NULL
putenv 參數 string 格式為 name=value ,如果環境變數名稱不存在,則新建並加入環境變數中,若變數已存在,則修改變數值。return nonzero if OK,zero on error
setenv 基本上跟 putenv 功能一樣,不過參數 overwrite nonzero 時會覆寫數值,zero 時則不會
unsetenv 從環境變數中移除指定名稱的變數
clearenv 清除環境變數中所有的 name-value pairs,並將 global variable environ 設為 NULL
Environment List Operations
0x03 Memory layout
0x04 Shared libraries
shared libraries
現今多數 UNIX 支援 shared libraries
shared libraries 從執行檔中移出了 commom library
在記憶體維護一份 library routine 的副本,讓所有 process 可以參照,可以減少每個執行檔對記憶體的需求,但可能會產生一些 runtime overhead
shared libraries function 可以被攔截取代,但不需要 relink 每個執行檔,然而這也帶來一些安全疑慮
gcc 編譯時可加上 -static 參數,可明顯見到將所有 libraries 在編譯時直接涵蓋進來,編出來的執行檔會較大
Library Injection
Functions referenced to shared libraries can be overridden
使用 LD_PRELOAD 這個環境變數
Library injection does not work suid/sgid executables
使用 “gcc -o inject1.so -shared -fPIC inject1.c -ldl” 來將自己寫的 inject1.c 編譯成 shared library
$ LD_PRELOAD=./inject1.so ./getuid
即可看見原來的 getuid() function 被 inject1 攔截取代
//getuid.c
int main()
{
printf("UID = %d\n", getuid());
return 0;
}
//injected library, inject1.c
#include <stdio.h>
#include <sys/types.h>
uid_t getuid(void)
{
fprintf(stderr, "injected getuid, always return 0\n");
return 0;
}
More on Library Inject
#include <dlfcn.h>
void *dlopen(const char *filename, int flag);
char *dlerror(void);
void *dlsym(void *handle, const char *symbol);
int dlclose(void *handle);
dlopen() 第一個參數為 library 名稱,ex:libc.so.6
dlopen() 第二參數 flag 包含
RTLD_LAZY
RTLD_NOW
RTLD_GLOBAL
RTLD_LOCAL
RTLD_NODELETE
RTLD_NOLOAD
RTLD_DEEPBIND
如果同個 library 被 load 超過一次,則會直接回傳相同的 handle
dlopen() 回傳一個 handle,可用於 dlsym,error on NULL
dlerror() 回傳最近一次關於 dlopen()、dlsym() 或 dlclose() 遭遇的錯誤,by human readable string
dlsym() 第一個參數是 dlopen() 回傳的一個 dynamic library handle
dlsym() 第二個參數數 null-terminated symbol name,也就是被我們攔截覆寫,而我們要呼叫的原始指令
dlsym() returning the address where that symbol is loaded into memory,所以可以用一個 function pointer 儲存,之後可以調用
dlclose() 會減少 dynamic library handle 上的 reference count,當減至零,且沒有其他 library 使用其中的 symbol 時,dynamic library 會被 unload
dlclose() return 0,error on nonzero
/*
inject2.c
gcc -o inject2.so -shared -fPIC inject2.c -ldl
*/
static uid_t (*old_getuid)(void) = NULL; /* function pointer */
uid_t getuid(void)
{
if(old_getuid == NULL)
{
void *handle = dlopen("libc.so.6", RTLD_LAZY);
if(handle != NULL)
old_getuid = dlsym(handle, "getuid");
}
fprintf(stderr, "injected getuid, always return 0\n");
if(old_getuid != NULL)
fprintf(stderr, "real uid = %d\n", old_getuid());
return 0;
}
Determine Library Injection Possibility
No SUID/SGID enabled
Not a statically linked binary,library 若編譯時用 -static 直接靜態編入也無法 inject
ldd
command print shared object dependencies
nm
command list symbols from object files
strip
command discard symbols from object files.
0x05 Memory allocation
memory allocation functions
#include <stdlib.h>
void *malloc(size_t size);
void free(void *ptr);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
#include <alloca.h>
void *alloca(size_t size);
malloc() function allocates size bytes and returns a pointer to the allocated memory
malloc() memory is not initialized,if size is 0,return NULL
free() function frees the memory space pointed to by ptr,which must have been returned by a previous call tomalloc()、calloc() or realloc()
calloc() function allocates memory for an array of nmemb elements of size bytes each and returns a pointer to the allocated memory
realloc() function changes the size of the memory block pointed to by ptr to size bytes
allocation 會改變 memory heap 大小,常會以 system call sbrk(2) 處理,但實際上 free space 常保存在 malloc pool,而沒有回到 kernel
0x06 Setjmp and longjmp
setjmp and longjmp Function
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
setjmp() save stack context for nonlocal goto
setjmp() 和 longjmp 可以用來處理函式呼叫後遭遇到 error 或 interrupts
setjmp() 將 stack context/environment 儲存在 env 中,之後可供 longjmp() 使用
setjmp() return 0 if called directly, nonzero if returning from a call to longjmp
longjmp() restores the environment saved by the last call of setjmp(3) with the corresponding env argument
longjmp() 被呼叫後,程式回到前面 setjmp() 狀態與環境,longjmp() 的第二個參數 val 即是 回到前面 setjmp() 後,setjmp() 的 return value,不為 0 (即使 programmer 把 val 設為 0,longjmp() 會自動回傳 1)
Restoration of Variables
0x07 Process resource limits
Process resource limits
getrlimit and setrlimit Function
#include <sys/time.h>
#include <sys/resource.h>
int getrlimit(int resource, struct rlimit *rlim);
int setrlimit(int resource, const struct rlimit *rlim);
struct rlimit
{
rlim_t rlim_cur; /* Soft limit */
rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur) */
};
0x08 參考資料