restore_usa: ; Update Sequence Array restore mov bx, [di+4] mov cx, [di+6] inc bx add bx, di inc bx add di, 1feh dec cx @@: mov ax, [bx] stosw inc bx inc bx add di, 1feh loop @b ret find_attr: ; in: di->file record, ax=attribute ; out: di->attribute or di=0 if not found add di, [di+14h] .1: ; attributes codes are formally dwords, but all they fit in word cmp word [di], -1 jz .notfound cmp word [di], ax jnz .continue ; for $DATA attribute, scan only unnamed cmp ax, 80h jnz .found cmp byte [di+9], 0 jz .found .continue: add di, [di+4] jmp .1 .notfound: xor di, di .found: ret process_mcb_nonres: ; in: si->attribute, es:di->buffer ; out: di->buffer end add si, [si+20h] xor ebx, ebx .loop: lodsb test al, al jz .done push invalid_read_request_string movzx cx, al shr cx, 4 jz find_error_sp xchg ax, dx and dx, 0Fh jz find_error_sp add si, cx add si, dx pop ax push si dec si movsx eax, byte [si] dec cx jz .l1e .l1: dec si shl eax, 8 mov al, [si] loop .l1 .l1e: xchg ebp, eax dec si movsx eax, byte [si] mov cx, dx dec cx jz .l2e .l2: dec si shl eax, 8 mov al, byte [si] loop .l2 .l2e: pop si add ebx, ebp ; eax=length, ebx=disk block stosd mov eax, ebx stosd jmp .loop .done: xor eax, eax stosd ret load_attr: ; in: ax=attribute, es:bx->buffer, di->base record ; out: bx->buffer end; CF set if not found push di push ax mov byte [es:bx], 1 inc bx push bx mov [ofs], bx ; scan for attrubute add di, [di+14h] @@: call find_attr.1 test di, di jz .notfound1 cmp byte [di+8], 0 jnz .nonresident ; resident attribute mov si, di pop di dec di mov al, 0 stosb mov ax, [si+10h] stosw xchg ax, cx add si, [si+14h] rep movsb mov bx, di pop ax pop di ret .nonresident: ; nonresident attribute cmp dword [di+10h], 0 jnz @b ; read start of data mov si, di pop di call process_mcb_nonres push di .notfound1: ; $ATTRIBUTE_LIST is always in base file record cmp word [esp+2], 20h jz .nofragmented ; scan for $ATTRIBUTE_LIST = 20h mov di, [esp+4] mov ax, 20h call find_attr test di, di jz .nofragmented ; load $ATTRIBUTE_LIST itself push es mov bx, 0C000h mov di, [esp+6] push bx push [ofs] push ds pop es call load_attr pop [ofs] pop si mov bx, 8000h push bx push si call read_attr_full pop si pop bx add dx, bx mov ax, [esp+4] pop es .1: cmp [bx], ax jnz .continue1 ; only unnamed $DATA attributes! cmp ax, 80h jnz @f cmp byte [bx+6], 0 jnz .continue1 @@: cmp dword [bx+10h], 0 jz .continue1 cmp dword [bx+8], 0 jnz @f push ax mov ax, [esp+2] cmp ax, [ofs] pop ax jnz .continue1 @@: pushad mov eax, [bx+10h] mov bx, dx push [ofs] push es push ds pop es call read_file_record pop es pop [ofs] popad pushad mov di, dx add di, [di+14h] .2: call find_attr.1 mov eax, [bx+8] cmp eax, [di+10h] jnz .2 mov si, di mov di, [esp+20h] sub di, 4 call process_mcb_nonres mov [esp+20h], di popad .continue1: add bx, [bx+4] cmp bx, dx jb .1 .nofragmented: pop bx pop ax pop di cmp bx, [ofs] jnz @f dec bx stc @@: ret read_attr_full: ; in: si->decoded attribute data, bx->buffer ; out: edx=length in bytes lodsb cmp al, 0 jnz .nonresident ; resident lodsw movzx edx, ax xchg ax, cx mov di, bx rep movsb ret .nonresident: ; nonresident :-) xor edx, edx .loop: lodsd xchg ecx, eax jecxz .loopend lodsd xchg edi, eax ; read ecx clusters from cluster edi to es:bx .intloop: push ecx ; read 1 cluster from physical cluster edi to es:bx mov ecx, [cluster_size] mov eax, edi mul ecx push bx call relative_read pop bx pop ecx inc edi mov eax, [cluster_size] add edx, eax shr eax, 4 mov bp, es add bp, ax mov es, bp loop .intloop jmp .loop .loopend: mov es, cx ret read_file_record: ; in: eax=index of record, bx=buffer mov si, 700h mov ecx, [frs_size] mul ecx push bx push [cur_obj] mov [cur_obj], mft_string call read_attr pop [cur_obj] pop di call restore_usa ret read_attr: ; in: edx:eax=offset in bytes, ecx=size in bytes, bx=buffer, si=attribute push invalid_read_request_string cmp byte [si], 0 jnz .nonresident test edx, edx jnz find_error_sp cmp eax, 10000h jae find_error_sp cmp ecx, 10000h jae find_error_sp cmp ax, [si+2] jae find_error_sp cmp cx, [si+2] ja find_error_sp add si, 3 add si, ax mov di, bx rep movsb pop ax ret .nonresident: mov edi, [cluster_size] div edi mov [ofs], dx add cx, dx push eax xchg eax, ecx xor edx, edx dec eax div edi inc eax xchg eax, ecx pop eax add si, 1 xor edx, edx push bx ; eax=offset in clusters, ecx=size in clusters .scan: mov ebx, [si] test ebx, ebx jz .notfound add edx, ebx add si, 8 cmp eax, edx jae .scan mov edi, [si-4] ; now edx=end of block, ebx=length of block, edi=start of block on disk ; eax=required offset, ecx=required length push edx push edi sub edx, eax add edi, ebx sub edi, edx cmp edx, ecx jb @f mov edx, ecx @@: ; read (edx) clusters from (edi=disk offset in clusters) to ([esp+8]) cmp [ofs], 0 jnz .ofs_read .cont: pushad movzx ebx, byte [50Dh] ; xchg eax, edx ; mul ebx xchg ax, dx mul bx xchg cx, ax xchg eax, edi mul ebx mov bx, [esp+8+20h] call relative_read mov [esp+8+20h], bx popad .cont2: add eax, edx sub ecx, edx .cont3: pop edi pop edx jnz .scan pop bx pop ax ret .ofs_read: push ecx movzx ecx, byte [50Dh] ; bpb_sects_per_clust mov eax, edi push edx mul ecx push 1000h pop es xor bx, bx call relative_read mov cx, bx push si push di mov si, [ofs] mov di, [esp+8+12] sub cx, si push ds push es pop ds pop es rep movsb mov [esp+8+12], di push es pop ds pop di pop si pop edx pop ecx inc edi mov [ofs], 0 inc eax dec ecx jz .cont3 dec edx jnz .cont jmp .cont2 .notfound: mov si, invalid_read_request_string jmp find_error_si ntfs_parse_dir: ; in: eax=directory iRecord, [word sp+2]=filename ; out: si=$DATA attribute of file mov bx, [free] mov [dir], bx push bx call read_file_record mov ax, word [frs_size] add [free], ax pop di ; find attributes $INDEX_ROOT, $INDEX_ALLOCATION, $BITMAP mov ax, 90h ; $INDEX_ROOT push di mov bx, [free] mov [index_root], bx call load_attr mov si, noindex_string jc find_error_si mov [free], bx pop di mov ax, 0A0h ; $INDEX_ALLOCATION mov bx, [free] mov [index_alloc], bx call load_attr jnc @f mov [index_alloc], 0 @@: mov [free], bx ; search for entry mov si, [index_root] mov bx, [free] call read_attr_full mov ebp, [bx+8] ; subnode_size add bx, 10h .scan_record: add bx, [bx] .scan: test byte [bx+0Ch], 2 jnz .not_found mov si, [esp+2] movzx cx, byte [bx+50h] ; namelen lea di, [bx+52h] ; name xor ax, ax @@: lodsb cmp al, 'a' jb .notletter cmp al, 'z' ja .notletter or byte [di], 20h .notletter: scasw loopz @b jb .not_found ja @f cmp byte [esi], 0 jz .file_found @@: add bx, [bx+8] jmp .scan .not_found: test byte [bx+0Ch], 1 jz file_not_found cmp [index_alloc], 0 jz file_not_found add bx, [bx+8] mov eax, [bx-8] mul [cluster_size] mov si, [index_alloc] mov ecx, ebp mov bx, [free] call read_attr mov di, [free] call restore_usa mov bx, [free] add bx, 18h jmp .scan_record .file_found: mov si, [esp+2] mov [cur_obj], si cmp byte [esp+4], 0 jz .need_file mov si, notdir_string test byte [bx+48h+3], 10h jz find_error_si mov eax, [bx] mov bx, [dir] mov [free], bx ret 2 .need_file: mov si, directory_string test byte [bx+48h+3], 10h ; directory? jnz find_error_si ; read entry mov eax, [bx] mov bx, [dir] mov [free], bx mov bx, 4000h push bx call read_file_record pop di mov ax, 80h push 2000h pop es xor bx, bx call load_attr mov si, nodata_string jz find_error_si mov [free], bx ret 2