/* * UltraDefrag - powerful defragmentation tool for Windows NT. * Copyright (c) 2011 by Jean-Pierre Andre for the Linux version * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * Windows calls emulations * * Do not import symbols from libntfs-3g, relay to ntfs-3g.c * * Trace levels : * 1 : debug : only major errors * 2 : expert : major library and system calls * 3 : forensic : details on system calls * levels 1 and 2 should be much similar to the ones on Windows */ #include "compiler.h" #include "linux.h" #include #include //#include #include #include #include //#include #include #include #include #include #ifndef UNTHREADED #include #endif #include "zenwinx.h" #include "ntfs.h" #include "ntfs-3g.h" #include "extrawin.h" #if STSC #undef USETIMEOFDAY #else #define USETIMEOFDAY 1 #endif #define STACKSZ 131072 /* stack size */ #define PBUFSZ MAX_PATH /* temporary print buffer size */ /* * Declarations which should be shared hackwin/linux */ #define TIMEUNIT 10000000 /* number of time units (100ns) in a second */ struct TRANSLATIONS { struct TRANSLATIONS *greater; struct TRANSLATIONS *less; const char *source; utf16_t translation[1]; } ; #ifndef UNTHREADED struct THREADLIST { struct THREADLIST *next; PTHREAD_START_ROUTINE start; PVOID param; ULONG_PTR minstack; ULONG_PTR maxstack; int tid; int state; pthread_t thread; } ; #define THREADCREATED 1 #define THREADSTARTED 2 #define THREADTERMINATED 4 #define THREADCLOSED 8 struct EVENTLIST { struct EVENTLIST *next; const char *name; } ; #endif void showstack(const char*); //void zdump(FILE*, const char*, const char*, int); void abort(void); extern int trace; static char *devicename; /* name of NTFS device */ static HANDLE devicehandle; /* handle to device (ntfs_volume*) */ static int devicecount = 0; /* current count of openings */ static struct TRANSLATIONS *translations = (struct TRANSLATIONS*)NULL; #if STSC char *getenv(const char*); int setenv(const char*, const char*, int); #endif #if (LXSC | L8SC | STSC | SPGC | ARGC) & !NOMEMCHK void *ntfs_mymalloc(size_t, const char*, int); /* extra relay desirable */ void *ntfs_mycalloc(size_t,size_t, const char*, int); /* extra relay desirable */ void *ntfs_myrealloc(void*,size_t, const char*, int); /* extra relay desirable */ void ntfs_myfree(void*, const char*, int); /* extra relay desirable */ #define malloc(sz) ntfs_mymalloc(sz,__FILE__,__LINE__) #define calloc(sz,cnt) ntfs_mycalloc(sz,cnt,__FILE__,__LINE__) #define realloc(old,sz) ntfs_myrealloc(old,sz,__FILE__,__LINE__) #define free(old) ntfs_myfree(old,__FILE__,__LINE__) #else /* LXSC | L8SC | STSC | SPGC | ARGC */ void *ntfs_malloc(size_t); void *ntfs_calloc(size_t); void *ntfs_realloc(size_t,void*); #define malloc(sz) ntfs_malloc(sz) #define calloc(sz,cnt) ntfs_calloc(sz*cnt) #define realloc(sz,old) ntfs_realloc(sz,old) #endif /* LXSC | L8SC | STSC | SPGC | ARGC */ #ifndef UNTHREADED pthread_mutex_t loglock; pthread_mutex_t threadlock; pthread_mutex_t eventlock; struct THREADLIST *threadlist; struct EVENTLIST *eventlist; #endif static void undefined(const char *name) { fprintf(stderr,"Undefined function %s()\n",name); #ifdef CURSES curs_set(1); endwin(); #endif fflush(stdout); fflush(stderr); #if STSC exit(1); #else abort(); #endif } #define UNDEF(name) int name() { undefined(#name); return (0); } #define WUNDEF(name) int __stdcall name() { undefined(#name); return (0); } #define RUNDEF(name) { undefined(#name); return (0); } #define VUNDEF(name) { undefined(#name); } void showstack(const char *text) { fprintf(stderr,"Stack at %s : 0x%lx\n",text,&text); } /* * Allocate a temporary buffer * to only be used during a printf or system call */ static char *nextbuf(void) { static char buf[PBUFSZ*8]; static int n = 0; return (&buf[(++n & 7)*PBUFSZ]); } const char *calledfrom(void *firstarg) { static char buf[50]; #if L8SC | PPGC | SPGC | ARGC /* computers which do not push the return addr to stack */ sprintf(buf,"called from undetermined"); #else unsigned long *p; p = (unsigned long*)firstarg; sprintf(buf,"called from 0x%lx",*--p); #endif return (buf); } void safe_dump(FILE *f, const char *text, const char *zone, int cnt) { int i,j; #ifndef UNTHREADED pthread_mutex_lock(&loglock); #endif fprintf(stderr,"Dump of %s %d bytes at 0x%lx",text,cnt,(long)zone); #if STSC | PMSC | PPGC | SPGC fprintf(stderr," (big endian)\n"); #else fprintf(stderr," (little endian)\n"); #endif #ifndef UNTHREADED pthread_mutex_unlock(&loglock); #endif for (i=0; i= ' ') && (zone[i+j] < 0x7f) ? zone[i+j] : '.')); fprintf(stderr,"\n"); #ifndef UNTHREADED pthread_mutex_unlock(&loglock); #endif } } /* * Serialized logging */ int safe_fprintf(FILE *f, const char *format, ...) { int r; va_list ap; va_start(ap,format); #ifdef UNTHREADED r = vfprintf(f,format,ap); #else pthread_mutex_lock(&loglock); r = vfprintf(f,format,ap); pthread_mutex_unlock(&loglock); #endif va_end(ap); return (r); } void WgxDbgPrintLastError(const char *format, ...) { va_list ap; va_start(ap,format); #ifdef UNTHREADED vfprintf(stderr,format,ap); fprintf(stderr,"\n"); #else pthread_mutex_lock(&loglock); vfprintf(stderr,format,ap); fprintf(stderr,"\n"); pthread_mutex_unlock(&loglock); #endif va_end(ap); } /* * Time functions */ SLONGLONG currenttime(void) { SLONGLONG units; struct timespec now; #ifdef USETIMEOFDAY struct timeval microseconds; gettimeofday(µseconds, (struct timezone*)NULL); now.tv_sec = microseconds.tv_sec; now.tv_nsec = microseconds.tv_usec*1000; #else now.tv_sec = time((time_t*)NULL); now.tv_nsec = 0; #endif units = (SLONGLONG)now.tv_sec * TIMEUNIT + ((SLONGLONG)(369 * 365 + 89) * 24 * 3600 * TIMEUNIT) + now.tv_nsec/100; return (units); } VOID NTAPI RtlTimeToTimeFields(PLARGE_INTEGER time,PTIME_FIELDS fields) { DWORD days; DWORD millisecs; unsigned int year; int weekday; int mon; int cnt; days = time->QuadPart/(86400LL*TIMEUNIT); millisecs = (time->QuadPart - days*(86400LL*TIMEUNIT))/(TIMEUNIT/1000); weekday = (days + 1) % 7; year = 1601; /* periods of 400 years */ cnt = days/146097; days -= 146097*cnt; year += 400*cnt; /* periods of 100 years */ cnt = (3*days + 3)/109573; days -= 36524*cnt; year += 100*cnt; /* periods of 4 years */ cnt = days/1461; days -= 1461*cnt; year += 4*cnt; /* periods of a single year */ cnt = (3*days + 3)/1096; days -= 365*cnt; year += cnt; if ((!(year % 100) ? (year % 400) : (year % 4)) && (days > 58)) days++; if (days > 59) { mon = (5*days + 161)/153; days -= (153*mon - 162)/5; } else { mon = days/31 + 1; days -= 31*(mon - 1) - 1; } fields->Year = year; fields->Month = mon; fields->Day = days; fields->Weekday = weekday; fields->Hour = millisecs / 3600000; fields->Minute = (millisecs / 60000) % 60; fields->Second = (millisecs / 1000) % 60; fields->Milliseconds = millisecs % 1000; if (trace > 2) safe_fprintf(stderr,"%d/%d/%d %02d:%02d:%02d wday %d\n",(int)fields->Day,(int)fields->Month,(int)fields->Year,(int)fields->Hour,(int)fields->Minute,(int)fields->Second,(int)fields->Weekday); } NTSTATUS NTAPI RtlSystemTimeToLocalTime(const LARGE_INTEGER *SystemTime, PLARGE_INTEGER LocalTime) { #if STSC LocalTime->QuadPart = SystemTime->QuadPart; #else long days; time_t seconds; struct tm *ptm; LocalTime->QuadPart = SystemTime->QuadPart; days = SystemTime->QuadPart/(86400LL*TIMEUNIT) - 134774; if ((days > -24850) && (days < 24850)) { /* get local time the same day at 12:00 UTC */ seconds = days*86400 + 43200; ptm = localtime(&seconds); LocalTime->QuadPart += (ptm->tm_hour - 12)*3600LL*TIMEUNIT; } #endif return (STATUS_SUCCESS); } /* * Translate from utf16 for outputing text. * It is wise to reserve a target size at least three times * the incnt to avoid overflow when converting to utf8 */ int ntfs_toutf8(char *out, const utf16_t *in, int incnt) { char *t; const char *cin; int i, size; utf16_t halfpair; halfpair = 0; t = out; cin = (char*)in; size = 0; for (i = 0; i < incnt; i++) { unsigned short c = (cin[2*i] & 255) + ((cin[2*i+1] & 255) << 8); if (halfpair) { if ((c >= 0xdc00) && (c < 0xe000)) { *t++ = 0xf0 + (((halfpair + 64) >> 8) & 7); *t++ = 0x80 + (((halfpair + 64) >> 2) & 63); *t++ = 0x80 + ((c >> 6) & 15) + ((halfpair & 3) << 4); *t++ = 0x80 + (c & 63); halfpair = 0; } else goto fail; } else if (c < 0x80) { *t++ = c; } else { if (c < 0x800) { *t++ = (0xc0 | ((c >> 6) & 0x3f)); *t++ = 0x80 | (c & 0x3f); } else if (c < 0xd800) { *t++ = 0xe0 | (c >> 12); *t++ = 0x80 | ((c >> 6) & 0x3f); *t++ = 0x80 | (c & 0x3f); } else if (c < 0xdc00) halfpair = c; else if (c >= 0xe000) { *t++ = 0xe0 | (c >> 12); *t++ = 0x80 | ((c >> 6) & 0x3f); *t++ = 0x80 | (c & 0x3f); } else goto fail; } } fail : return (t - out); } /* * Translate to utf16le * * The incnt is the number of bytes (may contain a '\0') * Returns the number of utf16 little endian chars. */ int ntfs_toutf16(utf16_t *out, const char *in, int incnt) { int size; unsigned int code; char *target; int c; int i; enum { BASE, TWO, THREE, THREE2, THREE3, FOUR, FOUR2, FOUR3, ERR } state; i = 0; target = (char*)out; size = 0; c = 0; state = BASE; while (i < incnt) { c = in[i++] & 255; switch (state) { case BASE : if (!(c & 0x80)) { *target++ = c; *target++ = 0; size++; } else { if (c < 0xc2) state = ERR; else if (c < 0xe0) { code = c & 31; state = TWO; } else if (c < 0xf0) { code = c & 15; if (c == 0xe0) state = THREE2; else if (c == 0xed) state = THREE3; else state = THREE; } else if (c < 0xf8) { code = c & 7; state = FOUR; } else state = ERR; } break; case TWO : if ((c & 0xc0) != 0x80) state = ERR; else { *target++ = ((code & 3) << 6) + (c & 63); *target++ = ((code >> 2) & 255); size++; state = BASE; } break; case THREE : if ((c & 0xc0) != 0x80) state = ERR; else { code = ((code & 15) << 6) + (c & 63); state = TWO; } break; case THREE2 : if ((c & 0xe0) != 0xa0) state = ERR; else { code = ((code & 15) << 6) + (c & 63); state = TWO; } break; case THREE3 : if ((c & 0xe0) != 0x80) state = ERR; else { code = ((code & 15) << 6) + (c & 63); state = TWO; } break; case FOUR : if ((c & 0xc0) != 0x80) state = ERR; else { code = (code << 6) + (c & 63); state = FOUR2; } break; case FOUR2 : if ((c & 0xc0) != 0x80) state = ERR; else { code = (code << 6) + (c & 63); state = FOUR3; } break; case FOUR3 : if ((code > 0x43ff) || (code < 0x400) || ((c & 0xc0) != 0x80)) state = ERR; else { *target++ = ((code - 0x400) >> 4) & 255; *target++ = 0xd8 + (((code - 0x400) >> 12) & 3); *target++ = ((code & 3) << 6) + (c & 63); *target++ = 0xdc + ((code >> 2) & 3); size += 2; state = BASE; } break; case ERR : break; } } if (state != BASE) { /* error */ size = 0; *target++ = 0; *target++ = 0; } return (size); } char *_strlwr(char *strg) { int i; /* replaces original */ for (i=0; strg[i]; i++) if ((strg[i] >= 'A') && (strg[i] <= 'Z')) strg[i] += 'a' - 'A'; return (strg); } /* * Translate a constant UTF8 string to UTF16 * * The input MUST be a static string, we compare pointers to * original strings. */ const utf16_t *UTF16(const char *in) { utf16_t *out; struct TRANSLATIONS *current; struct TRANSLATIONS **link; static int num = 0; if (in) { if (trace > 2) safe_fprintf(stderr,"UTF16 %s\n",in); current = translations; link = &translations; if (current) { do { if (in > current->source) { link = ¤t->greater; current = current->greater; } else if (in < current->source) { link = ¤t->less; current = current->less; } } while (current && (in != current->source)); } if (!current) { current = (struct TRANSLATIONS*)malloc (sizeof(struct TRANSLATIONS) + 2*strlen(in)); if (current) { out = current->translation; ntfs_toutf16(out,in,strlen(in) + 1); current->source = in; current->greater = (struct TRANSLATIONS*)NULL; current->less = (struct TRANSLATIONS*)NULL; *link = current; num++; if (trace > 2) safe_fprintf(stderr,"allocated num %u string %s\n",num,in); } else { out = (utf16_t*)NULL; safe_fprintf(stderr,"** No more memory in UTF16\n"); get_out(1); } } else out = current->translation; } else { if (trace > 2) safe_fprintf(stderr,"UTF16 null in\n"); out = (utf16_t*)NULL; } return (out); } static void freetranslation(struct TRANSLATIONS *tr) { if (tr->greater) freetranslation(tr->greater); if (tr->less) freetranslation(tr->less); free(tr); } /* * Output formatting routines */ /* * Build a relative path from a prefix "\??\x:\" * where x matches the current open device * A '/' separates the directory levels, and "//" announces * an inner stream. * * result returned in a circular buffer */ const utf_t *printablepath(const utf_t *p, int l) { char *buf; const char *r; int i; r = "???"; /* default return, never NULL */ if (p) { if (!l) l = strlen(p); if ((l >= 7) && !strncmp(p,"\\??\\",4) && (p[4] >= 'A') && (p[4] <= 'Z') && (p[5] == ':') && (p[6] == '\\')) { /* prefix : must match open device */ if ((devicecount > 0) && !strcmp(volumes[p[4]],devicename) && (l < (PBUFSZ + 6))) { buf = nextbuf(); buf[0] = '/'; for (i=7; i 2) safe_fprintf(stderr,"Returning buf %s\n",buf); va_end(ap); return (sz); } char *adjformat(char *target, const char *source) { const char *s; char *t; char c; enum { BEGIN, PERCENT, I, I6, L, W } state; state = BEGIN; s = source; t = target; while ((c = *s++)) { switch (state) { case BEGIN : if (c == '%') state = PERCENT; *t++ = c; break; case PERCENT : switch (c) { case 'w' : state = W; break; case 'I' : state = I; break; default : if (((c < '0') || (c > '9')) && (c != '.') || (c != '*')) state = BEGIN; *t ++ = c; break; } case I : if (c != '6') { *t++ = 'I'; *t++ = c; state = BEGIN; } else state = I6; break; case I6 : if (c != '6') { *t++ = 'I'; *t++ = '6'; *t++ = c; } else { *t++ = 'l'; *t++ = 'l'; } state = BEGIN; break; case L : if (c != 's') *t++ = 'l'; *t++ = c; state = BEGIN; break; case W : if (c != 's') *t++ = 'w'; *t++ = c; state = BEGIN; break; } } return (target); } int _snprintf(utf_t *buf, int size, const utf_t *format, ...) { va_list ap; int sz; const utf16_t *p; if (trace > 2) safe_fprintf(stderr,"_snprintf format %s allowed size %d\n",format,(int)size); va_start(ap,format); if (!strcmp(format,"%ws")) { /* just translating to utf8 */ /* not used any more */ p = va_arg(ap,utf16_t*); sz = utf16len(p) + 1; if (size < 3*sz) { safe_fprintf(stderr,"** Unsafe translation sz %d size %d\n",(int)sz,(int)size); } sz = ntfs_toutf8(buf,p,sz); } else { if (strstr(format,"%ws") || strstr(format,"%ls")) safe_fprintf(stderr,"** Unsupported format %s\n",format); sz = vsnprintf(buf, size, format, ap); } va_end(ap); if (trace > 2) safe_fprintf(stderr,"_snprintf buf %s\n",buf); return (sz); } int _vsnprintf(char *buf, size_t size, const char * format, va_list args) { size_t sz; if (trace > 2) { #if ARGC safe_fprintf(stderr,"_vsnprintf size 0x%lx format %s args 0x%lx %s\n",size,format, &args, calledfrom(&buf)); #else safe_fprintf(stderr,"_vsnprintf size 0x%lx format %s args 0x%lx %s\n",size,format,(args ? *(char**)args : (char*)NULL),calledfrom(&buf)); #endif safe_fprintf(stderr,"%s\n",calledfrom(&buf)); } if (strstr(format,"%ws") || strstr(format,"%ls")) { safe_fprintf(stderr,"** Undesired format %s\n",format); get_out(1); } sz = vsnprintf(buf,size,format,args); if (trace > 2) safe_fprintf(stderr,"returning buf %s\n",buf); return (sz); } void winx_print(char *string) VUNDEF(winx_print) int __cdecl winx_printf(const char *format, ...) RUNDEF(winx_printf) long long _atoi64(const char *s) { long long v; BOOLEAN neg; v = 0; neg = FALSE; while (*s == ' ') s++; if (*s == '-') { neg = TRUE; s++; } while ((*s >= '0') && (*s <= '9')) v = v*10 + (*s++) - '0'; if (neg) v = -v; return (v); } /* * Operations on utf16le strings */ /* * Translate to lower case * Only needed for comparing against plain ascii chars, * no need to assemble a full Unicode point */ utf_t utflower(utf_t wc) { return ((wc < 'A') || (wc > 'Z') ? wc : wc + 'a' - 'A'); } int utf16len(const utf16_t *in) { int i; i = 0; while (in[i]) i++; return (i); } utf16_t *utf16cpy(utf16_t *dst, const utf16_t *src) { int i; i = 0; do { } while ((dst[i] = src[i]) && ++i); return (dst); } utf16_t *utf16ncpy(utf16_t *dst, const utf16_t *src, int size) { int i; i = 0; if (size) do { } while ((dst[i] = src[i]) && (++i < size)); return (dst); } int utf16cmp(const utf16_t *left, const utf16_t *right) { unsigned int lc,rc; if (trace > 2) { safe_fprintf(stderr,"utf16cmp l %s",printableutf16(left)); safe_fprintf(stderr," -- r %s",printableutf16(right)); } do { } while (((rc = *right++) == (lc = *left++)) && rc); #ifdef BIGENDIAN if (lc && rc) { rc = ((rc & 255) << 8) | ((rc >> 8) & 255); lc = ((lc & 255) << 8) | ((lc >> 8) & 255); } #endif if (trace > 2) { safe_fprintf(stderr," -- lc 0x%x rc 0x%x : %d\n",lc,rc,lc-rc); safe_fprintf(stderr,"%s\n",calledfrom(&left)); } return (lc - rc); } int utfmixcmp(const utf16_t *left, const utf_t *right) { unsigned int lc,rc; if (trace > 2) { safe_fprintf(stderr,"utfmixcmp l %s",printableutf16(left)); safe_fprintf(stderr," -- r %s",right); } do { lc = *left++; #ifdef BIGENDIAN lc = ((lc & 255) << 8) | ((lc >> 8) & 255); #endif rc = *right++ & 255; if (rc > 127) { // TODO make it safer : first 11xxxxxx next 10xxxxxx if (rc < 224) rc = ((rc & 31) << 6) + (*right++ & 63); else { rc = ((rc & 15) << 6) + (*right++ & 63); rc = (rc << 6) + (*right++ & 63); } } } while ((rc == lc) && rc); if (trace > 2) safe_fprintf(stderr," -- lc 0x%x rc 0x%x : %d %s\n",lc,rc,lc-rc,calledfrom(&left)); return (lc - rc); } /* * Temporary redefinition of standard functions */ char *_strupr(char *strg) { int i; /* replaces original */ for (i=0; strg[i]; i++) if ((strg[i] >= 'a') && (strg[i] <= 'z')) strg[i] += 'A' - 'a'; return (strg); } int winx_enable_privilege(unsigned long luid) { return (0); } #ifndef CURSES /* * Minimal console output, not using curses */ HANDLE WINAPI GetStdHandle(DWORD num) { return (~num == 10 ? (HANDLE)stdout : (HANDLE)NULL); } BOOLEAN WINAPI GetConsoleScreenBufferInfo(HANDLE h, CONSOLE_SCREEN_BUFFER_INFO *csbi) { csbi->dwSize.X = 80; csbi->dwSize.Y = 24; csbi->dwCursorPosition.X = 0; csbi->dwCursorPosition.Y = 0; csbi->wAttributes = 0; csbi->srWindow.Left = 0; csbi->srWindow.Top = 0; csbi->srWindow.Right = csbi->dwSize.X - 1; csbi->srWindow.Left = csbi->dwSize.Y - 1; return (TRUE); } void WINAPI close_console(HANDLE h) { } BOOLEAN WINAPI SetConsoleCursorPosition(HANDLE h, COORD p) { //safe_fprintf(stderr,"moving to y %d x %d %s\n",p.Y,p.X,calledfrom(&h)); return (TRUE); } BOOLEAN WINAPI SetConsoleTextAttribute(HANDLE h, WORD attr) { return (TRUE); } void set_map(int i, int j, char c) { // putc(c,stderr); } void set_message(int i, int j, int attr, const char *text) { // safe_fprintf(stderr,"%s\n",text); } #endif /* * Substitutes for Windows calls */ VOID NTAPI RtlFreeAnsiString(PANSI_STRING in) { /* String allocated by RtlUnicodeStringToAnsiString() */ if (in) { if (in->Buffer) free(in->Buffer); in->Buffer = (PCHAR)NULL; in->Length = 0; in->MaximumLength = 0; } } VOID NTAPI RtlInitAnsiString(PANSI_STRING out, const utf_t *in) { if (out) { if (in) { out->Length = strlen(in); /* danger : dropping the const attribute */ out->Buffer = (PCHAR)strchr(in,in[0]); out->MaximumLength = out->Length + 1; /* termination byte included */ } else { out->Buffer = (PCHAR)NULL; out->Length = 0; out->MaximumLength = 0; } } else { safe_fprintf(stderr,"Bad args to RtlInitAnsiString\n"); get_out(1); } if (trace > 2) safe_fprintf(stderr,"RtlInitAnsiString out 0x%lx buf 0x%lx mxl %d\n",(long)out,(long)out->Buffer,(int)out->MaximumLength); } VOID NTAPI RtlFreeUnicodeString(PUNICODE_STRING in) { /* string allocated by RtlAnsiStringToUnicodeString() */ if (in) { if (in->Buffer) free(in->Buffer); in->Buffer = (utf_t*)NULL; in->Length = 0; in->MaximumLength = 0; } } VOID WINAPI RtlInitUnicodeString(struct _UNICODE_STRING *out, const utf_t *in) { if (out) { if (in) { out->Length = strlen(in); /* danger : dropping the const attribute */ out->Buffer = (utf_t*)strchr(in,in[0]); out->MaximumLength = out->Length + 1; /* termination byte included */ } else { out->Buffer = (utf_t*)NULL; out->Length = 0; out->MaximumLength = 0; } } else { safe_fprintf(stderr,"Bad args to RtlInitUnicodeString\n"); get_out(1); } if (trace > 2) safe_fprintf(stderr,"RtlInitUnicodeString out 0x%lx buf 0x%lx mxl %d\n",(long)out,(long)out->Buffer,(int)out->MaximumLength); } NTSTATUS NTAPI RtlAnsiStringToUnicodeString(PUNICODE_STRING out, PANSI_STRING in, SIZE_T newout) { NTSTATUS r; r = STATUS_SUCCESS; if (in && out) { if (newout) { out->MaximumLength = in->Length + 1; out->Buffer = (utf_t*)malloc(out->MaximumLength*sizeof(utf_t)); } if (out->Buffer && (out->MaximumLength >= in->Length)) { out->Length = in->Length; memcpy(out->Buffer,in->Buffer,in->Length); } else { r = STATUS_UNSUCCESSFUL; } } else { safe_fprintf(stderr,"Bad args to RtlAnsiStringToUnicodeString\n"); r = STATUS_UNSUCCESSFUL; get_out(1); } if (trace > 2) safe_fprintf(stderr,"RtlAnsiStringToUnicodeString out 0x%lx buf 0x%lx mxl %d\n",(long)out,(long)out->Buffer,(int)out->MaximumLength); return (r); } NTSTATUS NTAPI RtlUnicodeStringToAnsiString(PANSI_STRING a, PUNICODE_STRING b, SIZE_T c) RUNDEF(RtlUnicodeStringToAnsiString) /* * Memory allocation */ PVOID NTAPI RtlAllocateHeap(HANDLE h, SIZE_T flags, SIZE_T size) { PVOID p; if (h) { p = (PVOID)malloc(size); if (!p) safe_fprintf(stderr,"** Memory allocation failed\n"); /* proceed in order to get details */ } else { fprintf(stderr,"** Heap was not created\n"); p = (PVOID)NULL; get_out(1); } if (trace > 2) safe_fprintf(stderr,"%ld bytes allocated at 0x%lx %s\n",(long)size,(long)p,calledfrom(&h)); return (p); } HANDLE NTAPI RtlCreateHeap(SIZE_T a, PVOID b, SIZE_T c, SIZE_T d, PVOID e, PRTL_HEAP_DEFINITION f) { return ((HANDLE)RtlCreateHeap); } HANDLE NTAPI RtlDestroyHeap(HANDLE a) { #if (LXSC | L8SC | STSC | SPGC | ARGC) & !NOMEMCHK showlist(999); /* check memory leaks */ #endif return ((HANDLE)NULL); } /* 2nd arg is ULONG on msdn */ BOOLEAN NTAPI RtlFreeHeap(HANDLE h, SIZE_T b, PVOID p) { BOOLEAN r; r = TRUE; if (h) { if (trace > 2) safe_fprintf(stderr,"freeing 0x%lx\n",(long)p); free(p); //domemcheck(); } else { fprintf(stderr,"** Heap was not allocated\n"); r = FALSE; get_out(1); } return (r); } NTSTATUS NTAPI NtAllocateVirtualMemory(HANDLE a, PVOID *b, SIZE_T c, SIZE_T *d, SIZE_T e, SIZE_T f) RUNDEF(NtAllocateVirtualMemory) UNDEF(LocalFree) NTSTATUS NTAPI NtFreeVirtualMemory(HANDLE a, PVOID *b, SIZE_T *c, SIZE_T d) RUNDEF(NtFreeVirtualMemory) /* * Processes, threads and synchronization */ HANDLE WINAPI GetCurrentThread(void) { #ifndef UNTHREADED HANDLE h; struct THREADLIST *item; h = (HANDLE)NULL; pthread_mutex_lock(&threadlock); item = threadlist; while (item && (((ULONG_PTR)&h < item->minstack) || ((ULONG_PTR)&h > item->maxstack))) item = item->next; if (item) h = (HANDLE)item->thread; pthread_mutex_unlock(&threadlock); return (h); /* NULL in main thread */ #else return ((HANDLE)(DWORD_PTR)getpid()); #endif } BOOLEAN WINAPI SetThreadPriority(HANDLE h, int p) { return (TRUE); } NTSTATUS NTAPI ZwTerminateThread(HANDLE h, NTSTATUS b) { #ifndef UNTHREADED NTSTATUS r; HANDLE th; HANDLE curr; struct THREADLIST *item; struct THREADLIST *previous; if (trace > 1) safe_fprintf(stderr,"ZwTerminateThread 0x%lx\n",(long)h); r = STATUS_UNSUCCESSFUL; previous = (struct THREADLIST*)NULL; curr = GetCurrentThread(); if (h == (HANDLE)-2) th = curr; else th = h; if (th) { pthread_mutex_lock(&threadlock); item = threadlist; while (item && ((HANDLE)item->thread != th)) { previous = item; item = item->next; } if (item && ((HANDLE)item->thread == th)) { if (trace > 1) safe_fprintf(stderr,"terminating thread 0x%lx item 0x%lx\n", (long)th,(long)item); /* terminated and closed : remove from list */ if (item->state & THREADCLOSED) { if (trace > 1) safe_fprintf(stderr,"removing thread 0x%lx\n",(long)th); if (previous) threadlist = previous->next; else threadlist = item->next; free(item); if (th == curr) { pthread_mutex_unlock(&threadlock); pthread_exit((void*)NULL); get_out(1); /* should not happen */ } } else item->state |= THREADTERMINATED; r = STATUS_SUCCESS; pthread_mutex_unlock(&threadlock); } else { pthread_mutex_unlock(&threadlock); if (trace) safe_fprintf(stderr,"** Failed to terminate thread\n"); get_out(1); } } else { safe_fprintf(stderr,"** Cannot terminate main thread\n"); get_out(1); } return (r); #else return (STATUS_UNSUCCESSFUL); #endif } #ifndef UNTHREADED /* * Effective start of a thread * * Collect starting parameters */ void *threadstart(void *arg) { struct THREADLIST *item; static int tid = 0; item = (struct THREADLIST*)arg; if (trace > 1) safe_fprintf(stderr,"Thread starting item 0x%lx start 0x%lx\n", (long)item,(long)item->start); pthread_mutex_lock(&threadlock); item->tid = ++tid; item->maxstack = (ULONG_PTR)&arg; /* assume stack grows down */ item->minstack = item->maxstack - STACKSZ; item->state |= THREADSTARTED; pthread_mutex_unlock(&threadlock); (*item->start)(item->param); ZwTerminateThread((HANDLE)item->thread,STATUS_SUCCESS); /* * Getting here means the handle was not (yet) closed by * the creating thread. So we close it, hoping it is not * used any more by the creating thread. * Of course this can lead to a double closing. */ if (trace > 1) safe_fprintf(stderr,"Thread handle was not closed\n"); CloseHandle((HANDLE)item->thread); get_out(1); /* should not happen */ return ((void*)NULL); } #endif NTSTATUS NTAPI RtlCreateUserThread(HANDLE parent, PSECURITY_DESCRIPTOR psec, SIZE_T susp, SIZE_T zero, SIZE_T reserv, SIZE_T comm, PTHREAD_START_ROUTINE start, PVOID param, PHANDLE ph, PCLIENT_ID pcid) { #ifndef UNTHREADED NTSTATUS r; struct THREADLIST *newitem; r = STATUS_UNSUCCESSFUL; newitem = (struct THREADLIST*)malloc(sizeof(struct THREADLIST)); if (newitem) { newitem->state = THREADCREATED; newitem->start = start; newitem->param = param; newitem->tid = 0; newitem->maxstack = 0; newitem->thread = (pthread_t)NULL; pthread_mutex_lock(&threadlock); newitem->next = threadlist; threadlist = newitem; pthread_mutex_unlock(&threadlock); /* start through an auxiliary routine */ if (!pthread_create(&newitem->thread,(pthread_attr_t*)NULL, threadstart,newitem)) { if (trace > 1) safe_fprintf(stderr,"Thread created, handle 0x%lx item 0x%lx start 0x%lx\n", (long)newitem->thread,(long)newitem,(long)start); *ph = (PHANDLE)newitem->thread; if (pcid) { pcid->UniqueProcess = (HANDLE)NULL; pcid->UniqueThread = (HANDLE)newitem->thread; } r = STATUS_SUCCESS; } } return (r); #else return (STATUS_UNSUCCESSFUL); #endif } HANDLE WINAPI CreateThread(LPSECURITY_ATTRIBUTES psec, SIZE_T size, LPTHREAD_START_ROUTINE start, LPVOID param, DWORD flags, LPDWORD pid) { #ifndef UNTHREADED HANDLE h; NTSTATUS r; CLIENT_ID cid; r = RtlCreateUserThread((HANDLE)NULL, (PSECURITY_DESCRIPTOR)NULL, 0, 0, size, size, start, param, &h, &cid); if (pid && (r == STATUS_SUCCESS)) *pid = (DWORD)(ULONG_PTR)cid.UniqueThread; return (r == STATUS_SUCCESS ? h : (HANDLE)NULL); #else return ((HANDLE)NULL); #endif } NTSTATUS NTAPI NtCreateEvent(PHANDLE ph, ACCESS_MASK b, const OBJECT_ATTRIBUTES *c, SIZE_T d, SIZE_T e) { if (trace > 1) fprintf(stderr,"** Skipping NtCreateEvent\n"); *ph = (void*)NULL; return (STATUS_SUCCESS); } HANDLE WINAPI CreateEvent(LPSECURITY_ATTRIBUTES pattr, BOOLEAN reset, BOOLEAN initial, LPCTSTR name) { #ifndef UNTHREADED HANDLE h; struct EVENTLIST *event; struct EVENTLIST *newevent; h = (HANDLE)NULL; newevent = (struct EVENTLIST*)malloc(sizeof(struct EVENTLIST)); if (newevent && !pattr && !reset && initial && name) { pthread_mutex_lock(&eventlock); event = eventlist; while (event && strcmp(event->name,name)) event = event->next; if (event) { h = (HANDLE)event; errno = ERROR_ALREADY_EXISTS; free(newevent); } else { h = (HANDLE)newevent; newevent->name = name; newevent->next = eventlist; eventlist = newevent; } pthread_mutex_unlock(&eventlock); if (trace > 1) safe_fprintf(stderr,"creating event %s h 0x%lx errno %d\n", name,(long)h,errno); } return ((HANDLE)h); #else return ((HANDLE)NULL); #endif } UNDEF(_getch) BOOLEAN __stdcall IncreaseGoogleAnalyticsCounter(const char *hostname, const char *path, const char *account) { return (TRUE); } NTSTATUS NTAPI RtlGetVersion(PRTL_OSVERSIONINFOW pinf) { const char *s; NTSTATUS r; if (trace > 2) safe_fprintf(stderr,"RtlGetVersion\n"); if (pinf) { /* data for Vista */ pinf->dwOSVersionInfoSize = sizeof(PRTL_OSVERSIONINFOW); pinf->dwMajorVersion = 6; pinf->dwMinorVersion = 0; pinf->dwBuildNumber = 0x1772; pinf->dwPlatformId = 2; s = "Service Pack 2"; ntfs_toutf16(pinf->szCSDVersion, s, strlen(s) + 1); r = STATUS_SUCCESS; } else { safe_fprintf(stderr,"Bad RtlGetVersion parameter\n"); r = STATUS_UNSUCCESSFUL; get_out(1); } return (r); } NTSTATUS NTAPI LdrGetDllHandle(SIZE_T a,SIZE_T b, const UNICODE_STRING *c, HMODULE *d) { if (trace > 2) safe_fprintf(stderr,"Ignoring LdrGetDllHandle\n"); return (STATUS_SUCCESS); } NTSTATUS NTAPI LdrGetProcedureAddress(PVOID a, PANSI_STRING name, SIZE_T c, PVOID *pfunc) { PVOID func; func = (PVOID)NULL; if (!strncmp((const char*)name->Buffer,"RtlGetVersion",12)) func = (PVOID)RtlGetVersion; if (!func) { safe_fprintf(stderr,"** Cannot get addr of %s\n",name->Buffer); get_out(1); } /* * Danger : returned functions are __stdcall, * caller has to be aware of signature */ *pfunc = func; return (func ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); } NTSTATUS NTAPI NtClose(HANDLE h) { int r; #ifndef UNTHREADED struct THREADLIST *item; struct THREADLIST *previous; struct EVENTLIST *event; struct EVENTLIST *previousevent; HANDLE current; #endif if (trace > 1) safe_fprintf(stderr,"NtClose h 0x%lx devicecount %d\n",(long)h,devicecount); if (devicecount && (h == devicehandle)) { if (!--devicecount) { r = ntfs_close(h); free(devicename); devicename = (char*)NULL; devicehandle = (HANDLE)NULL; } else r = 0; } else { #ifndef UNTHREADED current = (HANDLE)GetCurrentThread(); pthread_mutex_lock(&threadlock); item = threadlist; previous = (struct THREADLIST*)NULL; while (item && ((HANDLE)item->thread != h)) { previous = item; item = item->next; } if (item && ((HANDLE)item->thread == h)) { if (trace > 1) safe_fprintf(stderr,"closing thread 0x%lx state 0x%lx\n", (long)h,(long)item->state); /* terminated and closed : remove from list */ if (item->state & THREADTERMINATED) { if (trace > 1) safe_fprintf(stderr,"removing thread 0x%lx\n",(long)h); if (previous) threadlist = previous->next; else threadlist = item->next; free(item); if (h == current) { pthread_mutex_unlock(&threadlock); if (trace > 1) safe_fprintf(stderr,"terminating current thread 0x%lx\n", (long)h); pthread_exit((void*)NULL); get_out(1); /* should not happen */ } }else item->state |= THREADCLOSED; r = 0; pthread_mutex_unlock(&threadlock); }else { pthread_mutex_unlock(&threadlock); pthread_mutex_lock(&eventlock); event = eventlist; previousevent = (struct EVENTLIST*)NULL; while (event && (event != (HANDLE)h)) { previousevent = event; event = event->next; } if (event == (HANDLE)h) { if (trace > 1) safe_fprintf(stderr,"removing event %s\n",event->name); if (previousevent) previousevent->next = event->next; else eventlist = event->next; free(event); r = 0; pthread_mutex_unlock(&eventlock); } else { pthread_mutex_unlock(&eventlock); #else { { #endif if (h && devicecount) { r = ntfs_close_file(h); } else { safe_fprintf(stderr,"** Closing closed device\n"); get_out(1); r = -1; } } } } return (r ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS); /* return some error */ } BOOLEAN WINAPI CloseHandle(HANDLE h) { NTSTATUS r; r = NtClose(h); return (r == STATUS_SUCCESS); } NTSTATUS NTAPI NtDelayExecution(SIZE_T a, const LARGE_INTEGER *b) { unsigned long delay; if (trace > 2) safe_fprintf(stderr,"** sleeping %lld msec\n",(long long)b->QuadPart/10000); if (b->QuadPart < TIMEUNIT) delay = 1; else delay = b->QuadPart/TIMEUNIT; sleep(delay); return (STATUS_SUCCESS); } VOID WINAPI Sleep(DWORD msecs) { if (msecs >= 1000) sleep(msecs/1000); } NTSTATUS NTAPI NtDeleteFile(POBJECT_ATTRIBUTES in) { const char *path; int r; r = -1; if (in) { path = printablepath(in->ObjectName->Buffer,in->ObjectName->Length); if (path) { if (trace > 2) safe_fprintf(stderr,"deleting %s\n",path); r = ntfs_unlink(devicehandle,path); } else { safe_fprintf(stderr,"Bad NtDeleteFile path %s\n",path); get_out(1); } } else { safe_fprintf(stderr,"Bad NtDeleteFile argument\n"); get_out(1); } if (trace > 2) safe_fprintf(stderr,"NtDeleteFile %s : %s\n", printablepath(in->ObjectName->Buffer,in->ObjectName->Length), (r ? "not deleted" : "deleted")); return (r ? STATUS_UNSUCCESSFUL : STATUS_SUCCESS); /* return some error */ } NTSTATUS NTAPI NtDeviceIoControlFile(HANDLE h, HANDLE he, PIO_APC_ROUTINE pr, PVOID pc, PIO_STATUS_BLOCK ps, SIZE_T cc, PVOID in, SIZE_T inl, PVOID out, SIZE_T outl) { NTSTATUS r; struct _NTFS_DATA *pdata; if (trace > 2) { safe_fprintf(stderr,"NtDeviceIoControlFile h 0x%lx he 0x%lx pr 0x%lx\n",(long)h,(long)he,(long)pr); safe_fprintf(stderr," pc 0x%lx ps 0x%lx cc 0x%lx\n",(long)pc,(long)ps,(long)cc); safe_fprintf(stderr," in 0x%lx inl %ld out 0x%lx outl %ld\n",(long)in,(long)inl,(long)out,(long)outl); } r = STATUS_UNSUCCESSFUL; switch (cc) { case 0x64 : /* FSCTL_GET_NTFS_VOLUME_DATA */ if (devicecount && !he && !pr && !pc && !in && h && ps && out && (outl == sizeof(struct _NTFS_DATA))) { pdata = (struct _NTFS_DATA*)out; if (!ntfs_fill_ntfs_data(h, &pdata->VolumeSerialNumber, &pdata->BytesPerSector, &pdata->MftValidDataLength)) { ps->Information = (ULONG_PTR)sizeof(struct _NTFS_DATA); r = STATUS_SUCCESS; } } else { safe_fprintf(stderr,"Bad NtDeviceIoControlFile parameter for controlcode 0x%ld\n",(long)cc); get_out(1); } break; case 0x78 : /* FSCTL_IS_VOLUME_DIRTY : get the volume flags */ if (devicecount && !he && !pr && !pc && !in && h && ps && out && (outl == sizeof(ULONG)) && !ntfs_fill_vol_flags(h,(ULONG*)out)) { ps->Information = (ULONG_PTR)sizeof(ULONG); r = STATUS_SUCCESS; } else { safe_fprintf(stderr,"Bad NtDeviceIoControlFile parameter for controlcode 0x%ld\n",(long)cc); get_out(1); } break; default : safe_fprintf(stderr,"Bad NtDeviceIoControlFile controlcode 0x%lx\n",(long)cc); r = STATUS_UNSUCCESSFUL; get_out(1); } ps->Status = r; return (r); } NTSTATUS NTAPI NtFlushBuffersFile(HANDLE h, PIO_STATUS_BLOCK pb) { NTSTATUS r; r = STATUS_UNSUCCESSFUL; if (h && pb) { if (!ntfs_sync(h)) r = STATUS_SUCCESS; } else { safe_fprintf(stderr,"Bad args to NtFlushBuffersFile\n"); get_out(1); } pb->Information = (ULONG_PTR)0; pb->Status = r; if (trace > 2) safe_fprintf(stderr,"NtFlushBuffersFile : 0x%lx\n",(long)r); return (r); } /* need win data */ NTSTATUS NTAPI NtFsControlFile(HANDLE h, HANDLE he, PIO_APC_ROUTINE pr, PVOID pc, PIO_STATUS_BLOCK ps, SIZE_T cc, PVOID in, SIZE_T inl, PVOID out, SIZE_T outl) // in is "device-specific information to be given to the target driver" // out is "information returned from the target driver" { NTSTATUS r; ULONGLONG pos; ULONGLONG fullsize; ULONGLONG inode; ULONG retlen; BOOLEAN bad = FALSE; int s; NTFS_FILE_RECORD_OUTPUT_BUFFER *pinode; BITMAP_DESCRIPTOR *pbitmap; const MOVEFILE_DESCRIPTOR *pmove; GET_RETRIEVAL_DESCRIPTOR *prun; struct _NTFS_DATA *pdata; int headsz; if (trace > 2) { safe_fprintf(stderr,"NtFsControlFile h 0x%lx he 0x%lx pr 0x%lx\n",(long)h,(long)he,(long)pr); safe_fprintf(stderr," pc 0x%lx ps 0x%lx cc 0x%lx\n",(long)pc,(long)ps,(long)cc); safe_fprintf(stderr," in 0x%lx inl %ld out 0x%lx outl %ld\n",(long)in,(long)inl,(long)out,(long)outl); } r = STATUS_UNSUCCESSFUL; bad = TRUE; switch (cc - 0x90000) { case 0x64 : /* FSCTL_GET_NTFS_VOLUME_DATA, on X64 only */ if (devicecount && !he && !pr && !pc && !in && h && ps && out && (outl == sizeof(struct _NTFS_DATA))) { pdata = (struct _NTFS_DATA*)out; if (!ntfs_fill_ntfs_data(h, &pdata->VolumeSerialNumber, &pdata->BytesPerSector, &pdata->MftValidDataLength)) { ps->Information = (ULONG_PTR)sizeof(struct _NTFS_DATA); r = STATUS_SUCCESS; } bad = FALSE; } ps->Status = r; break; case 0x68 : /* Get a file record */ if (devicecount && !he && !pr && !pc && h && ps && in && (inl == 8) && out && outl) { pinode = (NTFS_FILE_RECORD_OUTPUT_BUFFER*)out; memcpy(&inode,in,8); if (trace > 2) safe_fprintf(stderr,"get file record for inode %lld\n",(long long)inode); headsz = (char*)pinode->FileRecordBuffer - (char*)pinode; if (!ntfs_fill_inode_data(h, pinode->FileRecordBuffer, // danger : unaligned buffer outl - headsz, inode, &retlen)) { //safe_dump(stderr,"inode",pinode->FileRecordBuffer,retlen); pinode->FileReferenceNumber = inode; ps->Information = retlen + headsz; // might want to return 0xc000000d for an unused record. r = STATUS_SUCCESS; } else { display_error("Failed to get inode data"); if (trace > 2) safe_fprintf(stderr,"** Failed to get inode %lld\n",(long long)inode); } bad = FALSE; } ps->Status = r; break; case 0x6f : /* Get part of bitmap */ if (devicecount && !he && !pr && !pc && h && ps && in && (inl == 8) && out && outl) { memcpy(&pos,in,8); pos = (pos >> 3); /* align to multiple of 8 */ pbitmap = (BITMAP_DESCRIPTOR*)out; headsz = (char*)pbitmap->Map - (char*)pbitmap; if (!ntfs_fill_bitmap(h, pbitmap->Map, outl - headsz, pos, &fullsize, &retlen)) { pbitmap->ClustersToEndOfVol = fullsize - (pos << 3); pbitmap->StartLcn = pos << 3; ps->Information = (ULONG_PTR)(retlen + headsz); if ((retlen << 3) < pbitmap->ClustersToEndOfVol) r = STATUS_BUFFER_OVERFLOW; else r = STATUS_SUCCESS; if (trace > 2) safe_fprintf(stderr,"bitmap size returned %ld\n",(long)ps->Information); bad = FALSE; } else { display_error("Failed to get a bitmap"); if (trace > 2) safe_fprintf(stderr,"** Failed to get a bitmap"); } } ps->Status = r; break; case 0x73 : /* get the runlist (vcn and lcn) */ /* * Format of the returned data (CPU endian) * cnt vcn[0] cnt is the number of runs returned * 0 vcn[1] lcn[0] size needed : 16*(cnt + 1) * 1 vcn[2] lcn[1] sparse entries have lcn = -1 * 2 vcn[3] lcn[2] * ... ...... ...... * cnt-2 vcn[cnt-1] lcn[cnt-2] * cnt-1 1st free lcn[cnt-1] */ if (devicecount && !he && !pr && !pc && h && ps && in && (inl == 8) && out && outl) { memcpy(&pos,in,8); prun = (GET_RETRIEVAL_DESCRIPTOR*)out; headsz = (char*)prun->Pair - (char*)prun; s = ntfs_fill_runlist(h,pos, &prun->NumberOfPairs, prun->Pair, outl - headsz); if (s >= 0) { ps->Information = (ULONG_PTR)(prun->NumberOfPairs*16 + headsz); prun->StartVcn = pos; if (s) r = STATUS_BUFFER_OVERFLOW; else r = STATUS_SUCCESS; if (trace > 2) { safe_fprintf(stderr,"returned size %ld pairs %ld headsz %ld\n", (long)ps->Information,(long)prun->NumberOfPairs,(long)headsz); safe_dump(stderr,"runlist",(char*)out,ps->Information); } } else { display_error("Failed to get a runlist"); if (trace > 2) safe_fprintf(stderr,"** Failed to get runlist\n"); } bad = FALSE; // apparently the size is the only returned information // check returned size } ps->Status = r; break; case 0x74 : /* relocate cluster */ /* NtDeviceIoControlFile move.c code 0x90074 Dump of NtDeviceIoControlFile input 32 bytes at 0xb3f2d8 (Windows endian) 0000 9c000000 2d000000 5e080000 00000000 ....-...^....... 0010 873e0000 00000000 c2000000 03000000 .>.............. svcn at 8, tlcn at 16, count at 24 */ if (devicecount && !he && !pr && !pc && h && ps && in && (inl == sizeof(MOVEFILE_DESCRIPTOR)) && !out && !outl) { pmove = (const MOVEFILE_DESCRIPTOR*)in; if (!ntfs_move_clusters(pmove->FileHandle, pmove->StartVcn.QuadPart, pmove->TargetLcn.QuadPart, pmove->NumVcns)) { ps->Information = (ULONG_PTR)inl; r = STATUS_SUCCESS; } else { display_error("Failed to move clusters"); if (trace > 2) safe_fprintf(stderr,"** Failed to move clusters vcn 0x%llx cnt %ld to lcn 0x%llx\n", (long long)pmove->StartVcn.QuadPart, (long)pmove->NumVcns, (long long)pmove->TargetLcn.QuadPart); } bad = FALSE; } ps->Status = r; break; case 0x78 : /* FSCTL_IS_VOLUME_DIRTY : get the volume flags */ if (devicecount && !he && !pr && !pc && !in && h && ps && out && (outl == sizeof(ULONG)) && !ntfs_fill_vol_flags(h,(ULONG*)out)) { ps->Information = (ULONG_PTR)sizeof(ULONG); r = STATUS_SUCCESS; bad = FALSE; } ps->Status = r; break; default : bad = TRUE; break; } /* Only wrong calls, other errors to be processed by caller */ if (bad) { safe_fprintf(stderr,"Bad NtFsControlFile parameter for controlcode 0x%lx\n",(long)cc); r = STATUS_UNSUCCESSFUL; ps->Status = r; get_out(1); } return (r); } NTSTATUS NTAPI NtLoadDriver(PUNICODE_STRING a) RUNDEF(NtLoadDriver) NTSTATUS NTAPI NtOpenEvent(PHANDLE ph, ACCESS_MASK msk, const OBJECT_ATTRIBUTES *pattr) { /* Only used to trigger an external debugger */ *ph = (PHANDLE)NULL; return (STATUS_UNSUCCESSFUL); } NTSTATUS NTAPI NtOpenKey(PHANDLE a, ACCESS_MASK b, POBJECT_ATTRIBUTES c) RUNDEF(NtOpenKey) NTSTATUS NTAPI NtQueryDirectoryFile(HANDLE a, HANDLE b, PIO_APC_ROUTINE c, PVOID d, PIO_STATUS_BLOCK e, PVOID f, SIZE_T g, FILE_INFORMATION_CLASS h, SIZE_T i, PUNICODE_STRING j, SIZE_T k) RUNDEF(NtQueryDirectoryFile) NTSTATUS NTAPI NtQueryInformationFile(HANDLE a, PIO_STATUS_BLOCK b, PVOID c, SIZE_T d, FILE_INFORMATION_CLASS f) RUNDEF(NtQueryInformationFile) NTSTATUS NTAPI NtQueryInformationProcess(HANDLE a, PROCESSINFOCLASS b, PVOID c, SIZE_T d, PULONG e) RUNDEF(NtQueryInformationProcess) NTSTATUS NTAPI NtQuerySystemTime(PLARGE_INTEGER ptime) { ptime->QuadPart = currenttime(); return (STATUS_SUCCESS); } NTSTATUS NTAPI NtQueryValueKey(HANDLE a, PUNICODE_STRING b, KEY_VALUE_INFORMATION_CLASS c, PVOID d, SIZE_T e, PULONG f) RUNDEF(NtQueryValueKey) struct _TEB * WINAPI NtCurrentTeb(void) { static struct _TEB teb; static int pid; pid = getpid(); teb.ClientId.UniqueProcess = (HANDLE*)&pid; return (&teb); } NTSTATUS NTAPI NtQueryVolumeInformationFile(HANDLE h, PIO_STATUS_BLOCK pb, PVOID buf, SIZE_T sz, FS_INFORMATION_CLASS class) { static char ntfs[] = { 'N', 0, 'T', 0, 'F', 0, 'S', 0 } ; FILE_FS_VOLUME_INFORMATION *pvi; FILE_FS_DEVICE_INFORMATION *pdi; FILE_FS_SIZE_INFORMATION *psz; FILE_FS_ATTRIBUTE_INFORMATION *pai; NTSTATUS r; if (trace > 2) safe_fprintf(stderr,"NtQueryVolumeInformationFile buf 0x%lx sz %d class %d\n", (long)buf,(int)sz,(int)class); r = STATUS_UNSUCCESSFUL; switch (class) { case FileFsVolumeInformation : /* Dump of volinfo4 540 bytes at 0x2507c8 0000 80ca3404 cd92ca01 56553d49 06000000 ..4.....VU=I.... 0010 01007000 71003100 00000000 00000000 ..p.q.1......... */ pvi = (FILE_FS_VOLUME_INFORMATION*)buf; if (!ntfs_fill_vol_info(h, &pvi->VolumeCreationTime, /* bytes 0x48-0x4b in cpu endianness */ &pvi->VolumeSerialNumber, /* number of bytes expected */ &pvi->VolumeLabelLength, /* encoded on Windows on 1 byte + alignment */ &pvi->Unknown, /* BOOLEAN SupportsObjects, */ pvi->VolumeLabel)) r = STATUS_SUCCESS; break; case FileFsSizeInformation : /* Dump of volinfo2 24 bytes at 0x2507c8 0000 f9ef0e00 00000000 a1ae0300 00000000 0010 08000000 00020000 */ psz = (FILE_FS_SIZE_INFORMATION*)buf; if (!ntfs_fill_fs_sizes(h, &psz->TotalAllocationUnits, &psz->AvailableAllocationUnits, &psz->SectorsPerAllocationUnit, &psz->BytesPerSector)) r = STATUS_SUCCESS; break; case FileFsAttributeInformation : /* Dump of volinfo3 534 bytes at 0x2507c8 0000 ff002700 ff000000 08000000 4e005400 ..'.........N.T. 0010 46005300 00000000 00000000 00000000 F.S............. */ pai = (FILE_FS_ATTRIBUTE_INFORMATION*)buf; /* oid, reparse, sparse, compress, acls, unicode, etc. */ pai->FileSystemAttributes = 0x002700ff; pai->MaximumComponentNameLength = 255; pai->FileSystemNameLength = 8; memcpy(pai->FileSystemName,ntfs,8); r = STATUS_SUCCESS; break; case FileFsDeviceInformation : pdi = (FILE_FS_DEVICE_INFORMATION*)buf; fprintf(stderr,"** Unsupported FileFsDeviceInformation\n"); get_out(1); break; default : safe_fprintf(stderr,"** Unsupported NtQueryVolumeInformationFile class %d\n", (int)class); get_out(1); } return (r); } NTSTATUS NTAPI NtReadFile(HANDLE h, HANDLE he, PIO_APC_ROUTINE pr, PVOID pc, PIO_STATUS_BLOCK ps, PVOID buf, SIZE_T sz, PLARGE_INTEGER ppos, PULONG pi) { NTSTATUS r; LONGLONG pos; if (trace > 2) { safe_fprintf(stderr,"NtReadFile h 0x%lx he 0x%lx pr 0x%lx\n",(long)h,(long)he,(long)pr); safe_fprintf(stderr," pc 0x%lx ps 0x%lx buf 0x%lx\n",(long)pc,(long)ps,(long)buf); safe_fprintf(stderr," sz 0x%lx ppos 0x%lx pi 0x%lx\n",(long)sz,(long)ppos,(long)pi); } r = STATUS_UNSUCCESSFUL; if (!he && !pr && !pc && !pi && h && buf && sz && ppos && devicecount) { memcpy(&pos,&ppos->QuadPart,8); /* why is this wrongly aligned ? */ if (!ntfs_read(h,buf,sz,pos)) { r = STATUS_SUCCESS; ps->Information = (ULONG_PTR)sz; if (trace > 2) safe_dump(stderr,"read",buf,sz); } ps->Status = r; } else { safe_fprintf(stderr,"Bad args to NtReadFile\n"); get_out(1); } return (r); } NTSTATUS NTAPI NtSetEvent(HANDLE a, PULONG b) RUNDEF(NtSetEvent) NTSTATUS NTAPI NtUnloadDriver(PUNICODE_STRING a) RUNDEF(NtUnloadDriver) NTSTATUS NTAPI NtWaitForSingleObject(HANDLE h, SIZE_T sz, const LARGE_INTEGER *pdelay) { if (pdelay && (trace > 1)) fprintf(stderr,"** Skipping NtWaitForSingleObject h 0x%lx sz %ld delay %lld\n",(long)h,(long)sz,(long long)pdelay->QuadPart); return (STATUS_SUCCESS); } NTSTATUS NTAPI NtWriteFile(HANDLE h, HANDLE he, PIO_APC_ROUTINE pr, PVOID pc, PIO_STATUS_BLOCK ps, PVOID buf, SIZE_T sz, PLARGE_INTEGER ppos, PULONG pi) { NTSTATUS r; ULONGLONG pos; if (trace > 2) { safe_fprintf(stderr,"NtWriteFile h 0x%lx he 0x%lx pr 0x%lx\n",(long)h,(long)he,(long)pr); safe_fprintf(stderr," pc 0x%lx ps 0x%lx buf 0x%lx\n",(long)pc,(long)ps,(long)buf); safe_fprintf(stderr," sz 0x%lx ppos 0x%lx pi 0x%lx\n",(long)sz,(long)ppos,(long)pi); } r = STATUS_UNSUCCESSFUL; if (!he && !pr && !pc && !pi // && h && buf && sz && ppos && devicecount) { && h && buf && ppos && devicecount) { memcpy(&pos,&ppos->QuadPart,8); if (h == devicehandle) { safe_dump(stderr,"writing to device",buf,sz); get_out(1); if (!ntfs_write(h,buf,sz,pos)) { r = STATUS_SUCCESS; ps->Information = (ULONG_PTR)sz; } } else { if (trace > 2) safe_dump(stderr,"writing to file",buf,sz); // if (!ntfs_write_file(h,buf,sz,pos)) { if (!sz || !ntfs_write_file(h,buf,sz,pos)) { r = STATUS_SUCCESS; ps->Information = (ULONG_PTR)sz; } } ps->Status = r; } else { safe_fprintf(stderr,"Bad args to NtWriteFile\n"); get_out(1); } return (r); } VOID WINAPI RtlZeroMemory(VOID *Destination, SIZE_T Length) { if (trace > 2) safe_fprintf(stderr,"memset 0x%lx lth %ld %s\n",(long)Destination,(long)Length,calledfrom(&Destination)); memset(Destination, 0, Length); } BOOLEAN WINAPI SetConsoleWindowInfo(HANDLE h, BOOLEAN abs, const SMALL_RECT*pr) RUNDEF(SetConsoleWindowInfo) UNDEF(udefrag_fbsize) UNDEF(udefrag_toupper) UNDEF(_wcsupr) utf16_t * WINAPI GetCommandLineW(void) { undefined("GetCommandLineW"); return (NULL); } NTSTATUS NTAPI NtOpenSection(HANDLE *a, ACCESS_MASK b, const OBJECT_ATTRIBUTES *c) RUNDEF(NtOpenSection) NTSTATUS NTAPI NtUnmapViewOfSection(HANDLE h, PVOID p) RUNDEF(NtUnmapViewOfSection) NTSTATUS NTAPI NtMapViewOfSection(HANDLE a, HANDLE b, PVOID *c, SIZE_T d, SIZE_T e, const LARGE_INTEGER *f, SIZE_T *g, SECTION_INHERIT h, SIZE_T i, SIZE_T j) RUNDEF(NtMapViewOfSection) BOOLEAN WINAPI SetConsoleCtrlHandler(PHANDLER_ROUTINE routine, BOOLEAN add) { #if STSC if (trace > 2) fprintf(stderr,"Skipping SetConsoleCtrlHandler\n"); return (TRUE); #else typedef void (*signalroutine)(int); signalroutine oldroutine; if (add) oldroutine = signal(SIGINT, (signalroutine)routine); else oldroutine = signal(SIGINT, SIG_DFL); if (trace > 2) { if (add) safe_fprintf(stderr,"set ctrl-c to 0x%lx old 0x%lx\n",(long)routine,(long)oldroutine); else safe_fprintf(stderr,"removed ctrl-c 0x%lx\n",(long)oldroutine); } return (oldroutine != SIG_ERR); #endif } DWORD WINAPI GetEnvironmentVariableW(const utf_t *name, utf_t *buf, DWORD size) { char *p; DWORD sz; sz = 0; if (name && buf && size) { p = getenv(name); if (p) { sz = strlen(p); if (sz < size) { strcpy(buf,p); errno = 0; } else sz = 0; } else { errno = ERROR_ENVVAR_NOT_FOUND; sz = 0; } buf[sz] = 0; } return (sz); } BOOLEAN WINAPI SetEnvironmentVariableW(const utf_t *name, const utf_t *value) { int r; r = setenv(name,value,1); if (trace > 2) safe_fprintf(stderr,"setenv name [%s] value [%s] : %d\n", name,value,r); return (!r); } BOOLEAN WINAPI SetEnvironmentVariableA(const char *name, const char *value) { int r; r = setenv(name,value,1); if (trace > 2) safe_fprintf(stderr,"setenv name [%s] value [%s] : %d\n", name,value,r); return (!r); } NTSTATUS NTAPI RtlQueryEnvironmentVariable_U(PWSTR a, PUNICODE_STRING in, PUNICODE_STRING out) { char *name; const char *p; NTSTATUS r; r = STATUS_UNSUCCESSFUL; if (in && out && (in->Length < PBUFSZ)) { name = nextbuf(); memcpy(name,in->Buffer,in->Length); name[in->Length] = 0; p = getenv(name); if (p) { if (trace > 2) safe_fprintf(stderr,"querying %s, got %s\n",name,p); if (strlen(p) <= out->MaximumLength) { out->Length = strlen(p); memcpy(out->Buffer,p,out->Length); r = STATUS_SUCCESS; } else { fprintf(stderr,"** Env too long\n"); get_out(1); } } else { errno = ERROR_ENVVAR_NOT_FOUND; out->Length = 0; if (trace > 2) safe_fprintf(stderr,"querying %s, was not set\n",name); } } else { safe_fprintf(stderr,"Bad args to RtlQueryEnvironmentVariable_U\n"); get_out(1); } return (r); } NTSTATUS NTAPI RtlSetEnvironmentVariable(PWSTR env, PUNICODE_STRING name, PUNICODE_STRING value) { char *n; char *v; NTSTATUS r; r = STATUS_UNSUCCESSFUL; if (name && value && (name->Length < PBUFSZ) && (value->Length < PBUFSZ)) { n = nextbuf(); memcpy(n,name->Buffer,name->Length); n[name->Length] = 0; v = nextbuf(); memcpy(v,value->Buffer,value->Length); v[value->Length] = 0; if (setenv(n,v,1)) r = STATUS_SUCCESS; if (trace > 2) safe_fprintf(stderr,"setenv name [%s] value [%s] : 0x%lx\n", n,v,r); } return (r); } DWORD WINAPI GetLastError(void) { int old = errno; /* using errno, thread safe */ errno = 0; return (old); } DWORD WINAPI FormatMessageA(DWORD flg, LPCVOID src, DWORD idmess, DWORD idlang, LPSTR buf, DWORD sz, va_list *args) //DWORD WINAPI FormatMessageA(long, char*, long, long, char*, long, va_list*) { return (-1); /* return an error */ } DWORD WINAPI FormatMessage(DWORD flg, LPCVOID src, DWORD idmess, DWORD idlang, LPSTR buf, DWORD sz, va_list *args) //DWORD WINAPI FormatMessage(long, char*, long, long, char*, long, va_list*) { return (-1); /* return an error */ } NTSTATUS NTAPI xNtCreateFile(PHANDLE ph, ACCESS_MASK acc, POBJECT_ATTRIBUTES p, PIO_STATUS_BLOCK s, PLARGE_INTEGER i, SIZE_T attr, SIZE_T share, SIZE_T d, SIZE_T e, PVOID q, SIZE_T f, const char *file, int line) { const char *filename; // TODO check all arguments if (trace > 1) { safe_fprintf(stderr,"NtCreateFile phandle 0x%lx acc 0x%lx attr 0x%lx from %s line %d\n",(long)ph,(long)acc,(long)attr,file,line); safe_fprintf(stderr," name %s devicecount %d\n",printablepath(p->ObjectName->Buffer,p->ObjectName->Length),devicecount); } if (devicecount) { /* device already open */ if ((strlen(devicename) == p->ObjectName->Length) && !memcmp(devicename,p->ObjectName->Buffer,p->ObjectName->Length)) { devicecount++; *ph = devicehandle; if (trace > 2) safe_fprintf(stderr,"Reopening %s handle 0x%lx\n", devicename,devicehandle); } else { filename = printablepath(p->ObjectName->Buffer,p->ObjectName->Length); // TODO allow any length (remove prefix, insert terminator) if (filename) { if (acc & (FILE_GENERIC_WRITE | FILE_APPEND_DATA)) *ph = ntfs_create_file(devicehandle,filename); else *ph = ntfs_open_file(devicehandle,filename); } else { safe_fprintf(stderr,"** Opening several devices\n"); *ph = (HANDLE)NULL; get_out(1); } } } else { devicename = (char*)malloc(p->ObjectName->Length + 1); if (devicename) { memcpy(devicename,p->ObjectName->Buffer,p->ObjectName->Length); devicename[p->ObjectName->Length] = 0; *ph = (HANDLE)ntfs_open(devicename); if (*ph) { if (trace > 2) safe_fprintf(stderr,"NTFS device opened, handle 0x%lx\n",(long)*ph); devicecount++; devicehandle = *ph; } } else { safe_fprintf(stderr,"** Failed to allocate %u bytes for device name\n", (unsigned int)p->ObjectName->Length); *ph = (HANDLE)NULL; get_out(1); } } return (*ph ? STATUS_SUCCESS : STATUS_UNSUCCESSFUL); /* return some error */ } DWORD MAKELANGID(DWORD lang, DWORD sublang) { return (0); } NTSTATUS NTAPI NtSetInformationProcess(HANDLE a, PROCESS_INFORMATION_CLASS b,PVOID c, SIZE_T d) { return (STATUS_SUCCESS); } VOID NTAPI DbgBreakPoint(VOID) VUNDEF(DbgBreakPoint) PRTL_USER_PROCESS_PARAMETERS NTAPI RtlNormalizeProcessParams(RTL_USER_PROCESS_PARAMETERS *pp) { return (pp); } void initwincalls(void) { #ifndef UNTHREADED threadlist = (struct THREADLIST*)NULL; eventlist = (struct EVENTLIST*)NULL; pthread_mutex_init(&loglock,(pthread_mutexattr_t*)NULL); pthread_mutex_init(&threadlock,(pthread_mutexattr_t*)NULL); pthread_mutex_init(&eventlock,(pthread_mutexattr_t*)NULL); #endif } void endwincalls(void) { if (translations) freetranslation(translations); translations = (struct TRANSLATIONS*)NULL; } #if STSC | SPGC UNDEF(_wcslwr) UNDEF(wcscat) UNDEF(wcschr) #endif #if 0 /* * Functions which are probably not needed any more * (kept here, just in case) */ utf16_t WINAPI **CommandLineToArgvW(const utf16_t *xargv, const int *xargc) { undefined("CommandLineToArgvW"); return (NULL); } WUNDEF(GetFullPathNameW) WUNDEF(GetProcAddress) WUNDEF(GlobalFree) HANDLE WINAPI LoadLibrary(const char *name) { undefined("LoadLibrary"); return (NULL); } NTSTATUS NTAPI NtCancelIoFile(HANDLE a, PIO_STATUS_BLOCK b) RUNDEF(NtCancelIoFile) NTSTATUS NTAPI NtDisplayString(PUNICODE_STRING in) { char buf[256]; NTSTATUS r; r = STATUS_UNSUCCESSFUL; if (in && (in->Length < 256)) { memcpy(buf,in->Buffer,in->Length); buf[in->Length] = 0; safe_fprintf(stderr,"console : %s",buf); r = STATUS_SUCCESS; } else { safe_fprintf(stderr,"Bad args to NtDisplayString\n"); get_out(1); } return (r); } NTSTATUS NTAPI NtFlushKey(HANDLE KeyHandle) RUNDEF(NtFlushKey) NTSTATUS NTAPI NtOpenSymbolicLinkObject(PHANDLE a, ACCESS_MASK b, POBJECT_ATTRIBUTES d) RUNDEF(NtOpenSymbolicLinkObject) NTSTATUS NTAPI NtQueryPerformanceCounter(PLARGE_INTEGER pval, PLARGE_INTEGER pfreq) /* danger : not documented as NTAPI on msdn */ { if (pval) pval->QuadPart = 0; if (pfreq) pfreq->QuadPart = 0; return (STATUS_SUCCESS); } NTSTATUS NTAPI NtQuerySymbolicLinkObject(HANDLE a, PUNICODE_STRING b, PULONG c) RUNDEF(NtQuerySymbolicLinkObject) NTSTATUS NTAPI NtSetValueKey(HANDLE a, PUNICODE_STRING b, SIZE_T c, SIZE_T d, PVOID e, SIZE_T f) RUNDEF(NtSetValueKey) NTSTATUS NTAPI NtShutdownSystem(SHUTDOWN_ACTION a) RUNDEF(NtShutdownSystem) NTSTATUS NTAPI NtTerminateProcess(HANDLE a, SIZE_T b) RUNDEF(NtTerminateProcess) NTSTATUS NTAPI RtlAdjustPrivilege(SIZE_T Id, SIZE_T Enable, SIZE_T ForCurrentThread, SIZE_T *WasEnabled) { if (trace > 2) fprintf(stderr,"** Skipping RtlAdjustPrivilege\n"); return (STATUS_SUCCESS); } PRTL_USER_PROCESS_PARAMETERS NTAPI RtlNormalizeProcessParams(RTL_USER_PROCESS_PARAMETERS *a) RUNDEF(RtlNormalizeProcessParams) WUNDEF(SetConsoleTextAttribute) WUNDEF(Sleep) UNDEF(WriteConsoleW) #endif /* not needed any more */