;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ;; ;; Copyright (C) KolibriOS team 2004-2016. All rights reserved. ;; ;; Distributed under terms of the GNU General Public License ;; ;; ;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; $Revision$ ; NTFS driver ; Basic concepts: ; File is a FileRecord in the $MFT. ; $MFT is a file, that consists of FileRecords and starts with FileRecord of itself. ; FileRecord (FILE) consists of a header and attributes. ; Attribute consists of a header and a body. ; Attribute's body can be inside (resident) or outside of FileRecord. ; File's data is a body of $Data (80h) attribute. ; FileRecords is a data of the $MFT file. ; Directory is a file, that consists of index nodes. ; Resident index node is always located in a body of $IndexRoot (90h) attribute. ; Body of $IndexAllocation (A0h) attribute is always non resident ; and consists of IndexRecords. ; IndexRecord (INDX) consists of a header and an index node. ; Index node consists of a header and indexes. ; Index consists of a header and a copy of indexed attribute's body. ; Directories index $Filename (30h) attribute of all existing files. ; $IndexRoot and $IndexAllocation attributes of a directory has a name — $I30. ; Offsets: ; record header updateSequenceOffset = 4 updateSequenceSize = 6 reuseCounter = 16 hardLinkCounter = 12h attributeOffset = 14h recordFlags = 16h recordRealSize = 18h recordAllocatedSize = 1ch baseRecordReference = 20h ; for auxiliary records baseRecordReuse = 26h newAttributeID = 28h ; attribute header attributeType = 0 sizeWithHeader = 4 nonResidentFlag = 8 nameLength = 9 nameOffset = 10 attributeFlags = 12 attributeID = 14 ; resident attribute header sizeWithoutHeader = 10h ; attributeOffset = 14h indexedFlag = 16h ; non resident attribute header firstVCN = 10h lastVCN = 18h dataRunsOffset = 20h attributeAllocatedSize = 28h attributeRealSize = 30h initialDataSize = 38h ; $IndexRoot collationRule = 4 indexRecordSize = 8 indexRecordSizeClus = 12 ; node header indexOffset = 0 nodeRealSize = 4 nodeAllocatedSize = 8 ; $Filename index fileRecordReference = 0 fileReferenceReuse = 6 indexAllocatedSize = 8 indexRawSize = 10 indexFlags = 12 directoryRecordReference = 16 directoryReferenceReuse = 16h fileAllocatedSize = 38h fileRealSize = 40h fileFlags = 48h fileNameLength = 50h struct NTFS PARTITION Lock MUTEX ? ; Currently operations with one partition ; can not be executed in parallel since the legacy code is not ready. sectors_per_cluster dd ? mft_cluster dd ? ; location mftmirr_cluster dd ? ; location frs_size dd ? ; in bytes frs_buffer dd ? ; MFT fileRecord buffer mft_retrieval dd ? mft_retrieval_size dd ? mft_retrieval_alloc dd ? mft_retrieval_end dd ? cur_index_size dd ? ; in sectors cur_index_buf dd ? ; index node buffer BitmapBuffer dd ? BitmapTotalSize dd ? ; bytes reserved BitmapSize dd ? ; bytes readen BitmapLocation dd ? ; starting sector BitmapStart dd ? ; first byte after area, reserved for MFT mftBitmapBuffer dd ? ; one cluster mftBitmapSize dd ? ; bytes readen mftBitmapLocation dd ? ; starting sector ntfs_cur_attr dd ? ; attribute type ntfs_cur_iRecord dd ? ; number of fileRecord in MFT ntfs_cur_offs dd ? ; attribute VCN in sectors ntfs_cur_size dd ? ; max sectors to read ntfs_cur_buf dd ? ntfs_cur_read dd ? ; bytes readen ntfsLastRead dd ? ; last readen block of sectors newMftRecord dd ? ; number of fileRecord in MFT fileDataStart dd ? ; starting cluster fileDataSize dd ? ; in clusters fileRealSize dd ? ; in bytes indexOffset dd ? nodeLastRead dd ? ntfs_bCanContinue db ? ntfsFolder db ? ntfsWriteAttr db ? ; Warning: Don't forget to turn off!!! ntfsFragmentCount db ? cur_subnode_size dd ? ntfs_attr_iRecord dd ? ntfs_attr_iBaseRecord dd ? ntfs_attr_offs dd ? ntfs_attr_list dd ? ntfs_attr_size dq ? ntfs_cur_tail dd ? ntfs_attrlist_buf rb 0x400 ntfs_attrlist_mft_buf rb 0x400 ntfs_bitmap_buf rb 0x400 ends ; NTFS external functions ; in: ; ebx -> parameter structure of sysfunc 70 ; ebp -> NTFS structure ; [esi]+[esp+4] = name ; out: ; eax, ebx = return values for sysfunc 70 iglobal align 4 ntfs_user_functions: dd ntfs_free dd (ntfs_user_functions_end - ntfs_user_functions - 4) / 4 dd ntfs_ReadFile dd ntfs_ReadFolder dd ntfs_CreateFile dd ntfs_WriteFile dd ntfs_SetFileEnd dd ntfs_GetFileInfo dd ntfs_SetFileInfo dd 0 dd ntfs_Delete dd ntfs_CreateFolder ntfs_user_functions_end: endg ntfs_test_bootsec: ; in: ebx -> buffer, edx = size of partition ; out: CF=1 -> invalid ; 1. Name=='NTFS ' cmp dword [ebx+3], 'NTFS' jnz .no cmp dword [ebx+7], ' ' jnz .no ; 2. Number of bytes per sector is the same as for physical device ; (that is, 0x200 for hard disk) cmp word [ebx+11], 0x200 jnz .no ; 3. Number of sectors per cluster must be power of 2 movzx eax, byte [ebx+13] dec eax js .no test al, [ebx+13] jnz .no ; 4. FAT parameters must be zero cmp word [ebx+14], 0 jnz .no cmp dword [ebx+16], 0 jnz .no cmp byte [ebx+20], 0 jnz .no cmp word [ebx+22], 0 jnz .no cmp dword [ebx+32], 0 jnz .no ; 5. Number of sectors <= partition size cmp dword [ebx+0x2C], 0 ja .no cmp [ebx+0x28], edx ja .no ; 6. $MFT and $MFTMirr clusters must be within partition cmp dword [ebx+0x34], 0 ja .no push edx movzx eax, byte [ebx+13] mul dword [ebx+0x30] test edx, edx pop edx jnz .no cmp eax, edx ja .no cmp dword [ebx+0x3C], 0 ja .no push edx movzx eax, byte [ebx+13] mul dword [ebx+0x38] test edx, edx pop edx jnz .no cmp eax, edx ja .no ; 7. Clusters per FRS must be either power of 2 or between -31 and -9 movsx eax, byte [ebx+0x40] cmp al, -31 jl .no cmp al, -9 jle @f dec eax js .no test [ebx+0x40], al jnz .no @@: ; 8. Same for clusters per IndexAllocationBuffer movsx eax, byte [ebx+0x44] cmp al, -31 jl .no cmp al, -9 jle @f dec eax js .no test [ebx+0x44], al jnz .no @@: ; OK, this is correct NTFS bootsector clc ret .no: ; No, this bootsector isn't NTFS stc ret ntfs_create_partition: cmp dword [esi+DISK.MediaInfo.SectorSize], 512 jnz .nope mov edx, dword [ebp+PARTITION.Length] cmp dword [esp+4], 0 jz .boot_read_ok add ebx, 512 lea eax, [edx-1] call fs_read32_sys test eax, eax jnz @f call ntfs_test_bootsec jnc .ntfs_setup @@: mov eax, edx shr eax, 1 call fs_read32_sys test eax, eax jnz .nope .boot_read_ok: call ntfs_test_bootsec jnc .ntfs_setup .nope: xor eax, eax jmp .exit ; By given bootsector, initialize some NTFS variables .ntfs_setup: movi eax, sizeof.NTFS call malloc test eax, eax jz .exit mov ecx, dword [ebp+PARTITION.FirstSector] mov dword [eax+NTFS.FirstSector], ecx mov ecx, dword [ebp+PARTITION.FirstSector+4] mov dword [eax+NTFS.FirstSector+4], ecx mov ecx, [ebp+PARTITION.Disk] mov [eax+NTFS.Disk], ecx mov [eax+NTFS.FSUserFunctions], ntfs_user_functions mov [eax+NTFS.ntfsWriteAttr], 0 push ebx ebp esi mov ebp, eax lea ecx, [ebp+NTFS.Lock] call mutex_init movzx eax, byte [ebx+13] mov [ebp+NTFS.sectors_per_cluster], eax mov eax, [ebx+0x28] mov dword [ebp+NTFS.Length], eax and dword [ebp+NTFS.Length+4], 0 mov eax, [ebx+0x30] mov [ebp+NTFS.mft_cluster], eax mov eax, [ebx+0x38] mov [ebp+NTFS.mftmirr_cluster], eax movsx eax, byte [ebx+0x40] test eax, eax js @f mul [ebp+NTFS.sectors_per_cluster] shl eax, 9 jmp .1 @@: neg eax mov ecx, eax mov eax, 1 shl eax, cl .1: mov [ebp+NTFS.frs_size], eax stdcall kernel_alloc, eax test eax, eax jz .fail_free mov [ebp+NTFS.frs_buffer], eax ; read $MFT disposition mov eax, [ebp+NTFS.mft_cluster] mul [ebp+NTFS.sectors_per_cluster] call ntfs_read_frs_sector test eax, eax jnz .usemirr cmp dword [ebx], 'FILE' jnz .usemirr call ntfs_restore_usa_frs jnc .mftok .usemirr: mov eax, [ebp+NTFS.mftmirr_cluster] mul [ebp+NTFS.sectors_per_cluster] call ntfs_read_frs_sector test eax, eax jnz .fail_free_frs cmp dword [ebx], 'FILE' jnz .fail_free_frs call ntfs_restore_usa_frs jc .fail_free_frs .mftok: ; read $MFT table retrieval information ; start with one page, increase if not enough (when MFT too fragmented) push ebx stdcall kernel_alloc, 0x1000 pop ebx test eax, eax jz .fail_free_frs mov [ebp+NTFS.mft_retrieval], eax and [ebp+NTFS.mft_retrieval_size], 0 mov [ebp+NTFS.mft_retrieval_alloc], 0x1000/8 ; $MFT base record must contain unnamed non-resident $DATA attribute movzx eax, word [ebx+14h] add eax, ebx .scandata: cmp dword [eax], -1 jz .fail_free_mft cmp dword [eax], 0x80 jnz @f cmp byte [eax+9], 0 jz .founddata @@: add eax, [eax+4] jmp .scandata .founddata: cmp byte [eax+8], 0 jz .fail_free_mft ; load first portion of $DATA attribute retrieval information mov edx, [eax+0x18] mov [ebp+NTFS.mft_retrieval_end], edx mov esi, eax movzx eax, word [eax+0x20] add esi, eax sub esp, 10h .scanmcb: call ntfs_decode_mcb_entry jnc .scanmcbend call .get_mft_retrieval_ptr mov edx, [esp] ; block length mov [eax], edx mov edx, [esp+8] ; block addr (relative) mov [eax+4], edx inc [ebp+NTFS.mft_retrieval_size] jmp .scanmcb .scanmcbend: add esp, 10h ; there may be other portions of $DATA attribute in auxiliary records; ; if they will be needed, they will be loaded later mov [ebp+NTFS.cur_index_size], 0x1000/0x200 stdcall kernel_alloc, 0x1000 test eax, eax jz .fail_free_mft mov [ebp+NTFS.cur_index_buf], eax ; reserve adress space for bitmap buffer and load some part of bitmap mov eax, dword [ebp+NTFS.Length] xor edx, edx div [ebp+NTFS.sectors_per_cluster] shr eax, 3 mov [ebp+NTFS.BitmapTotalSize], eax add eax, 7FFFh and eax, not 7FFFh push eax call alloc_kernel_space test eax, eax jz .failFreeIndex mov [ebp+NTFS.BitmapBuffer], eax mov [ebp+NTFS.ntfs_cur_buf], eax mov eax, [ebp+NTFS.BitmapTotalSize] add eax, [ebp+NTFS.mft_cluster] shr eax, 3+2 ; reserve 1/8 of partition for $MFT shl eax, 2 mov [ebp+NTFS.BitmapStart], eax shr eax, 15 inc eax shl eax, 3 push eax push eax shl eax, 3 mov [ebp+NTFS.ntfs_cur_size], eax call alloc_pages test eax, eax pop ecx jz .failFreeBitmap add eax, 3 mov ebx, [ebp+NTFS.BitmapBuffer] call commit_pages mov [ebp+NTFS.ntfs_cur_iRecord], 6 mov [ebp+NTFS.ntfs_cur_attr], 0x80 mov [ebp+NTFS.ntfs_cur_offs], 0 call ntfs_read_attr jc .failFreeBitmap mov eax, [ebp+NTFS.ntfs_cur_read] mov [ebp+NTFS.BitmapSize], eax mov eax, [ebp+NTFS.ntfsLastRead] mov [ebp+NTFS.BitmapLocation], eax ; read MFT $BITMAP attribute mov eax, [ebp+NTFS.sectors_per_cluster] mov [ebp+NTFS.ntfs_cur_size], eax shl eax, 9 stdcall kernel_alloc, eax test eax, eax jz .failFreeBitmap mov [ebp+NTFS.mftBitmapBuffer], eax mov [ebp+NTFS.ntfs_cur_buf], eax mov [ebp+NTFS.ntfs_cur_iRecord], 0 mov [ebp+NTFS.ntfs_cur_attr], 0xB0 mov [ebp+NTFS.ntfs_cur_offs], 0 call ntfs_read_attr mov eax, [ebp+NTFS.ntfs_cur_read] cmp eax, 4 jc .failFreeBitmapMFT mov [ebp+NTFS.mftBitmapSize], eax mov eax, [ebp+NTFS.ntfsLastRead] mov [ebp+NTFS.mftBitmapLocation], eax mov eax, ebp .pop_exit: pop esi ebp ebx .exit: cmp dword [esp+4], 0 jz @f sub ebx, 512 @@: ret .failFreeBitmapMFT: stdcall kernel_free, [ebx+NTFS.mftBitmapBuffer] .failFreeBitmap: stdcall kernel_free, [ebx+NTFS.BitmapBuffer] .failFreeIndex: stdcall kernel_free, [ebp+NTFS.cur_index_buf] .fail_free_mft: stdcall kernel_free, [ebp+NTFS.mft_retrieval] .fail_free_frs: stdcall kernel_free, [ebp+NTFS.frs_buffer] .fail_free: mov eax, ebp call free xor eax, eax jmp .pop_exit .get_mft_retrieval_ptr: pushad mov eax, [ebp+NTFS.mft_retrieval_size] cmp eax, [ebp+NTFS.mft_retrieval_alloc] jnz .ok add eax, 0x1000/8 mov [ebp+NTFS.mft_retrieval_alloc], eax shl eax, 3 stdcall kernel_alloc, eax test eax, eax jnz @f popad add esp, 14h jmp .fail_free_mft @@: mov esi, [ebp+NTFS.mft_retrieval] mov edi, eax mov ecx, [ebp+NTFS.mft_retrieval_size] add ecx, ecx rep movsd push [ebp+NTFS.mft_retrieval] mov [ebp+NTFS.mft_retrieval], eax call kernel_free mov eax, [ebp+NTFS.mft_retrieval_size] .ok: shl eax, 3 add eax, [ebp+NTFS.mft_retrieval] mov [esp+28], eax popad ret ntfs_free: push ebx mov ebx, eax stdcall kernel_free, [ebx+NTFS.frs_buffer] stdcall kernel_free, [ebx+NTFS.mft_retrieval] stdcall kernel_free, [ebx+NTFS.cur_index_buf] stdcall kernel_free, [ebx+NTFS.mftBitmapBuffer] stdcall kernel_free, [ebx+NTFS.BitmapBuffer] mov eax, ebx pop ebx jmp free ntfs_lock: lea ecx, [ebp+NTFS.Lock] jmp mutex_lock ntfs_unlock: lea ecx, [ebp+NTFS.Lock] jmp mutex_unlock ntfs_read_frs_sector: push ecx mov ebx, [ebp+NTFS.frs_buffer] push ebx mov ecx, [ebp+NTFS.frs_size] shr ecx, 9 push ecx mov ecx, eax @@: mov eax, ecx call fs_read32_sys test eax, eax jnz .fail add ebx, 0x200 inc ecx dec dword [esp] jnz @b pop eax .fail: pop ebx pop ecx ret ntfs_read_attr: ; [ebp+NTFS.ntfsWriteAttr]=1 -> write attribute ; in: ; [ebp+NTFS.ntfs_cur_iRecord] = number of fileRecord ; [ebp+NTFS.ntfs_cur_attr] = attribute type ; [ebp+NTFS.ntfs_cur_offs] = attribute VCN in sectors ; [ebp+NTFS.ntfs_cur_buf] -> buffer for data ; [ebp+NTFS.ntfs_cur_size] = max sectors to read ; out: ; [ebp+NTFS.ntfs_cur_read] = bytes readen ; CF=1 -> failed, eax = disk error code, eax=0 -> something with FS xor eax, eax pushad and [ebp+NTFS.ntfs_cur_read], 0 cmp [ebp+NTFS.ntfs_cur_iRecord], 0 jnz .nomft cmp [ebp+NTFS.ntfs_cur_attr], 0x80 jnz .nomft mov eax, [ebp+NTFS.mft_retrieval_end] inc eax mul [ebp+NTFS.sectors_per_cluster] cmp eax, [ebp+NTFS.ntfs_cur_offs] jbe .nomft ; precalculated part of $Mft $DATA mov esi, [ebp+NTFS.mft_retrieval] mov eax, [ebp+NTFS.ntfs_cur_offs] xor edx, edx div [ebp+NTFS.sectors_per_cluster] ; eax = VCN, edx = offset in sectors from beginning of cluster xor ecx, ecx ; ecx will contain LCN .mftscan: add ecx, [esi+4] sub eax, [esi] jb @f add esi, 8 push eax mov eax, [ebp+NTFS.mft_retrieval_end] shl eax, 3 add eax, [ebp+NTFS.mft_retrieval] cmp eax, esi pop eax jnz .mftscan jmp .nomft @@: push ecx add ecx, eax add ecx, [esi] push eax push edx mov eax, [ebp+NTFS.sectors_per_cluster] mul ecx ; eax = sector on partition pop edx add eax, edx mov ebx, [ebp+NTFS.ntfs_cur_buf] pop ecx neg ecx imul ecx, [ebp+NTFS.sectors_per_cluster] sub ecx, edx mov [ebp+NTFS.ntfsLastRead], eax cmp ecx, [ebp+NTFS.ntfs_cur_size] jb @f mov ecx, [ebp+NTFS.ntfs_cur_size] @@: ; ecx = number of sequential sectors to read push eax call fs_read32_sys pop edx test eax, eax jnz .errread add [ebp+NTFS.ntfs_cur_read], 0x200 dec [ebp+NTFS.ntfs_cur_size] inc [ebp+NTFS.ntfs_cur_offs] add ebx, 0x200 mov [ebp+NTFS.ntfs_cur_buf], ebx lea eax, [edx+1] loop @b pop ecx xor eax, eax xor edx, edx cmp [ebp+NTFS.ntfs_cur_size], eax jz @f add esi, 8 push eax mov eax, [ebp+NTFS.mft_retrieval_end] shl eax, 3 add eax, [ebp+NTFS.mft_retrieval] cmp eax, esi pop eax jz .nomft jmp .mftscan @@: popad ret .errread: pop ecx .errret: mov [esp+28], eax stc popad ret .nomft: ; 1. Read file record. ; N.B. This will do recursive call of read_attr for $MFT::$Data. mov eax, [ebp+NTFS.ntfs_cur_iRecord] mov [ebp+NTFS.ntfs_attr_iRecord], eax and [ebp+NTFS.ntfs_attr_list], 0 or dword [ebp+NTFS.ntfs_attr_size], -1 or dword [ebp+NTFS.ntfs_attr_size+4], -1 or [ebp+NTFS.ntfs_attr_iBaseRecord], -1 call ntfs_read_file_record jc .errret ; 2. Find required attribute. mov eax, [ebp+NTFS.frs_buffer] ; a) For auxiliary records, read base record ; N.B. If base record is present, ; base iRecord may be 0 (for $Mft), but SequenceNumber is nonzero cmp dword [eax+24h], 0 jz @f mov eax, [eax+20h] ; test eax, eax ; jz @f .beginfindattr: mov [ebp+NTFS.ntfs_attr_iRecord], eax call ntfs_read_file_record jc .errret @@: ; b) Scan for required attribute and for $ATTR_LIST mov eax, [ebp+NTFS.frs_buffer] movzx ecx, word [eax+14h] add eax, ecx mov ecx, [ebp+NTFS.ntfs_cur_attr] and [ebp+NTFS.ntfs_attr_offs], 0 .scanattr: cmp dword [eax], -1 jz .scandone cmp dword [eax], ecx jz .okattr cmp [ebp+NTFS.ntfs_attr_iBaseRecord], -1 jnz .scancont cmp dword [eax], 0x20 ; $ATTR_LIST jnz .scancont mov [ebp+NTFS.ntfs_attr_list], eax jmp .scancont .okattr: ; ignore named $DATA attributes (aka NTFS streams) cmp ecx, 0x80 jnz @f cmp byte [eax+9], 0 jnz .scancont @@: mov [ebp+NTFS.ntfs_attr_offs], eax .scancont: add eax, [eax+4] jmp .scanattr .continue: pushad and [ebp+NTFS.ntfs_cur_read], 0 .scandone: ; c) Check for required offset and length mov ecx, [ebp+NTFS.ntfs_attr_offs] jecxz .noattr push [ebp+NTFS.ntfs_cur_size] push [ebp+NTFS.ntfs_cur_read] call .doreadattr pop edx pop ecx jc @f cmp [ebp+NTFS.ntfs_bCanContinue], 0 jz @f sub edx, [ebp+NTFS.ntfs_cur_read] neg edx shr edx, 9 sub ecx, edx mov [ebp+NTFS.ntfs_cur_size], ecx jnz .not_in_cur @@: popad ret .noattr: .not_in_cur: cmp [ebp+NTFS.ntfs_cur_attr], 0x20 jz @f mov ecx, [ebp+NTFS.ntfs_attr_list] test ecx, ecx jnz .lookattr .ret_is_attr: and dword [esp+28], 0 cmp [ebp+NTFS.ntfs_attr_offs], 1 ; CF set <=> ntfs_attr_offs == 0 popad ret .lookattr: ; required attribute or required offset was not found in base record; ; it may be present in auxiliary records; ; scan $ATTR_LIST mov eax, [ebp+NTFS.ntfs_attr_iBaseRecord] cmp eax, -1 jz @f call ntfs_read_file_record jc .errret or [ebp+NTFS.ntfs_attr_iBaseRecord], -1 @@: push [ebp+NTFS.ntfs_cur_offs] push [ebp+NTFS.ntfs_cur_size] push [ebp+NTFS.ntfs_cur_read] push [ebp+NTFS.ntfs_cur_buf] push dword [ebp+NTFS.ntfs_attr_size] push dword [ebp+NTFS.ntfs_attr_size+4] or dword [ebp+NTFS.ntfs_attr_size], -1 or dword [ebp+NTFS.ntfs_attr_size+4], -1 and [ebp+NTFS.ntfs_cur_offs], 0 mov [ebp+NTFS.ntfs_cur_size], 2 and [ebp+NTFS.ntfs_cur_read], 0 lea eax, [ebp+NTFS.ntfs_attrlist_buf] cmp [ebp+NTFS.ntfs_cur_iRecord], 0 jnz @f lea eax, [ebp+NTFS.ntfs_attrlist_mft_buf] @@: mov [ebp+NTFS.ntfs_cur_buf], eax push eax call .doreadattr pop esi mov edx, 1 pop dword [ebp+NTFS.ntfs_attr_size+4] pop dword [ebp+NTFS.ntfs_attr_size] mov ecx, [ebp+NTFS.ntfs_cur_read] pop [ebp+NTFS.ntfs_cur_buf] pop [ebp+NTFS.ntfs_cur_read] pop [ebp+NTFS.ntfs_cur_size] pop [ebp+NTFS.ntfs_cur_offs] jc .errret or edi, -1 lea ecx, [ecx+esi-1Ah] .scanliststart: push ecx mov eax, [ebp+NTFS.ntfs_cur_attr] .scanlist: cmp esi, [esp] jae .scanlistdone cmp eax, [esi] jz @f .scanlistcont: movzx ecx, word [esi+4] add esi, ecx jmp .scanlist @@: ; ignore named $DATA attributes (aka NTFS streams) cmp eax, 0x80 jnz @f cmp byte [esi+6], 0 jnz .scanlistcont @@: push eax mov eax, [esi+8] test eax, eax jnz .testf mov eax, dword [ebp+NTFS.ntfs_attr_size] and eax, dword [ebp+NTFS.ntfs_attr_size+4] cmp eax, -1 jnz .testfz ; if attribute is in auxiliary records, its size is defined only in first mov eax, [esi+10h] call ntfs_read_file_record jnc @f .errret_pop: pop ecx ecx jmp .errret .errret2_pop: xor eax, eax jmp .errret_pop @@: mov eax, [ebp+NTFS.frs_buffer] movzx ecx, word [eax+14h] add eax, ecx mov ecx, [ebp+NTFS.ntfs_cur_attr] @@: cmp dword [eax], -1 jz .errret2_pop cmp dword [eax], ecx jz @f .l1: add eax, [eax+4] jmp @b @@: cmp eax, 0x80 jnz @f cmp byte [eax+9], 0 jnz .l1 @@: cmp byte [eax+8], 0 jnz .sdnores mov eax, [eax+10h] mov dword [ebp+NTFS.ntfs_attr_size], eax and dword [ebp+NTFS.ntfs_attr_size+4], 0 jmp .testfz .sdnores: mov ecx, [eax+30h] mov dword [ebp+NTFS.ntfs_attr_size], ecx mov ecx, [eax+34h] mov dword [ebp+NTFS.ntfs_attr_size+4], ecx .testfz: xor eax, eax .testf: imul eax, [ebp+NTFS.sectors_per_cluster] cmp eax, [ebp+NTFS.ntfs_cur_offs] pop eax ja @f mov edi, [esi+10h] ; keep previous iRecord jmp .scanlistcont @@: pop ecx .scanlistfound: cmp edi, -1 jnz @f popad ret @@: mov eax, [ebp+NTFS.ntfs_cur_iRecord] mov [ebp+NTFS.ntfs_attr_iBaseRecord], eax mov eax, edi jmp .beginfindattr .scanlistdone: pop ecx sub ecx, ebp sub ecx, NTFS.ntfs_attrlist_buf-1Ah cmp [ebp+NTFS.ntfs_cur_iRecord], 0 jnz @f sub ecx, NTFS.ntfs_attrlist_mft_buf-NTFS.ntfs_attrlist_buf @@: cmp ecx, 0x400 jnz .scanlistfound inc edx push esi edi lea esi, [ebp+NTFS.ntfs_attrlist_buf+0x200] lea edi, [ebp+NTFS.ntfs_attrlist_buf] cmp [ebp+NTFS.ntfs_cur_iRecord], 0 jnz @f lea esi, [ebp+NTFS.ntfs_attrlist_mft_buf+0x200] lea edi, [ebp+NTFS.ntfs_attrlist_mft_buf] @@: mov ecx, 0x200/4 rep movsd mov eax, edi pop edi esi sub esi, 0x200 push [ebp+NTFS.ntfs_cur_offs] push [ebp+NTFS.ntfs_cur_size] push [ebp+NTFS.ntfs_cur_read] push [ebp+NTFS.ntfs_cur_buf] push dword [ebp+NTFS.ntfs_attr_size] push dword [ebp+NTFS.ntfs_attr_size+4] or dword [ebp+NTFS.ntfs_attr_size], -1 or dword [ebp+NTFS.ntfs_attr_size+4], -1 mov [ebp+NTFS.ntfs_cur_offs], edx mov [ebp+NTFS.ntfs_cur_size], 1 and [ebp+NTFS.ntfs_cur_read], 0 mov [ebp+NTFS.ntfs_cur_buf], eax mov ecx, [ebp+NTFS.ntfs_attr_list] push esi edx edi call .doreadattr pop edi edx esi mov ecx, [ebp+NTFS.ntfs_cur_read] pop dword [ebp+NTFS.ntfs_attr_size+4] pop dword [ebp+NTFS.ntfs_attr_size] pop [ebp+NTFS.ntfs_cur_buf] pop [ebp+NTFS.ntfs_cur_read] pop [ebp+NTFS.ntfs_cur_size] pop [ebp+NTFS.ntfs_cur_offs] jc .errret lea ecx, [ecx+ebp+NTFS.ntfs_attrlist_buf+0x200-0x1A] cmp [ebp+NTFS.ntfs_cur_iRecord], 0 jnz .scanliststart add ecx, NTFS.ntfs_attrlist_mft_buf-NTFS.ntfs_attrlist_buf jmp .scanliststart .doreadattr: mov [ebp+NTFS.ntfs_bCanContinue], 0 cmp byte [ecx+8], 0 jnz .nonresident mov eax, [ecx+10h] ; length mov esi, eax mov edx, [ebp+NTFS.ntfs_cur_offs] shr eax, 9 cmp eax, edx jb .okret shl edx, 9 sub esi, edx movzx eax, word [ecx+14h] add edx, eax add edx, ecx ; edx -> data mov eax, [ebp+NTFS.ntfs_cur_size] cmp eax, (0xFFFFFFFF shr 9)+1 jbe @f mov eax, (0xFFFFFFFF shr 9)+1 @@: shl eax, 9 cmp eax, esi jbe @f mov eax, esi @@: ; eax = length, edx -> data mov [ebp+NTFS.ntfs_cur_read], eax mov ecx, eax mov eax, edx mov ebx, [ebp+NTFS.ntfs_cur_buf] call memmove and [ebp+NTFS.ntfs_cur_size], 0 ; CF=0 ret .nonresident: ; Not all auxiliary records contain correct FileSize info mov eax, dword [ebp+NTFS.ntfs_attr_size] mov edx, dword [ebp+NTFS.ntfs_attr_size+4] push eax and eax, edx cmp eax, -1 pop eax jnz @f mov eax, [ecx+30h] ; FileSize mov edx, [ecx+34h] mov dword [ebp+NTFS.ntfs_attr_size], eax mov dword [ebp+NTFS.ntfs_attr_size+4], edx @@: add eax, 0x1FF adc edx, 0 shrd eax, edx, 9 sub eax, [ebp+NTFS.ntfs_cur_offs] ja @f ; return with nothing read and [ebp+NTFS.ntfs_cur_size], 0 .okret: clc ret @@: ; reduce read length and [ebp+NTFS.ntfs_cur_tail], 0 cmp [ebp+NTFS.ntfs_cur_size], eax jb @f mov [ebp+NTFS.ntfs_cur_size], eax mov eax, dword [ebp+NTFS.ntfs_attr_size] and eax, 0x1FF mov [ebp+NTFS.ntfs_cur_tail], eax @@: cmp [ebp+NTFS.ntfs_cur_size], 0 jz .okret mov eax, [ebp+NTFS.ntfs_cur_offs] xor edx, edx div [ebp+NTFS.sectors_per_cluster] sub eax, [ecx+10h] ; first_vbo jb .okret ; eax = cluster, edx = starting sector cmp [ebp+NTFS.ntfs_cur_attr], 0x80 jnz .sys cmp [ebp+NTFS.ntfs_cur_iRecord], 0 jz .sys push fs_read64_app cmp [ebp+NTFS.ntfsWriteAttr], 1 jnz @f mov dword[esp], fs_write64_app jmp @f .sys: push fs_read64_sys @@: sub esp, 10h movzx esi, word [ecx+20h] ; mcb_info_ofs add esi, ecx xor edi, edi mov [ebp+NTFS.ntfsFragmentCount], 0 .readloop: call ntfs_decode_mcb_entry jnc .break add edi, [esp+8] sub eax, [esp] jae .readloop push ecx push eax add eax, [esp+8] add eax, edi imul eax, [ebp+NTFS.sectors_per_cluster] add eax, edx pop ecx neg ecx imul ecx, [ebp+NTFS.sectors_per_cluster] sub ecx, edx cmp ecx, [ebp+NTFS.ntfs_cur_size] jb @f mov ecx, [ebp+NTFS.ntfs_cur_size] @@: mov ebx, [ebp+NTFS.ntfs_cur_buf] mov [ebp+NTFS.ntfsLastRead], eax push ecx xor edx, edx call dword[esp+18h] pop ecx test eax, eax jnz .errread2 sub [ebp+NTFS.ntfs_cur_size], ecx add [ebp+NTFS.ntfs_cur_offs], ecx shl ecx, 9 add [ebp+NTFS.ntfs_cur_read], ecx add [ebp+NTFS.ntfs_cur_buf], ecx inc [ebp+NTFS.ntfsFragmentCount] pop ecx xor eax, eax xor edx, edx cmp [ebp+NTFS.ntfs_cur_size], 0 jnz .readloop add esp, 14h mov eax, [ebp+NTFS.ntfs_cur_tail] test eax, eax jz @f sub eax, 0x200 add [ebp+NTFS.ntfs_cur_read], eax @@: clc ret .errread2: pop ecx add esp, 14h stc ret .break: add esp, 14h ; CF=0 mov [ebp+NTFS.ntfs_bCanContinue], 1 ret ntfs_read_file_record: ; in: eax = iRecord ; out: [ebp+NTFS.frs_buffer] -> file record ; CF=1 -> failed, eax = disk error code, eax=0 -> something with FS ; Read attr $DATA of $Mft, starting from eax*[ebp+NTFS.frs_size] push ecx edx mov ecx, [ebp+NTFS.frs_size] mul ecx shrd eax, edx, 9 shr edx, 9 jnz .errret push [ebp+NTFS.ntfs_attr_iRecord] push [ebp+NTFS.ntfs_attr_iBaseRecord] push [ebp+NTFS.ntfs_attr_offs] push [ebp+NTFS.ntfs_attr_list] push dword [ebp+NTFS.ntfs_attr_size+4] push dword [ebp+NTFS.ntfs_attr_size] push [ebp+NTFS.ntfs_cur_iRecord] push [ebp+NTFS.ntfs_cur_attr] push [ebp+NTFS.ntfs_cur_offs] push [ebp+NTFS.ntfs_cur_size] push [ebp+NTFS.ntfs_cur_buf] push [ebp+NTFS.ntfs_cur_read] mov [ebp+NTFS.ntfs_cur_attr], 0x80 ; $DATA and [ebp+NTFS.ntfs_cur_iRecord], 0 ; $Mft mov [ebp+NTFS.ntfs_cur_offs], eax shr ecx, 9 mov [ebp+NTFS.ntfs_cur_size], ecx mov eax, [ebp+NTFS.frs_buffer] mov [ebp+NTFS.ntfs_cur_buf], eax call ntfs_read_attr mov edx, [ebp+NTFS.ntfs_cur_read] pop [ebp+NTFS.ntfs_cur_read] pop [ebp+NTFS.ntfs_cur_buf] pop [ebp+NTFS.ntfs_cur_size] pop [ebp+NTFS.ntfs_cur_offs] pop [ebp+NTFS.ntfs_cur_attr] pop [ebp+NTFS.ntfs_cur_iRecord] pop dword [ebp+NTFS.ntfs_attr_size] pop dword [ebp+NTFS.ntfs_attr_size+4] pop [ebp+NTFS.ntfs_attr_list] pop [ebp+NTFS.ntfs_attr_offs] pop [ebp+NTFS.ntfs_attr_iBaseRecord] pop [ebp+NTFS.ntfs_attr_iRecord] jc .ret cmp edx, [ebp+NTFS.frs_size] jnz .errret mov eax, [ebp+NTFS.frs_buffer] cmp dword [eax], 'FILE' jnz .errret push ebx mov ebx, eax call ntfs_restore_usa_frs pop ebx jc .errret .ret: pop edx ecx ret .errret: pop edx ecx xor eax, eax stc ret ntfs_restore_usa_frs: mov eax, [ebp+NTFS.frs_size] ntfs_restore_usa: ; in: ; ebx -> record ; eax = size in bytes pushad shr eax, 9 mov ecx, eax inc eax cmp [ebx+6], ax jnz .err movzx eax, word [ebx+4] lea esi, [eax+ebx] lodsw mov edx, eax lea edi, [ebx+0x1FE] @@: cmp [edi], dx jnz .err lodsw stosw add edi, 0x1FE loop @b popad clc ret .err: popad stc ret ntfs_decode_mcb_entry: ; in: ; esi -> MCB entry ; esp -> buffer (16 bytes) ; out: ; esi -> next MCB entry ; esp -> data run size ; esp+8 -> cluster (delta) ; CF=0 -> MCB end push eax ecx edi lea edi, [esp+16] xor eax, eax lodsb test al, al jz .end mov ecx, eax and ecx, 0xF cmp ecx, 8 ja .end push ecx rep movsb pop ecx sub ecx, 8 neg ecx cmp byte [esi-1], 80h jae .end push eax xor eax, eax rep stosb pop ecx shr ecx, 4 cmp ecx, 8 ja .end push ecx rep movsb pop ecx sub ecx, 8 neg ecx cmp byte [esi-1], 80h cmc sbb eax, eax rep stosb stc .end: pop edi ecx eax ret unichar_toupper: push eax call uni2ansi_char cmp al, '_' jz .unk add esp, 4 call char_toupper jmp ansi2uni_char .unk: pop eax ret ntfs_find_lfn: ; in: [esi]+[esp+4] = name ; out: ; [ebp+NTFS.ntfs_cur_iRecord] = number of MFT fileRecord ; eax -> index in the parent index node ; CF=1 -> file not found, eax=0 -> error mov [ebp+NTFS.ntfs_cur_iRecord], 5 ; start parse from root cluster .doit2: mov [ebp+NTFS.ntfs_cur_attr], 0x90 ; $INDEX_ROOT and [ebp+NTFS.ntfs_cur_offs], 0 mov eax, [ebp+NTFS.cur_index_size] mov [ebp+NTFS.ntfs_cur_size], eax mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.ntfs_cur_buf], eax call ntfs_read_attr mov eax, 0 jnc @f .ret: ret 4 @@: cmp [ebp+NTFS.ntfs_cur_read], 0x20 jc .ret pushad mov esi, [ebp+NTFS.cur_index_buf] mov eax, [esi+14h] add eax, 10h cmp [ebp+NTFS.ntfs_cur_read], eax jae .readok1 add eax, 1FFh shr eax, 9 cmp eax, [ebp+NTFS.cur_index_size] ja @f .stc_ret: popad stc ret 4 @@: ; reallocate push eax stdcall kernel_free, [ebp+NTFS.cur_index_buf] pop eax mov [ebp+NTFS.cur_index_size], eax stdcall kernel_alloc, eax test eax, eax jnz @f and [ebp+NTFS.cur_index_size], 0 and [ebp+NTFS.cur_index_buf], 0 jmp .stc_ret @@: mov [ebp+NTFS.cur_index_buf], eax popad jmp .doit2 .readok1: mov edx, [esi+8] ; subnode_size shr edx, 9 cmp edx, [ebp+NTFS.cur_index_size] jbe .ok2 push esi edx stdcall kernel_alloc, edx pop edx esi test eax, eax jz .stc_ret mov edi, eax mov ecx, [ebp+NTFS.cur_index_size] shl ecx, 9-2 rep movsd mov esi, eax mov [ebp+NTFS.cur_index_size], edx push esi edx stdcall kernel_free, [ebp+NTFS.cur_index_buf] pop edx esi mov [ebp+NTFS.cur_index_buf], esi .ok2: add esi, 10h mov edi, [esp+4] ; edi -> name, esi -> current index data, edx = subnode size .scanloop: add esi, [esi] .scanloopint: test byte [esi+0Ch], 2 jnz .subnode push esi add esi, 0x52 movzx ecx, byte [esi-2] push edi @@: lodsw call unichar_toupper push eax mov al, [edi] inc edi cmp al, '/' jz .slash call char_toupper call ansi2uni_char cmp ax, [esp] pop eax loopz @b jz .found pop edi pop esi jb .subnode .scanloopcont: movzx eax, word [esi+8] add esi, eax jmp .scanloopint .slash: pop eax pop edi pop esi .subnode: test byte [esi+0Ch], 1 jz .notfound movzx eax, word [esi+8] mov eax, [esi+eax-8] imul eax, [ebp+NTFS.sectors_per_cluster] mov [ebp+NTFS.ntfs_cur_offs], eax mov [ebp+NTFS.ntfs_cur_attr], 0xA0 ; $INDEX_ALLOCATION mov [ebp+NTFS.ntfs_cur_size], edx mov eax, [ebp+NTFS.cur_index_buf] mov esi, eax mov [ebp+NTFS.ntfs_cur_buf], eax push edx call ntfs_read_attr pop edx mov eax, edx shl eax, 9 cmp [ebp+NTFS.ntfs_cur_read], eax jnz .err cmp dword [esi], 'INDX' jnz .err mov [ebp+NTFS.ntfs_cur_buf], esi mov ebx, esi call ntfs_restore_usa jc .err add esi, 0x18 jmp .scanloop .notfound: mov [esp+1Ch], esi .err: popad stc ret 4 .found: cmp byte [edi], 0 jz .done cmp byte [edi], '/' jz .next pop edi pop esi jmp .scanloopcont .done: .next: pop esi pop esi mov eax, [esi] mov [ebp+NTFS.ntfs_cur_iRecord], eax mov [esp+1Ch], esi mov [esp+4], edi popad inc esi cmp byte [esi-1], 0 jnz .doit2 cmp dword [esp+4], 0 jz @f mov esi, [esp+4] mov dword [esp+4], 0 jmp .doit2 @@: ret 4 ;---------------------------------------------------------------- ntfs_ReadFile: cmp byte [esi], 0 jnz @f or ebx, -1 movi eax, ERROR_ACCESS_DENIED ret @@: call ntfs_lock stdcall ntfs_find_lfn, [esp+4] jnc .found call ntfs_unlock or ebx, -1 movi eax, ERROR_FILE_NOT_FOUND ret .found: mov [ebp+NTFS.ntfs_cur_attr], 0x80 ; $DATA and [ebp+NTFS.ntfs_cur_offs], 0 and [ebp+NTFS.ntfs_cur_size], 0 call ntfs_read_attr jnc @f call ntfs_unlock or ebx, -1 movi eax, ERROR_ACCESS_DENIED ret @@: pushad and dword [esp+10h], 0 xor eax, eax cmp dword [ebx+8], 0x200 jb @f .eof0: popad xor ebx, ebx .eof: push ERROR_END_OF_FILE call ntfs_unlock pop eax ret @@: mov ecx, [ebx+12] mov edx, [ebx+16] mov eax, [ebx+4] test eax, 0x1FF jz .alignedstart push edx mov edx, [ebx+8] shrd eax, edx, 9 pop edx mov [ebp+NTFS.ntfs_cur_offs], eax mov [ebp+NTFS.ntfs_cur_size], 1 lea eax, [ebp+NTFS.ntfs_bitmap_buf] mov [ebp+NTFS.ntfs_cur_buf], eax call ntfs_read_attr.continue mov eax, [ebx+4] and eax, 0x1FF lea esi, [ebp+NTFS.ntfs_bitmap_buf+eax] sub eax, [ebp+NTFS.ntfs_cur_read] jae .eof0 neg eax push ecx cmp ecx, eax jb @f mov ecx, eax @@: mov [esp+10h+4], ecx mov edi, edx rep movsb mov edx, edi pop ecx sub ecx, [esp+10h] jnz @f .retok: popad call ntfs_unlock xor eax, eax ret @@: cmp [ebp+NTFS.ntfs_cur_read], 0x200 jz .alignedstart .eof_ebx: popad jmp .eof .alignedstart: mov eax, [ebx+4] push edx mov edx, [ebx+8] add eax, 511 adc edx, 0 shrd eax, edx, 9 pop edx mov [ebp+NTFS.ntfs_cur_offs], eax mov [ebp+NTFS.ntfs_cur_buf], edx mov eax, ecx shr eax, 9 mov [ebp+NTFS.ntfs_cur_size], eax add eax, [ebp+NTFS.ntfs_cur_offs] push eax call ntfs_read_attr.continue pop [ebp+NTFS.ntfs_cur_offs] mov eax, [ebp+NTFS.ntfs_cur_read] add [esp+10h], eax mov eax, ecx and eax, not 0x1FF cmp [ebp+NTFS.ntfs_cur_read], eax jnz .eof_ebx and ecx, 0x1FF jz .retok add edx, [ebp+NTFS.ntfs_cur_read] mov [ebp+NTFS.ntfs_cur_size], 1 lea eax, [ebp+NTFS.ntfs_bitmap_buf] mov [ebp+NTFS.ntfs_cur_buf], eax call ntfs_read_attr.continue cmp [ebp+NTFS.ntfs_cur_read], ecx jb @f mov [ebp+NTFS.ntfs_cur_read], ecx @@: xchg ecx, [ebp+NTFS.ntfs_cur_read] push ecx mov edi, edx lea esi, [ebp+NTFS.ntfs_bitmap_buf] add [esp+10h+4], ecx rep movsb pop ecx xor eax, eax cmp ecx, [ebp+NTFS.ntfs_cur_read] jz @f mov al, ERROR_END_OF_FILE @@: mov [esp+1Ch], eax call ntfs_unlock popad ret ;---------------------------------------------------------------- ntfs_ReadFolder: call ntfs_lock mov eax, 5 ; root cluster cmp byte [esi], 0 jz .doit stdcall ntfs_find_lfn, [esp+4] jnc .doit2 .notfound: or ebx, -1 push ERROR_FILE_NOT_FOUND .pop_ret: call ntfs_unlock pop eax ret .doit: mov [ebp+NTFS.ntfs_cur_iRecord], eax .doit2: mov [ebp+NTFS.ntfs_cur_attr], 0x10 ; $STANDARD_INFORMATION and [ebp+NTFS.ntfs_cur_offs], 0 mov [ebp+NTFS.ntfs_cur_size], 1 lea eax, [ebp+NTFS.ntfs_bitmap_buf] mov [ebp+NTFS.ntfs_cur_buf], eax call ntfs_read_attr jc .notfound mov [ebp+NTFS.ntfs_cur_attr], 0x90 ; $INDEX_ROOT and [ebp+NTFS.ntfs_cur_offs], 0 mov eax, [ebp+NTFS.cur_index_size] mov [ebp+NTFS.ntfs_cur_size], eax mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.ntfs_cur_buf], eax call ntfs_read_attr jnc .ok test eax, eax jz .notfound or ebx, -1 push ERROR_DEVICE jmp .pop_ret .ok: cmp [ebp+NTFS.ntfs_cur_read], 0x20 jae @f or ebx, -1 .fserr: push ERROR_FAT_TABLE jmp .pop_ret @@: pushad mov esi, [ebp+NTFS.cur_index_buf] mov eax, [esi+14h] add eax, 10h cmp [ebp+NTFS.ntfs_cur_read], eax jae .readok1 add eax, 1FFh shr eax, 9 cmp eax, [ebp+NTFS.cur_index_size] ja @f popad jmp .fserr @@: ; reallocate push eax stdcall kernel_free, [ebp+NTFS.cur_index_buf] pop eax mov [ebp+NTFS.cur_index_size], eax stdcall kernel_alloc, eax test eax, eax jnz @f and [ebp+NTFS.cur_index_size], 0 and [ebp+NTFS.cur_index_buf], 0 .nomem: call ntfs_unlock popad or ebx, -1 movi eax, ERROR_OUT_OF_MEMORY ret @@: mov [ebp+NTFS.cur_index_buf], eax popad jmp .doit2 .readok1: mov edx, [esi+8] ; subnode_size shr edx, 9 mov [ebp+NTFS.cur_subnode_size], edx cmp edx, [ebp+NTFS.cur_index_size] jbe .ok2 push esi edx stdcall kernel_alloc, edx pop edx esi test eax, eax jz .nomem mov edi, eax mov ecx, [ebp+NTFS.cur_index_size] shl ecx, 9-2 rep movsd mov esi, eax mov [ebp+NTFS.cur_index_size], edx stdcall kernel_free, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.cur_index_buf], esi .ok2: add esi, 10h mov edx, [ebx+16] push dword [ebx+8] ; read ANSI/UNICODE name ; init header mov edi, edx mov ecx, 32/4 xor eax, eax rep stosd mov byte [edx], 1 ; version mov ecx, [ebx+12] mov ebx, [ebx+4] push edx mov edx, esp ; edi -> BDFE, esi -> current index data, ebx = first wanted block, ; ecx = number of blocks to read ; edx -> parameters block: dd , dd cmp [ebp+NTFS.ntfs_cur_iRecord], 5 jz .skip_specials ; dot and dotdot entries push esi xor esi, esi call .add_special_entry inc esi call .add_special_entry pop esi .skip_specials: ; at first, dump index root add esi, [esi] .dump_root: test byte [esi+0Ch], 2 jnz .dump_root_done call .add_entry movzx eax, word [esi+8] add esi, eax jmp .dump_root .dump_root_done: ; now dump all subnodes push ecx edi lea edi, [ebp+NTFS.ntfs_bitmap_buf] mov [ebp+NTFS.ntfs_cur_buf], edi mov ecx, 0x400/4 xor eax, eax rep stosd mov [ebp+NTFS.ntfs_cur_attr], 0xB0 ; $BITMAP and [ebp+NTFS.ntfs_cur_offs], 0 mov [ebp+NTFS.ntfs_cur_size], 2 call ntfs_read_attr pop edi ecx push 0 ; save offset in $BITMAP attribute and [ebp+NTFS.ntfs_cur_offs], 0 .dumploop: mov [ebp+NTFS.ntfs_cur_attr], 0xA0 mov eax, [ebp+NTFS.cur_subnode_size] mov [ebp+NTFS.ntfs_cur_size], eax mov eax, [ebp+NTFS.cur_index_buf] mov esi, eax mov [ebp+NTFS.ntfs_cur_buf], eax push [ebp+NTFS.ntfs_cur_offs] mov eax, [ebp+NTFS.ntfs_cur_offs] imul eax, [ebp+NTFS.cur_subnode_size] mov [ebp+NTFS.ntfs_cur_offs], eax call ntfs_read_attr pop [ebp+NTFS.ntfs_cur_offs] mov eax, [ebp+NTFS.cur_subnode_size] shl eax, 9 cmp [ebp+NTFS.ntfs_cur_read], eax jnz .done push eax mov eax, [ebp+NTFS.ntfs_cur_offs] and eax, 0x400*8-1 bt dword [ebp+NTFS.ntfs_bitmap_buf], eax pop eax jnc .dump_subnode_done cmp dword [esi], 'INDX' jnz .dump_subnode_done push ebx mov ebx, esi call ntfs_restore_usa pop ebx jc .dump_subnode_done add esi, 0x18 add esi, [esi] .dump_subnode: test byte [esi+0Ch], 2 jnz .dump_subnode_done call .add_entry movzx eax, word [esi+8] add esi, eax jmp .dump_subnode .dump_subnode_done: inc [ebp+NTFS.ntfs_cur_offs] test [ebp+NTFS.ntfs_cur_offs], 0x400*8-1 jnz .dumploop mov [ebp+NTFS.ntfs_cur_attr], 0xB0 push ecx edi lea edi, [ebp+NTFS.ntfs_bitmap_buf] mov [ebp+NTFS.ntfs_cur_buf], edi mov ecx, 0x400/4 xor eax, eax rep stosd pop edi ecx pop eax push [ebp+NTFS.ntfs_cur_offs] inc eax mov [ebp+NTFS.ntfs_cur_offs], eax mov [ebp+NTFS.ntfs_cur_size], 2 push eax call ntfs_read_attr pop eax pop [ebp+NTFS.ntfs_cur_offs] push eax jmp .dumploop .done: pop eax pop edx mov ebx, [edx+4] pop edx xor eax, eax dec ecx js @f mov al, ERROR_END_OF_FILE @@: mov [esp+1Ch], eax mov [esp+10h], ebx call ntfs_unlock popad ret .add_special_entry: mov eax, [edx] inc dword [eax+8] ; new file found dec ebx jns .ret dec ecx js .ret inc dword [eax+4] ; new file block copied mov eax, [edx+4] mov [edi+4], eax ; mov eax, dword [ntfs_bitmap_buf+0x20] ; or al, 0x10 mov eax, 0x10 stosd scasd push edx mov eax, dword [ebp+NTFS.ntfs_bitmap_buf] mov edx, dword [ebp+NTFS.ntfs_bitmap_buf+4] call ntfs_datetime_to_bdfe mov eax, dword [ebp+NTFS.ntfs_bitmap_buf+0x18] mov edx, dword [ebp+NTFS.ntfs_bitmap_buf+0x1C] call ntfs_datetime_to_bdfe mov eax, dword [ebp+NTFS.ntfs_bitmap_buf+8] mov edx, dword [ebp+NTFS.ntfs_bitmap_buf+0xC] call ntfs_datetime_to_bdfe pop edx xor eax, eax stosd stosd mov al, '.' push edi ecx lea ecx, [esi+1] test byte [edi-0x24], 1 jz @f rep stosw pop ecx xor eax, eax stosw pop edi add edi, 520 ret @@: rep stosb pop ecx xor eax, eax stosb pop edi add edi, 264 .ret: ret .add_entry: ; do not return DOS 8.3 names cmp byte [esi+0x51], 2 jz .ret ; do not return system files ; ... note that there will be no bad effects if system files also were reported ... cmp dword [esi], 0x10 jb .ret mov eax, [edx] inc dword [eax+8] ; new file found dec ebx jns .ret dec ecx js .ret inc dword [eax+4] ; new file block copied mov eax, [edx+4] ; flags call ntfs_direntry_to_bdfe push ecx esi edi movzx ecx, byte [esi+0x50] add esi, 0x52 test byte [edi-0x24], 1 jz .ansi shr ecx, 1 rep movsd adc ecx, ecx rep movsw and word [edi], 0 pop edi add edi, 520 pop esi ecx ret .ansi: jecxz .skip @@: lodsw call uni2ansi_char stosb loop @b .skip: xor al, al stosb pop edi add edi, 264 pop esi ecx ret ntfs_direntry_to_bdfe: mov [edi+4], eax ; ANSI/UNICODE name mov eax, [esi+48h] test eax, 0x10000000 jz @f and eax, not 0x10000000 or al, 0x10 @@: stosd scasd push edx mov eax, [esi+0x18] mov edx, [esi+0x1C] call ntfs_datetime_to_bdfe mov eax, [esi+0x30] mov edx, [esi+0x34] call ntfs_datetime_to_bdfe mov eax, [esi+0x20] mov edx, [esi+0x24] call ntfs_datetime_to_bdfe pop edx mov eax, [esi+0x40] stosd mov eax, [esi+0x44] stosd ret iglobal _24 dd 24 _60 dd 60 _10000000 dd 10000000 days400year dd 365*400+100-4+1 days100year dd 365*100+25-1 days4year dd 365*4+1 days1year dd 365 months dd 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 months2 dd 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 _400 dd 400 _100 dd 100 endg ntfs_datetime_to_bdfe: ; edx:eax = number of 100-nanosecond intervals since January 1, 1601, in UTC push eax mov eax, edx xor edx, edx div [_10000000] xchg eax, [esp] div [_10000000] pop edx .sec: ; edx:eax = number of seconds since January 1, 1601 push eax mov eax, edx xor edx, edx div [_60] xchg eax, [esp] div [_60] mov [edi], dl pop edx ; edx:eax = number of minutes div [_60] mov [edi+1], dl ; eax = number of hours (note that 2^64/(10^7*60*60) < 2^32) xor edx, edx div [_24] mov [edi+2], dl mov [edi+3], byte 0 ; eax = number of days since January 1, 1601 xor edx, edx div [days400year] imul eax, 400 add eax, 1601 mov [edi+6], ax mov eax, edx xor edx, edx div [days100year] cmp al, 4 jnz @f dec eax add edx, [days100year] @@: imul eax, 100 add [edi+6], ax mov eax, edx xor edx, edx div [days4year] shl eax, 2 add [edi+6], ax mov eax, edx xor edx, edx div [days1year] cmp al, 4 jnz @f dec eax add edx, [days1year] @@: add [edi+6], ax push esi edx mov esi, months movzx eax, word [edi+6] test al, 3 jnz .noleap xor edx, edx push eax div [_400] pop eax test edx, edx jz .leap xor edx, edx div [_100] test edx, edx jz .noleap .leap: mov esi, months2 .noleap: pop edx xor eax, eax inc eax @@: sub edx, [esi] jb @f add esi, 4 inc eax jmp @b @@: add edx, [esi] pop esi inc edx mov [edi+4], dl mov [edi+5], al add edi, 8 ret ;---------------------------------------------------------------- ntfs_CreateFolder: mov [ebp+NTFS.ntfsFolder], 1 jmp @f ntfs_CreateFile: mov [ebp+NTFS.ntfsFolder], 0 @@: cmp byte [esi], 0 jnz @f xor ebx, ebx movi eax, ERROR_ACCESS_DENIED ret @@: ; 1. Search file call ntfs_lock stdcall ntfs_find_lfn, [esp+4] jc .notFound ; found, rewrite cmp [ebp+NTFS.ntfs_cur_iRecord], 16 jc ntfsDenied cmp [ebp+NTFS.ntfsFolder], 1 jz .folder xor ecx, ecx mov edx, [ebx+12] mov [ebp+NTFS.nodeLastRead], ecx cmp [eax+fileRealSize+4], ecx jnz @f cmp [eax+fileRealSize], edx jz .readAttribute @@: ; set file size in the directory cmp [ebp+NTFS.ntfsFragmentCount], 1 jnz ntfsUnsupported ; record fragmented mov edi, [ebp+NTFS.cur_index_buf] cmp dword [edi], 'INDX' jz @f mov esi, [ebp+NTFS.frs_buffer] mov ecx, [esi+recordRealSize] shr ecx, 2 rep movsd mov esi, [ebp+NTFS.ntfs_attr_offs] mov cl, [esi+attributeOffset] sub esi, [ebp+NTFS.frs_buffer] add eax, ecx add eax, esi xor ecx, ecx @@: mov [eax+fileRealSize], edx mov [eax+fileRealSize+4], ecx mov eax, [ebp+NTFS.ntfsLastRead] mov [ebp+NTFS.nodeLastRead], eax .readAttribute: mov [ebp+NTFS.ntfs_cur_attr], 0x80 mov [ebp+NTFS.ntfs_cur_offs], 0 mov [ebp+NTFS.ntfs_cur_size], 0 call ntfs_read_attr jc ntfsFail mov ecx, [ebp+NTFS.frs_buffer] mov eax, edx xor edx, edx cmp word [ecx+baseRecordReuse], 0 jnz ntfsUnsupported ; auxiliary record cmp byte [ecx+hardLinkCounter], 1 jnz ntfsUnsupported ; file copying required mov ecx, [ebp+NTFS.ntfs_attr_offs] cmp byte [ecx+nonResidentFlag], 1 jnz ntfsUnsupported ; resident $DATA test eax, eax jz ntfsUnsupported cmp [ecx+attributeRealSize+4], edx jnz @f cmp [ecx+attributeRealSize], eax jz ntfs_WriteFile.writeNode @@: jmp ntfs_WriteFile.resizeAttribute .folder: bt dword [eax+fileFlags], 28 jnc ntfsDenied push 0 jmp ntfsOut .notFound: ; create test eax, eax jz ntfsFail cmp [ebp+NTFS.ntfsFragmentCount], 1 jnz ntfsUnsupported ; record fragmented ; 2. Prepare directory record mov ecx, esi @@: ; count characters inc ecx cmp byte [ecx], '/' jz ntfsNotFound ; path folder not found cmp byte [ecx], 0 jnz @b sub ecx, esi push ecx lea ecx, [ecx*2+52h+7] ; precalculate index length and ecx, not 7 ; align 8 mov edi, [ebp+NTFS.cur_index_buf] push esi push ecx cmp dword [edi], 'INDX' jz .indexRecord mov esi, [ebp+NTFS.frs_buffer] ; indexRoot mov edx, [esi+recordRealSize] add edx, ecx cmp [esi+recordAllocatedSize], edx jnc @f add esp, 12 jmp ntfsUnsupported ; indexAllocation required @@: ; index fits in the indexRoot mov [esi+recordRealSize], edx mov ecx, edx shr ecx, 2 rep movsd mov edi, [ebp+NTFS.ntfs_attr_offs] sub edi, [ebp+NTFS.frs_buffer] add edi, [ebp+NTFS.cur_index_buf] mov esi, [esp] add [edi+sizeWithHeader], esi add [edi+sizeWithoutHeader], esi mov cl, [edi+attributeOffset] add edi, ecx add [edi+16+nodeRealSize], esi add [edi+16+nodeAllocatedSize], esi sub eax, [ebp+NTFS.cur_index_buf] add eax, edi mov edi, [ebp+NTFS.cur_index_buf] add edi, edx sub edi, 4 jmp .common .indexRecord: mov edx, [edi+28] add edx, ecx cmp [edi+32], edx jnc @f add esp, 12 jmp ntfsUnsupported ; new node required @@: ; index fits in the node mov [edi+28], edx lea edi, [edi+edx+24-4] .common: mov esi, edi sub esi, [esp] mov ecx, esi sub ecx, eax ; eax = pointer in the node shr ecx, 2 inc ecx std rep movsd ; move forward, make space mov ecx, [esp] shr ecx, 2 xor eax, eax rep stosd cld add edi, 4 pop eax pop esi mov [edi+indexAllocatedSize], ax ; fill index with data mov eax, [esp] shl eax, 1 add eax, 42h mov [edi+indexRawSize], ax mov eax, [ebp+NTFS.ntfs_attr_iRecord] mov [edi+directoryRecordReference], eax mov eax, [ebp+NTFS.frs_buffer] mov eax, [eax+reuseCounter] mov [edi+directoryReferenceReuse], ax mov eax, [ebx+12] mov [ebp+NTFS.fileRealSize], eax mov [edi+fileRealSize], eax mov ecx, [ebp+NTFS.sectors_per_cluster] shl ecx, 9 add eax, ecx dec eax xor edx, edx div ecx mov [ebp+NTFS.fileDataSize], eax mul ecx mov [edi+fileAllocatedSize], eax pop ecx mov [ebp+NTFS.indexOffset], edi mov [edi+fileNameLength], cl add edi, 52h @@: ; record filename lodsb call ansi2uni_char stosw dec ecx jnz @b mov eax, [ebp+NTFS.ntfsLastRead] mov [ebp+NTFS.nodeLastRead], eax cmp [ebp+NTFS.ntfsFolder], 0 jz @f mov edi, [ebp+NTFS.indexOffset] bts dword [edi+fileFlags], 28 jmp .mftBitmap @@: ; 3. File data cmp [ebp+NTFS.fileRealSize], 0 jz .mftBitmap mov edi, [ebp+NTFS.BitmapStart] call ntfsSpaceAlloc jc ntfsDiskFull mov [ebp+NTFS.fileDataStart], eax mul [ebp+NTFS.sectors_per_cluster] mov ecx, [ebp+NTFS.fileRealSize] add ecx, 511 shr ecx, 9 mov ebx, [ebx+16] call fs_write64_app test eax, eax jnz ntfsDevice ; 4. MFT record .mftBitmap: ; search for free record mov edi, [ebp+NTFS.mftBitmapBuffer] mov ecx, [ebp+NTFS.mftBitmapSize] mov al, -1 add edi, 3 sub ecx, 3 repz scasb dec edi movzx eax, byte [edi] not al bsf ecx, eax jz ntfsUnsupported ; no free records bts [edi], ecx ; get record location sub edi, [ebp+NTFS.mftBitmapBuffer] shl edi, 3 add edi, ecx mov [ebp+NTFS.newMftRecord], edi mov eax, [ebp+NTFS.frs_size] shr eax, 9 mul edi mov [ebp+NTFS.ntfs_cur_iRecord], 0 mov [ebp+NTFS.ntfs_cur_attr], 0x80 mov [ebp+NTFS.ntfs_cur_offs], eax mov [ebp+NTFS.ntfs_cur_size], 1 mov eax, [ebp+NTFS.frs_buffer] mov [ebp+NTFS.ntfs_cur_buf], eax call ntfs_read_attr cmp [ebp+NTFS.ntfs_cur_read], 0 jnz .mftRecord ; extend MFT $DATA mov eax, [ebp+NTFS.mft_cluster] mul [ebp+NTFS.sectors_per_cluster] cmp eax, [ebp+NTFS.ntfsLastRead] jnz ntfsUnsupported ; auxiliary record mov edi, [ebp+NTFS.ntfs_attr_offs] mov ebx, [ebp+NTFS.sectors_per_cluster] shl ebx, 9+3 add dword [edi+lastVCN], 8 add [edi+attributeAllocatedSize], ebx adc byte [edi+attributeAllocatedSize+4], 0 add [edi+attributeRealSize], ebx adc byte [edi+attributeRealSize+4], 0 add [edi+initialDataSize], ebx adc byte [edi+initialDataSize+4], 0 movzx eax, byte [edi+dataRunsOffset] add edi, eax mov al, [edi] inc edi shl eax, 4 shr al, 4 mov cl, 4 sub cl, al shl cl, 3 add ah, al shr eax, 8 cmp byte [edi+eax], 0 jnz ntfsUnsupported ; $MFT fragmented mov al, 8 mov edx, [edi] rol eax, cl rol edx, cl add eax, edx jc ntfsUnsupported ror eax, cl shr edx, cl mov [edi], eax add edx, [ebp+NTFS.mft_cluster] mov esi, edx mov ecx, edx and ecx, 7 shr edx, 3 add edx, [ebp+NTFS.BitmapBuffer] mov ax, [edx] shr ax, cl test al, al jnz ntfsUnsupported dec al xchg [edx], al mov [edx+1], al stdcall kernel_alloc, ebx test eax, eax jz ntfsNoMemory mov ecx, ebx shr ecx, 2 mov edi, eax push ebx mov ebx, eax xor eax, eax rep stosd mov eax, esi mul [ebp+NTFS.sectors_per_cluster] pop ecx shr ecx, 9 call fs_write64_sys ; clear new records stdcall kernel_free, ebx mov eax, esi shr eax, 3+9 mov ebx, eax shl ebx, 9 add ebx, [ebp+NTFS.BitmapBuffer] add eax, [ebp+NTFS.BitmapLocation] mov ecx, 1 xor edx, edx call fs_write64_app ; partition bitmap test eax, eax jnz ntfsDevice mov eax, [ebp+NTFS.frs_buffer] mov [ebp+NTFS.ntfs_cur_buf], eax call writeRecord ; $MFT test eax, eax jnz ntfsDevice mov eax, [ebp+NTFS.mftmirr_cluster] mul [ebp+NTFS.sectors_per_cluster] mov ebx, [ebp+NTFS.frs_buffer] movzx ecx, word [ebx+updateSequenceSize] dec ecx call fs_write64_sys ; $MFTMirr test eax, eax jnz ntfsDevice mov eax, [ebp+NTFS.ntfs_cur_offs] add [ebp+NTFS.ntfsLastRead], eax .mftRecord: mov esi, [ebp+NTFS.indexOffset] mov edi, [ebp+NTFS.frs_buffer] xor eax, eax movzx ecx, word [esi+indexAllocatedSize] add ecx, 8+30h+48h+50h+8 push ecx shr ecx, 2 rep stosd mov edi, [ebp+NTFS.frs_buffer] ; record header mov dword[edi], 'FILE' mov byte [edi+updateSequenceOffset], 2ah mov byte [edi+updateSequenceSize], 3 mov byte [edi+hardLinkCounter], 1 mov byte [edi+attributeOffset], 30h popd [edi+recordRealSize] mov word [edi+recordAllocatedSize], 1024 mov byte [edi+newAttributeID], 3 rdtsc mov [edi+2ah], ax add edi, 30h ; $StandardInformation mov byte [edi+attributeType], 10h mov byte [edi+sizeWithHeader], 48h mov byte [edi+sizeWithoutHeader], 30h mov byte [edi+attributeOffset], 18h add edi, 48h ; $FileName mov byte [edi+attributeType], 30h mov byte [edi+attributeID], 1 mov cx, [esi+indexRawSize] mov [edi+sizeWithoutHeader], ecx mov cx, [esi+indexAllocatedSize] add ecx, 8 mov [edi+sizeWithHeader], ecx mov byte [edi+attributeOffset], 18h mov byte [edi+indexedFlag], 1 add edi, 18h add esi, 16 sub ecx, 18h shr ecx, 2 rep movsd mov byte [edi+sizeWithHeader], 50h mov byte [edi+attributeID], 2 mov dword[edi+50h], -1 ; $End cmp [ebp+NTFS.ntfsFolder], 0 jnz @f ; $Data mov byte [edi+attributeType], 80h cmp [ebp+NTFS.fileRealSize], 0 jz .zeroSize mov esi, [ebp+NTFS.indexOffset] mov eax, [ebp+NTFS.fileDataSize] dec eax mov [edi+lastVCN], eax mov byte [edi+nonResidentFlag], 1 mov byte [edi+dataRunsOffset], 40h mov eax, [esi+fileAllocatedSize] mov [edi+attributeAllocatedSize], eax mov eax, [esi+fileRealSize] mov [edi+attributeRealSize], eax mov [edi+initialDataSize], eax mov esi, edi add edi, 40h call createMcbEntry mov al, 1 jmp .writeMftRecord .zeroSize: mov byte [edi+attributeOffset], 18h mov al, 1 jmp .writeMftRecord @@: ; $IndexRoot mov byte [edi+attributeType], 90h mov byte [edi+nameLength], 4 mov byte [edi+nameOffset], 18h mov byte [edi+sizeWithoutHeader], 30h mov byte [edi+attributeOffset], 20h mov dword[edi+18h], 490024h ; unicode $I30 mov dword[edi+18h+4], 300033h mov byte [edi+20h+attributeType], 30h mov byte [edi+20h+collationRule], 1 mov eax, [ebp+NTFS.sectors_per_cluster] shl eax, 9 mov [edi+20h+indexRecordSize], eax mov byte [edi+20h+indexRecordSizeClus], 1 mov byte [edi+30h+indexOffset], 16 mov byte [edi+30h+nodeRealSize], 32 mov byte [edi+30h+nodeAllocatedSize], 32 mov byte [edi+40h+indexAllocatedSize], 16 mov byte [edi+40h+indexFlags], 2 mov al, 3 .writeMftRecord: mov edi, [ebp+NTFS.frs_buffer] mov [ebp+NTFS.ntfs_cur_buf], edi mov [edi+recordFlags], al call writeRecord test eax, eax jnz ntfsDevice ; write MFT bitmap mov eax, [ebp+NTFS.newMftRecord] shr eax, 3+9 mov ebx, eax shl ebx, 9 add eax, [ebp+NTFS.mftBitmapLocation] add ebx, [ebp+NTFS.mftBitmapBuffer] mov ecx, 1 xor edx, edx call fs_write64_sys test eax, eax jnz ntfsDevice ; 5. Write partition bitmap cmp [ebp+NTFS.ntfsFolder], 0 jnz @f cmp [ebp+NTFS.fileRealSize], 0 jz @f mov eax, [ebp+NTFS.fileDataStart] mov ecx, [ebp+NTFS.fileDataSize] add ecx, eax add ecx, 4095 shr ecx, 3+9 shr eax, 3+9 sub ecx, eax mov ebx, eax shl ebx, 9 add eax, [ebp+NTFS.BitmapLocation] add ebx, [ebp+NTFS.BitmapBuffer] xor edx, edx call fs_write64_app test eax, eax jnz ntfsDevice @@: mov edi, [ebp+NTFS.indexOffset] mov eax, [ebp+NTFS.newMftRecord] mov [edi+fileRecordReference], eax ; 6. Write directory node mov eax, [ebp+NTFS.nodeLastRead] mov [ebp+NTFS.ntfsLastRead], eax mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.ntfs_cur_buf], eax call writeRecord mov ebx, [ebp+NTFS.fileRealSize] ntfsDone: mov esi, [ebp+PARTITION.Disk] call disk_sync call ntfs_unlock xor eax, eax ret writeRecord: ; make updateSequence and write to disk ; in: ; [ebp+NTFS.ntfs_cur_buf] -> record ; [ebp+NTFS.ntfsLastRead] = partition sector mov esi, [ebp+NTFS.ntfs_cur_buf] mov edi, esi movzx ecx, word [esi+updateSequenceOffset] add edi, ecx mov ax, [edi] inc ax stosw mov cx, [esi+updateSequenceSize] dec ecx push ecx @@: add esi, 510 movsw mov [esi-2], ax dec ecx jnz @b mov eax, [ebp+NTFS.ntfsLastRead] mov ebx, [ebp+NTFS.ntfs_cur_buf] pop ecx xor edx, edx jmp fs_write64_sys createMcbEntry: ; in: ; [ebp+NTFS.fileDataStart] = position value ; [ebp+NTFS.fileDataSize] = size value ; edi -> destination ; esi -> attribute header mov eax, [ebp+NTFS.fileDataStart] xor edx, edx shl eax, 1 jnc @f not eax @@: inc edx shr eax, 8 jnz @b mov eax, [ebp+NTFS.fileDataSize] shl eax, 1 xor ecx, ecx @@: inc ecx shr eax, 8 jnz @b lea eax, [edi+edx+1] add eax, ecx sub eax, esi sub ax, [esi+sizeWithHeader] jc @f add word [esi+sizeWithHeader], 8 ; extend attribute mov esi, [ebp+NTFS.frs_buffer] mov eax, [esi+recordRealSize] add eax, 8 cmp [esi+recordAllocatedSize], eax jc .end ; no space in the record mov [esi+recordRealSize], eax push ecx edi add esi, eax mov ecx, esi sub ecx, edi sub ecx, 8 shr ecx, 2 mov edi, esi sub edi, 4 sub esi, 12 std rep movsd cld pop edi ecx @@: mov eax, edx shl eax, 4 add eax, ecx stosb lea esi, [ebp+NTFS.fileDataSize] rep movsb lea esi, [ebp+NTFS.fileDataStart] mov ecx, edx rep movsb .end: ret resizeAttribute: ; in: ; [ebp+NTFS.frs_buffer] -> file record ; [ebp+NTFS.ntfs_attr_offs] -> attribute ; edx:eax = new size ; out: ; CF=1 -> eax = error code mov esi, [ebp+NTFS.ntfs_attr_offs] mov ecx, [ebp+NTFS.sectors_per_cluster] shl ecx, 9 mov dword [ebp+NTFS.ntfs_attr_size], eax mov dword [ebp+NTFS.ntfs_attr_size+4], edx mov [esi+attributeRealSize], eax mov [esi+attributeRealSize+4], edx mov [esi+initialDataSize], eax mov [esi+initialDataSize+4], edx sub eax, 1 sbb edx, 0 div ecx mov edi, eax inc eax mul ecx mov [esi+attributeAllocatedSize], eax mov [esi+attributeAllocatedSize+4], edx mov ecx, [esi+lastVCN] mov [esi+lastVCN], edi movzx eax, byte [esi+dataRunsOffset] sub edi, ecx jz .done jc .shrinkAttribute ; extend attribute mov [ebp+NTFS.fileDataSize], edi xor edi, edi add esi, eax push edi edi edi edi @@: mov edx, eax mov eax, esi add edi, [esp+8] call ntfs_decode_mcb_entry jc @b mov [esp+4], edx mov [esp+12], edi add edi, [esp] push edi shr edi, 5 shl edi, 2 push eax cmp edi, [ebp+NTFS.BitmapStart] jc .err1 call ntfsSpaceAlloc jc .err1 pop edi pop edx cmp edx, eax jnz .newEntry pop edx pop edi pop [ebp+NTFS.fileDataStart] mov [esp], eax push [ebp+NTFS.fileDataSize] add [ebp+NTFS.fileDataSize], edx jmp @f .newEntry: add esp, 12 pop edx push eax push [ebp+NTFS.fileDataSize] sub eax, edx mov [ebp+NTFS.fileDataStart], eax @@: mov esi, [ebp+NTFS.ntfs_attr_offs] call createMcbEntry pop ecx pop eax jc .err2 mov byte [edi], 0 add ecx, eax add ecx, 4095 shr ecx, 3+9 shr eax, 3+9 sub ecx, eax mov ebx, eax shl ebx, 9 add eax, [ebp+NTFS.BitmapLocation] add ebx, [ebp+NTFS.BitmapBuffer] xor edx, edx call fs_write64_app test eax, eax jz .done movi eax, ERROR_DEVICE stc .done: ret .err1: movi eax, ERROR_DISK_FULL add esp, 24 stc ret .err2: movi eax, ERROR_UNSUPPORTED_FS ret .shrinkAttribute: add ecx, edi inc ecx add esi, eax xor edi, edi sub esp, 20 @@: mov [esp+16], esi call ntfs_decode_mcb_entry jnc .err3 add edi, [esp+8] sub ecx, [esp] jnc @b mov ebx, ecx add ecx, [esp] mov eax, [esp+8] mov [ebp+NTFS.fileDataSize], ecx mov [ebp+NTFS.fileDataStart], eax push edi add edi, ecx neg ebx call ntfsSpaceFree pop edi jc .end @@: call ntfs_decode_mcb_entry jnc .end cmp dword[esp+8], 0 jz @b add edi, [esp+8] mov ebx, [esp] call ntfsSpaceFree jnc @b .end: add esp, 16 pop edi cmp [ebp+NTFS.fileDataSize], 0 jz @f mov esi, [ebp+NTFS.ntfs_attr_offs] call createMcbEntry @@: mov byte [edi], 0 ret .err3: movi eax, ERROR_FS_FAIL add esp, 20 stc ret ntfsSpaceAlloc: ; find and mark block of free space in bitmap buffer ; in: ; edi = offset in bitmap to start search from ; [ebp+NTFS.fileDataSize] = block size in clusters ; out: ; eax = allocated block starting cluster ; CF=1 -> disk full mov ecx, [ebp+NTFS.BitmapBuffer] add edi, ecx add ecx, [ebp+NTFS.BitmapSize] sub ecx, edi jnc @f call bitmapBuffering shl ecx, 2 @@: shr ecx, 2 mov eax, [ebp+NTFS.fileDataSize] shr eax, 5 jz .small push eax ; bitmap dwords .start: mov ecx, [ebp+NTFS.BitmapBuffer] add ecx, [ebp+NTFS.BitmapSize] sub ecx, edi shr ecx, 2 @@: xor eax, eax repnz scasd ; search for empty dword jz @f call bitmapBuffering jmp @b @@: cmp ecx, [esp] jnc @f call bitmapBuffering jmp @b @@: sub edi, 4 mov ecx, [esp] mov esi, edi xor eax, eax repz scasd ; check following dwords jnz .start sub esi, 4 mov eax, [esi] xor edx, edx bsr edx, eax inc edx push edx ; starting bit push esi ; starting dword add esi, 4 neg edx add edx, 32 mov eax, [ebp+NTFS.fileDataSize] sub eax, edx mov edx, eax shr eax, 5 shl eax, 2 add esi, eax mov eax, [esi] bsf ecx, eax ; check last dword jz .done and edx, 31 cmp ecx, edx jnc .done add esp, 8 jmp .start .small: ; less than 32 clusters mov eax, -1 repz scasd ; search for zero bits push ecx test ecx, ecx jnz @f call bitmapBuffering pop eax jmp .small @@: sub edi, 4 mov eax, [edi] not eax @@: bsf ecx, eax ; first 0 jz .again not eax shr eax, cl shl eax, cl bsf edx, eax ; next 1 jz @f sub edx, ecx cmp edx, [ebp+NTFS.fileDataSize] jnc .got ; fits inside bsf ecx, eax not eax shr eax, cl shl eax, cl jmp @b @@: ; next dword mov eax, [edi+4] bsf edx, eax jz .got ; empty add edx, 32 sub edx, ecx cmp edx, [ebp+NTFS.fileDataSize] jnc .got ; share between dwords .again: add edi, 4 pop ecx jmp .small .got: push ecx ; starting bit push edi ; starting dword .done: ; mark space mov ecx, [esp+4] cmp ecx, 32 jc @f xor ecx, ecx add dword [esp], 4 mov [esp+4], ecx @@: mov edi, [esp] xor eax, eax dec eax shr eax, cl shl eax, cl neg ecx add ecx, 32 sub ecx, [ebp+NTFS.fileDataSize] jc @f shl eax, cl ; fits inside dword shr eax, cl or [edi], eax jmp .end @@: or [edi], eax neg ecx push ecx shr ecx, 5 add edi, 4 xor eax, eax dec eax rep stosd pop ecx and ecx, 31 shr eax, cl shl eax, cl not eax or [edi], eax .end: pop eax sub eax, [ebp+NTFS.BitmapBuffer] shl eax, 3 pop edx add eax, edx pop edx ret ntfsSpaceFree: ; free disk space ; in: ; edi = starting cluster ; ebx = size in clusters mov eax, edi add eax, ebx shr eax, 3 inc eax cmp eax, [ebp+NTFS.BitmapSize] jc @f add eax, [ebp+NTFS.BitmapBuffer] push edi mov edi, eax call bitmapBuffering pop edi @@: push edi mov ecx, edi shr edi, 5 shl edi, 2 add edi, [ebp+NTFS.BitmapBuffer] and ecx, 31 xor eax, eax dec eax shr eax, cl shl eax, cl neg ecx add ecx, 32 sub ecx, ebx jc @f shl eax, cl ; fits inside dword shr eax, cl not eax and [edi], eax jmp .writeBitmap @@: not eax and [edi], eax neg ecx push ecx shr ecx, 5 add edi, 4 xor eax, eax rep stosd pop ecx and ecx, 31 dec eax shr eax, cl shl eax, cl and [edi], eax .writeBitmap: pop eax mov edi, eax lea ecx, [eax+ebx+4095] shr eax, 3+9 shr ecx, 3+9 sub ecx, eax mov ebx, eax shl ebx, 9 add eax, [ebp+NTFS.BitmapLocation] add ebx, [ebp+NTFS.BitmapBuffer] xor edx, edx jmp fs_write64_app bitmapBuffering: ; Extend BitmapBuffer and read next 32kb of bitmap ; Warning: $Bitmap fragmentation is not foreseen ; in: edi -> position in bitmap buffer ; out: ecx = number of buffered dwords left push ebx mov eax, [ebp+NTFS.BitmapTotalSize] cmp eax, [ebp+NTFS.BitmapSize] jz .end stdcall alloc_pages, 8 test eax, eax jz .end add eax, 3 mov ebx, [ebp+NTFS.BitmapBuffer] add ebx, [ebp+NTFS.BitmapSize] push ebx mov ecx, 8 call commit_pages mov eax, [ebp+NTFS.BitmapSize] shr eax, 9 add eax, [ebp+NTFS.BitmapLocation] pop ebx mov ecx, 64 xor edx, edx call fs_read64_app test eax, eax jnz .err add [ebp+NTFS.BitmapSize], 8000h mov eax, [ebp+NTFS.BitmapTotalSize] cmp eax, [ebp+NTFS.BitmapSize] jnc @f mov [ebp+NTFS.BitmapSize], eax @@: pop ebx mov ecx, [ebp+NTFS.BitmapBuffer] add ecx, [ebp+NTFS.BitmapSize] sub ecx, edi jc bitmapBuffering shr ecx, 2 ret .err: mov eax, [ebp+NTFS.BitmapBuffer] add eax, [ebp+NTFS.BitmapSize] mov ecx, 8 call release_pages .end: pop ebx pop eax ; ret pop eax stc ret ;---------------------------------------------------------------- ntfs_WriteFile: cmp byte [esi], 0 jnz @f xor ebx, ebx movi eax, ERROR_ACCESS_DENIED ret @@: call ntfs_lock stdcall ntfs_find_lfn, [esp+4] jc ntfsNotFound cmp [ebp+NTFS.ntfs_cur_iRecord], 16 jc ntfsDenied bt dword [eax+fileFlags], 28 jc ntfsDenied mov ecx, eax mov eax, [ebx+4] mov edx, [ebx+8] add eax, [ebx+12] adc edx, 0 mov [ebp+NTFS.nodeLastRead], 0 cmp edx, [ecx+fileRealSize+4] jc .readAttribute jnz @f cmp [ecx+fileRealSize], eax jnc .readAttribute @@: ; set file size in the directory cmp [ebp+NTFS.ntfsFragmentCount], 1 jnz ntfsUnsupported ; record fragmented mov edi, [ebp+NTFS.cur_index_buf] cmp dword [edi], 'INDX' jz @f mov esi, [ebp+NTFS.frs_buffer] push ecx mov ecx, [esi+recordRealSize] shr ecx, 2 rep movsd mov esi, [ebp+NTFS.ntfs_attr_offs] mov cl, [esi+attributeOffset] sub esi, [ebp+NTFS.frs_buffer] add esi, ecx pop ecx add ecx, esi @@: mov [ecx+fileRealSize], eax mov [ecx+fileRealSize+4], edx mov ecx, [ebp+NTFS.ntfsLastRead] mov [ebp+NTFS.nodeLastRead], ecx .readAttribute: mov [ebp+NTFS.ntfs_cur_attr], 0x80 mov [ebp+NTFS.ntfs_cur_offs], 0 mov [ebp+NTFS.ntfs_cur_size], 0 push eax call ntfs_read_attr pop eax jc ntfsFail mov ecx, [ebp+NTFS.frs_buffer] cmp word [ecx+baseRecordReuse], 0 jnz ntfsUnsupported ; auxiliary record cmp byte [ecx+hardLinkCounter], 1 jnz ntfsUnsupported ; file copying required mov ecx, [ebp+NTFS.ntfs_attr_offs] cmp byte [ecx+nonResidentFlag], 1 jnz ntfsUnsupported ; resident $DATA cmp edx, [ecx+attributeRealSize+4] jc .writeNode jnz .resizeAttribute cmp [ecx+attributeRealSize], eax jnc .writeNode .resizeAttribute: push ebx call resizeAttribute jc ntfsError mov eax, [ebp+NTFS.frs_buffer] mov [ebp+NTFS.ntfs_cur_buf], eax call writeRecord ; file mov ebx, [ebp+NTFS.frs_buffer] call ntfs_restore_usa_frs pop ebx .writeNode: mov eax, [ebp+NTFS.nodeLastRead] test eax, eax jz .writeData mov [ebp+NTFS.ntfsLastRead], eax mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.ntfs_cur_buf], eax push ebx call writeRecord ; directory pop ebx .writeData: mov eax, [ebx+4] mov edx, [ebx+8] mov ecx, [ebx+12] mov esi, [ebx+16] shrd eax, edx, 9 test dword[ebx+4], 1FFh jz .aligned mov [ebp+NTFS.ntfs_cur_offs], eax mov [ebp+NTFS.ntfs_cur_size], 1 lea edi, [ebp+NTFS.ntfs_bitmap_buf] mov [ebp+NTFS.ntfs_cur_buf], edi call ntfs_read_attr.continue jc ntfsDevice mov eax, [ebx+4] and eax, 1FFh add edi, eax sub eax, [ebp+NTFS.ntfs_cur_read] neg eax push ecx cmp ecx, eax jb @f mov ecx, eax @@: sub [esp], ecx rep movsb push ebx mov eax, [ebp+NTFS.ntfsLastRead] lea ebx, [ebp+NTFS.ntfs_bitmap_buf] mov ecx, 1 xor edx, edx call fs_write64_app pop ebx pop ecx test ecx, ecx jz @f mov eax, [ebx+4] mov edx, [ebx+8] shrd eax, edx, 9 inc eax .aligned: push ecx shr ecx, 9 mov [ebp+NTFS.ntfs_cur_offs], eax mov [ebp+NTFS.ntfs_cur_size], ecx mov [ebp+NTFS.ntfs_cur_buf], esi add eax, ecx push eax mov [ebp+NTFS.ntfsWriteAttr], 1 call ntfs_read_attr.continue mov [ebp+NTFS.ntfsWriteAttr], 0 pop [ebp+NTFS.ntfs_cur_offs] pop ecx jc ntfsDevice and ecx, 1FFh jz @f add esi, [ebp+NTFS.ntfs_cur_read] mov [ebp+NTFS.ntfs_cur_size], 1 lea edi, [ebp+NTFS.ntfs_bitmap_buf] mov [ebp+NTFS.ntfs_cur_buf], edi call ntfs_read_attr.continue jc ntfsDevice rep movsb push ebx mov eax, [ebp+NTFS.ntfsLastRead] lea ebx, [ebp+NTFS.ntfs_bitmap_buf] mov ecx, 1 xor edx, edx call fs_write64_app pop ebx @@: mov ebx, [ebx+12] jmp ntfsDone ;---------------------------------------------------------------- ntfs_Delete: cmp byte [esi], 0 jnz @f xor ebx, ebx movi eax, ERROR_ACCESS_DENIED ret @@: call ntfs_lock stdcall ntfs_find_lfn, [esp+4] jc ntfsNotFound cmp [ebp+NTFS.ntfs_cur_iRecord], 16 jc ntfsDenied cmp [ebp+NTFS.ntfsFragmentCount], 1 jnz ntfsUnsupported ; record fragmented test byte [eax+indexFlags], 1 jnz ntfsUnsupported ; index has a subnode mov edx, [ebp+NTFS.ntfs_cur_iRecord] shr edx, 3 cmp edx, [ebp+NTFS.mftBitmapSize] jnc ntfsUnsupported ; delete index from the node movzx edx, word [eax+indexAllocatedSize] mov edi, [ebp+NTFS.cur_index_buf] cmp dword [edi], 'INDX' jz .indexRecord mov esi, [ebp+NTFS.frs_buffer] ; indexRoot mov ecx, [esi+recordRealSize] shr ecx, 2 rep movsd mov esi, [ebp+NTFS.cur_index_buf] mov edi, [ebp+NTFS.ntfs_attr_offs] sub edi, [ebp+NTFS.frs_buffer] add edi, esi sub [edi+sizeWithHeader], edx sub [edi+sizeWithoutHeader], edx mov cl, [edi+attributeOffset] add edi, ecx sub [edi+16+nodeRealSize], edx sub [edi+16+nodeAllocatedSize], edx sub eax, esi add eax, edi sub [esi+recordRealSize], edx mov ecx, [esi+recordRealSize] jmp @f .indexRecord: sub [edi+28], edx mov ecx, [edi+28] add ecx, 24 @@: add ecx, [ebp+NTFS.cur_index_buf] sub ecx, eax shr ecx, 2 mov esi, eax add esi, edx mov edi, eax rep movsd mov eax, [ebp+NTFS.ntfsLastRead] mov [ebp+NTFS.nodeLastRead], eax ; examine file record mov [ebp+NTFS.ntfs_cur_attr], 0x80 mov [ebp+NTFS.ntfs_cur_offs], 0 mov [ebp+NTFS.ntfs_cur_size], 0 call ntfs_read_attr jc .folder mov esi, [ebp+NTFS.frs_buffer] cmp word [esi+baseRecordReuse], 0 jnz ntfsUnsupported ; auxiliary record cmp byte [esi+hardLinkCounter], 2 jnc .writeFileRecord ; delete hard link mov esi, [ebp+NTFS.ntfs_attr_offs] cmp byte [esi+nonResidentFlag], 0 jz .writeBitmapMFT movzx eax, byte [esi+dataRunsOffset] add esi, eax xor edi, edi sub esp, 16 @@: ; "delete" file data call ntfs_decode_mcb_entry jnc @f cmp dword[esp+8], 0 jz @b add edi, [esp+8] mov ebx, [esp] call ntfsSpaceFree jnc @b @@: add esp, 16 jmp .writeBitmapMFT .folder: ; empty? lea esi, [ebp+NTFS.ntfs_bitmap_buf] mov [ebp+NTFS.ntfs_cur_buf], esi mov [ebp+NTFS.ntfs_cur_attr], 0x90 mov [ebp+NTFS.ntfs_cur_offs], 0 mov [ebp+NTFS.ntfs_cur_size], 1 call ntfs_read_attr cmp [ebp+NTFS.ntfs_cur_read], 48 jnz ntfsDenied test byte [esi+32+indexFlags], 1 jnz ntfsDenied .writeBitmapMFT: ; "delete" file record mov eax, [ebp+NTFS.ntfs_cur_iRecord] mov ecx, eax shr eax, 3 and ecx, 7 mov edi, [ebp+NTFS.mftBitmapBuffer] btr [edi+eax], ecx shr eax, 9 mov ebx, eax shl ebx, 9 add eax, [ebp+NTFS.mftBitmapLocation] add ebx, edi mov ecx, 1 xor edx, edx call fs_write64_sys mov esi, [ebp+NTFS.frs_buffer] mov byte [esi+recordFlags], 0 .writeFileRecord: dec byte [esi+hardLinkCounter] mov [ebp+NTFS.ntfs_cur_buf], esi call writeRecord ; write directory node mov eax, [ebp+NTFS.nodeLastRead] mov [ebp+NTFS.ntfsLastRead], eax mov eax, [ebp+NTFS.cur_index_buf] mov [ebp+NTFS.ntfs_cur_buf], eax call writeRecord jmp ntfsDone ;---------------------------------------------------------------- ntfs_SetFileEnd: ntfs_SetFileInfo: movi eax, ERROR_UNSUPPORTED_FS ret ;---------------------------------------------------------------- ntfs_GetFileInfo: cmp byte [esi], 0 jnz @f movi eax, ERROR_UNSUPPORTED_FS ret @@: call ntfs_lock stdcall ntfs_find_lfn, [esp+4] jnc .found test eax, eax jz ntfsFail jmp ntfsNotFound .found: push esi edi mov esi, eax mov edi, [ebx+16] xor eax, eax call ntfs_direntry_to_bdfe pop edi esi call ntfs_unlock xor eax, eax ret ntfsUnsupported: push ERROR_UNSUPPORTED_FS jmp ntfsOut ntfsDevice: push ERROR_DEVICE jmp ntfsOut ntfsNotFound: push ERROR_FILE_NOT_FOUND jmp ntfsOut ntfsDenied: push ERROR_ACCESS_DENIED jmp ntfsOut ntfsFail: push ERROR_FS_FAIL jmp ntfsOut ntfsNoMemory: push ERROR_OUT_OF_MEMORY jmp ntfsOut ntfsDiskFull: push ERROR_DISK_FULL jmp ntfsOut ntfsError: pop ebx push eax ntfsOut: call ntfs_unlock xor ebx, ebx pop eax ret