/** * @internal * @brief Retrieves the volume label. * @param[in] hRoot handle to the * root directory. * @param[out] pointer to the structure * receiving the volume label. */ static void get_volume_label(HANDLE hRoot,winx_volume_information *v) { FILE_FS_VOLUME_INFORMATION *ffvi; int buffer_size; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS status; /* reset label */ v->label[0] = 0; /* allocate memory */ buffer_size = (sizeof(FILE_FS_VOLUME_INFORMATION) - sizeof(wchar_t)) + (MAX_PATH + 1) * sizeof(wchar_t); ffvi = winx_malloc(buffer_size); /* try to get actual label */ RtlZeroMemory(ffvi,buffer_size); status = NtQueryVolumeInformationFile(hRoot,&IoStatusBlock,ffvi, buffer_size,FileFsVolumeInformation); if(!NT_SUCCESS(status)){ strace(status,"cannot get volume label of drive %c:", v->volume_letter); winx_free(ffvi); return; } wcsncpy(v->label,ffvi->VolumeLabel,MAX_PATH); v->label[MAX_PATH] = 0; winx_free(ffvi); }
/** * @brief Queries an environment variable. * @param[in] name the environment variable name. * @return The value of the environment variable. * NULL indicates failure. * @note The returned string should be freed * by the winx_free call after its use. */ wchar_t *winx_getenv(wchar_t *name) { wchar_t *value; UNICODE_STRING n, v; NTSTATUS status; DbgCheck1(name,NULL); value = winx_malloc(MAX_ENV_VALUE_LENGTH * sizeof(wchar_t)); RtlInitUnicodeString(&n,name); v.Buffer = value; v.Length = 0; v.MaximumLength = MAX_ENV_VALUE_LENGTH * sizeof(wchar_t); status = RtlQueryEnvironmentVariable_U(NULL,&n,&v); if(!NT_SUCCESS(status)){ strace(status,"cannot query %ws",name); winx_free(value); return NULL; } if(value[0] == 0){ winx_free(value); return NULL; } return value; }
/** * @internal * @brief Saves the list of boot * execute programs to registry. * @return Zero for success, * negative value otherwise. */ static int save_boot_exec_list(struct cmd *list) { struct cmd *c; int length = 1; wchar_t *commands, *p; NTSTATUS status; for(c = list; c; c = c->next){ if(c->cmd[0]) length += (int)wcslen(c->cmd) + 1; if(c->next == list) break; } commands = winx_malloc(length * sizeof(wchar_t)); memset(commands,0,length * sizeof(wchar_t)); for(c = list, p = commands; c; c = c->next){ if(c->cmd[0]){ wcscpy(p,c->cmd); p += wcslen(c->cmd) + 1; } if(c->next == list) break; } status = RtlWriteRegistryValue(RTL_REGISTRY_CONTROL, L"Session Manager",L"BootExecute",REG_MULTI_SZ, commands,length * sizeof(wchar_t)); winx_free(commands); if(!NT_SUCCESS(status)){ strace(status,"cannot save list of boot execute commands"); return (-1); } return 0; }
/** * @brief Terminates the current thread. * @param[in] status the exit status. * @note This routine causes a small memory leak, * because it doesn't deallocate the initial stack. * On the other hand, such deallocation seems to be * not easy and even if we'll find a proper solution * for, let's say XP, we cannot guarantee its work * on other systems. */ void winx_exit_thread(NTSTATUS status) { NTSTATUS s = ZwTerminateThread(NtCurrentThread(),status); if(!NT_SUCCESS(s)){ strace(s,"cannot terminate thread"); } }
int main(int argc, char **argv) { pid_t pid; if (argc < 2) { printf("Usage: ./strace [-p] <pid | executable>\n"); return (EXIT_SUCCESS); } if (is_attach(argc, argv) == -1) { if ((pid = fork()) == 0) { xptrace(PTRACE_TRACEME, 0, NULL, 0); xexecvp(argv[1], &argv[1]); } else if (pid != -1) { print_execve_arg(argc, argv); strace(pid, 1); } else fprintf(stderr, "Error: fork failed\n"); } return (0); }
/** * @brief Gets the fully quallified path of the current module. * @details This routine is the native equivalent of GetModuleFileName. * @note The returned string should be freed by the winx_free call after its use. */ wchar_t *winx_get_module_filename(void) { PROCESS_BASIC_INFORMATION pi; NTSTATUS status; UNICODE_STRING *us; wchar_t *path; int size; RtlZeroMemory(&pi,sizeof(pi)); status = NtQueryInformationProcess(NtCurrentProcess(), ProcessBasicInformation,&pi, sizeof(pi),NULL); if(!NT_SUCCESS(status)){ strace(status,"cannot query basic process information"); return NULL; } us = &pi.PebBaseAddress->ProcessParameters->ImagePathName; size = us->MaximumLength + sizeof(wchar_t); path = winx_tmalloc(size); if(path == NULL){ mtrace(); return NULL; } memset(path,0,size); memcpy(path,us->Buffer,us->Length); return path; }
long syscall_SYS_fcntl(int fd, int cmd, ... /* arg */ ) { va_list va; va_start(va, cmd); auto ret = strace(sys_fcntl, "fcntl", fd, cmd, va); va_end(va); return ret; }
void watchAndStraceZygote() { system("rm -f %s", ZYGOTE_SYSCALL_TRACE_FILE); int pid = findZygotePid(); char *straceOutput = ZYGOTE_SYSCALL_TRACE_FILE; strace(pid, straceOutput); //printf("zygote pid is %d\n", pid); }
/** * @brief Enables a user privilege for the current process. * @param[in] luid the identifier of the requested privilege, * ntndk.h file contains definitions of various privileges. * @return Zero for success, negative value otherwise. * @par Example: * @code * winx_enable_privilege(SE_SHUTDOWN_PRIVILEGE); * @endcode */ int winx_enable_privilege(unsigned long luid) { NTSTATUS status; SIZE_T WasEnabled; /* boolean value receiving the previous state */ status = RtlAdjustPrivilege((SIZE_T)luid, TRUE, FALSE, &WasEnabled); if(!NT_SUCCESS(status)){ strace(status,"cannot enable privilege %x",(UINT)luid); return (-1); } return 0; }
/** * @brief Releases a mutex. * @param[in] h the mutex handle. * @return Zero for success, * negative value otherwise. */ int winx_release_mutex(HANDLE h) { NTSTATUS status; DbgCheck1(h,-1); status = NtReleaseMutant(h,NULL); if(!NT_SUCCESS(status)){ strace(status,"cannot release mutex"); return (-1); } return 0; }
/** * @brief Creates a thread and starts them. * @param[in] start_addr the starting address of the thread. * @param[in] parameter pointer to the data passed to the thread routine. * @return Zero for success, negative value otherwise. * @note Look at the following example for the thread function prototype. * @par Example: * @code * DWORD WINAPI thread_proc(LPVOID parameter) * { * // do something * winx_exit_thread(0); * return 0; * } * winx_create_thread(thread_proc,NULL); * @endcode */ int winx_create_thread(PTHREAD_START_ROUTINE start_addr,PVOID parameter) { NTSTATUS status; HANDLE hThread; DbgCheck1(start_addr,-1); status = RtlCreateUserThread(NtCurrentProcess(),NULL, 0,0,0,0,start_addr,parameter,&hThread,NULL); if(!NT_SUCCESS(status)){ strace(status,"cannot create thread"); return (-1); } NtCloseSafe(hThread); return 0; }
/** * @internal * @brief Retrieves the list of * registered boot execute programs. * @return Zero for success, * negative value otherwise. */ static int get_boot_exec_list(struct cmd **list) { NTSTATUS status; RTL_QUERY_REGISTRY_TABLE qt[] = { {query_routine, 0, L"BootExecute", NULL, REG_SZ, L"", 0}, {NULL, 0, NULL, NULL, 0, NULL, 0} }; status = RtlQueryRegistryValues(RTL_REGISTRY_CONTROL, L"Session Manager",qt,(PVOID)list,NULL); if(!NT_SUCCESS(status)){ strace(status,"cannot get list of boot execute commands"); return (-1); } return 0; }
/** * @internal * @brief Retrieves the drive geometry. * @param[in] hRoot handle to the * root directory. * @param[out] pointer to the structure * receiving the drive geometry. * @return Zero for success, negative * value otherwise. */ static int get_drive_geometry(HANDLE hRoot,winx_volume_information *v) { FILE_FS_SIZE_INFORMATION ffs; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS status; WINX_FILE *f; DISK_GEOMETRY dg; char buffer[32]; /* get drive geometry */ RtlZeroMemory(&ffs,sizeof(FILE_FS_SIZE_INFORMATION)); status = NtQueryVolumeInformationFile(hRoot,&IoStatusBlock,&ffs, sizeof(FILE_FS_SIZE_INFORMATION),FileFsSizeInformation); if(!NT_SUCCESS(status)){ strace(status,"cannot get geometry of drive %c:",v->volume_letter); return (-1); } /* fill all geometry related fields of the output structure */ v->total_bytes = (ULONGLONG)ffs.TotalAllocationUnits.QuadPart * \ ffs.SectorsPerAllocationUnit * ffs.BytesPerSector; v->free_bytes = (ULONGLONG)ffs.AvailableAllocationUnits.QuadPart * \ ffs.SectorsPerAllocationUnit * ffs.BytesPerSector; v->total_clusters = (ULONGLONG)ffs.TotalAllocationUnits.QuadPart; v->bytes_per_cluster = ffs.SectorsPerAllocationUnit * ffs.BytesPerSector; v->sectors_per_cluster = ffs.SectorsPerAllocationUnit; v->bytes_per_sector = ffs.BytesPerSector; /* optional: get device capacity */ v->device_capacity = 0; f = winx_vopen(v->volume_letter); if(f != NULL){ if(winx_ioctl(f,IOCTL_DISK_GET_DRIVE_GEOMETRY, "get_drive_geometry: device geometry request",NULL,0, &dg,sizeof(dg),NULL) >= 0){ v->device_capacity = dg.Cylinders.QuadPart * \ dg.TracksPerCylinder * dg.SectorsPerTrack * dg.BytesPerSector; winx_bytes_to_hr(v->device_capacity,1,buffer,sizeof(buffer)); itrace("%c: device capacity = %s",v->volume_letter,buffer); } winx_fclose(f); } return 0; }
/** * @brief Opens a named mutex. * @param[in] name the mutex name. * @param[out] phandle pointer to the mutex handle. * @return Zero for success, negative value otherwise. * @par Example: * @code * HANDLE h; * winx_open_mutex(L"\\BaseNamedObjects\\ultradefrag_mutex",&h); * @endcode */ int winx_open_mutex(wchar_t *name,HANDLE *phandle) { UNICODE_STRING us; NTSTATUS status; OBJECT_ATTRIBUTES oa; DbgCheck2(name,phandle,-1); *phandle = NULL; RtlInitUnicodeString(&us,name); InitializeObjectAttributes(&oa,&us,0,NULL,NULL); status = NtOpenMutant(phandle,MUTEX_ALL_ACCESS,&oa); if(!NT_SUCCESS(status)){ *phandle = NULL; strace(status,"cannot open %ws",name); return (-1); } return 0; }
void Tracer::trace(TraceLevel level, const char* method, const char* message, ...) const { // immediately return if the current set tracelevel is higher if (m_tracelevel > level) { return; } ///@todo with qt4 the follwing code can be used: /* va_list ap; va_start(ap, message); QString str; str.vsprintf(message, ap); va_end(ap); strace(level, method) << str << endl; */ va_list ap; int maxLength = 1024; int result = maxLength; char* str; while (result >= maxLength) { va_start(ap, message); str = (char*)malloc(maxLength); result = vsnprintf(str, maxLength-1, message, ap); va_end(ap); if (result >= maxLength) { delete str; maxLength = 2 * maxLength; result = maxLength; } } strace(level, method) << str << endl; delete str; }
/** * @internal * @brief Opens root directory of the volume. * @param[in] volume_letter the volume letter. * @return File handle, NULL indicates failure. */ static HANDLE OpenRootDirectory(unsigned char volume_letter) { wchar_t rootpath[] = L"\\??\\A:\\"; HANDLE hRoot; NTSTATUS status; UNICODE_STRING us; OBJECT_ATTRIBUTES ObjectAttributes; IO_STATUS_BLOCK IoStatusBlock; rootpath[4] = (wchar_t)winx_toupper(volume_letter); RtlInitUnicodeString(&us,rootpath); InitializeObjectAttributes(&ObjectAttributes,&us, FILE_READ_ATTRIBUTES,NULL,NULL); status = NtCreateFile(&hRoot,FILE_GENERIC_READ, &ObjectAttributes,&IoStatusBlock,NULL,0, FILE_SHARE_READ|FILE_SHARE_WRITE,FILE_OPEN,0, NULL,0); if(!NT_SUCCESS(status)){ strace(status,"cannot open %ls",rootpath); return NULL; } return hRoot; }
/** * @brief Creates a named mutex. * @param[in] name the mutex name. * @param[out] phandle pointer to the mutex handle. * @return Zero for success, negative value otherwise. * @par Example: * @code * HANDLE h; * winx_create_mutex(L"\\BaseNamedObjects\\ultradefrag_mutex",&h); * @endcode */ int winx_create_mutex(wchar_t *name,HANDLE *phandle) { UNICODE_STRING us; NTSTATUS status; OBJECT_ATTRIBUTES oa; DbgCheck2(name,phandle,-1); *phandle = NULL; RtlInitUnicodeString(&us,name); InitializeObjectAttributes(&oa,&us,0,NULL,NULL); status = NtCreateMutant(phandle,MUTEX_ALL_ACCESS,&oa,0); if(status == STATUS_OBJECT_NAME_COLLISION){ itrace("%ws already exists",name); status = NtOpenMutant(phandle,MUTEX_ALL_ACCESS,&oa); } if(!NT_SUCCESS(status)){ *phandle = NULL; strace(status,"cannot create/open %ws",name); return (-1); } return 0; }
/** * @brief Sets an environment variable. * @param[in] name the environment variable name. * @param[in] value the null-terminated value string. * NULL pointer causes a variable deletion. * @return Zero for success, negative value otherwise. * @note value buffer size must not exceed 32767 characters, * including terminal zero, as mentioned in MSDN. This is * because unsigned short data type can hold numbers * less than or equal to 32767. */ int winx_setenv(wchar_t *name, wchar_t *value) { UNICODE_STRING n, v; NTSTATUS status; DbgCheck1(name,-1); RtlInitUnicodeString(&n,name); if(value){ if(value[0]){ RtlInitUnicodeString(&v,value); status = RtlSetEnvironmentVariable(NULL,&n,&v); } else { status = RtlSetEnvironmentVariable(NULL,&n,NULL); } } else { status = RtlSetEnvironmentVariable(NULL,&n,NULL); } if(!NT_SUCCESS(status)){ strace(status,"cannot set %ws",name); return (-1); } return 0; }
/** * @internal * @brief Retrieves the name of the file system. * @param[in] hRoot handle to the root directory. * @param[out] pointer to the structure receiving * the filesystem name. * @return Zero for success, negative value otherwise. * @note We could analyze the first sector of the * partition directly, but this method is not so swift * as it accesses the disk physically. */ static int get_filesystem_name(HANDLE hRoot,winx_volume_information *v) { FILE_FS_ATTRIBUTE_INFORMATION *pfa; int fs_attr_info_size; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS status; wchar_t fs_name[MAX_FS_NAME_LENGTH + 1]; int length; fs_attr_info_size = MAX_PATH * sizeof(WCHAR) + sizeof(FILE_FS_ATTRIBUTE_INFORMATION); pfa = winx_malloc(fs_attr_info_size); RtlZeroMemory(pfa,fs_attr_info_size); status = NtQueryVolumeInformationFile(hRoot,&IoStatusBlock,pfa, fs_attr_info_size,FileFsAttributeInformation); if(!NT_SUCCESS(status)){ strace(status,"cannot get file system name of drive %c:",v->volume_letter); winx_free(pfa); return (-1); } /* * pfa->FileSystemName.Buffer may be not NULL terminated * (theoretically), so name extraction is more tricky * than it should be. */ length = min(MAX_FS_NAME_LENGTH,pfa->FileSystemNameLength / sizeof(wchar_t)); wcsncpy(fs_name,pfa->FileSystemName,length); fs_name[length] = 0; _snprintf(v->fs_name,MAX_FS_NAME_LENGTH,"%ws",fs_name); v->fs_name[MAX_FS_NAME_LENGTH] = 0; /* cleanup */ winx_free(pfa); return 0; }
long syscall_SYS_fchown(int fd, uid_t owner, gid_t group) { return strace(sys_fchown, "fchown", fd, owner, group); }
void syscall_SYS_exit(int status) { strace(sys_exit, "exit", status); }
long syscall_SYS_mremap(void *old_address, size_t old_size, size_t new_size, int flags, void *new_address) { return strace(sys_mremap, "mremap", old_address, old_size, new_size, flags, new_address); }
ssize_t socketcall_sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen) { return strace(sock_sendto, "sendto", sockfd, buf, len, flags, dest_addr, addrlen); }
long syscall_SYS_setrlimit(int resource, const struct rlimit* rlim) { return strace(sys_setrlimit, "setrlimit", resource, rlim); }
long socketcall_shutdown(int sockfd, int how) { return strace(sock_shutdown, "shutdown", sockfd, how); }
long socketcall_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen) { return strace(sock_accept, "accept", sockfd, addr, addrlen); }
long socketcall_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { return strace(sock_bind, "bind", sockfd, addr, addrlen); }
ssize_t socketcall_recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen) { return strace(sock_recvfrom, "recvfrom", sockfd, buf, len, flags, src_addr, addrlen); }
long socketcall_listen(int sockfd, int backlog) { return strace(sock_listen, "listen", sockfd, backlog); }
long socketcall_sendmsg(int sockfd, const struct msghdr *msg, int flags) { return strace(sock_sendmsg, "sendmsg", sockfd, msg, flags); }