With XDR we are adding a pair of new Sophos extensions GREP and HEX_TO_INT both of these come in handy when you want to read a file and show the contents as the result of a query.
ASCII DUMP
-- Perform an ASCII DUMP for a file -- VARIABLE $$File Path$$ STRING SELECT CAST(line AS TEXT) '$$File Path$$' FROM grep WHERE pattern IN (CHAR(0),CHAR(10),CHAR(32)) AND path = '$$File Path$$'
HEX DUMP
-- Perform a HEX DUMP for a file -- VARIABLE $$File Path$$ STRING -- VARIABLE $$Max Bytes to dump$$ STRING -- Prepare a few tables that we will need to perform the hex dump WITH RECURSIVE -- EACH row in the file has 16 hex pairs Counter(x) AS ( VALUES ( ( 0 ) ) UNION ALL SELECT x+16 FROM Counter WHERE x < $$Max Bytes to dump$$ ), -- Here we DUMP the file as a LONG HEX STRING RAW_DUMP AS ( SELECT SUBSTR(GROUP_CONCAT(HEX(line),''),0,$$Max Bytes to dump$$) FileBody FROM grep WHERE pattern IN (CHAR(0),CHAR(10),CHAR(32)) AND path = '$$File Path$$' ) -- WITH The file converted to a HEX String we now need to display the iformation SELECT -- Provide a col telling the reader the position in the file being shown printf('%08X', x) position, -- Build the hex dump col. Col name is that filepath being searched SUBSTRING(FileBody,x*2+1, 2 )||' '||SUBSTRING(FileBody,x*2+3, 2 )||' '||SUBSTRING(FileBody,x*2+5, 2 )||' '||SUBSTRING(FileBody,x*2+7, 2 )||' '|| SUBSTRING(FileBody,x*2+9, 2 )||' '||SUBSTRING(FileBody,x*2+11, 2 )||' '||SUBSTRING(FileBody,x*2+13, 2 )||' '|| SUBSTRING(FileBody,x*2+15, 2 )|| ' | '|| SUBSTRING(FileBody,x*2+17, 2 )||' '||SUBSTRING(FileBody,x*2+19, 2 )||' '||SUBSTRING(FileBody,x*2+21, 2 )||' '||SUBSTRING(FileBody,x*2+23, 2 )||' '|| SUBSTRING(FileBody,x*2+25, 2 )||' '||SUBSTRING(FileBody,x*2+27, 2 )||' '||SUBSTRING(FileBody,x*2+29, 2 )||' '||SUBSTRING(FileBody,x*2+31, 2 ) '$$File Path$$', -- Decode the HEX To ASCII avoiding unprintable characters (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+1, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+3, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+5, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+7, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+9, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+11, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+13, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+15, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+17, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+19, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+21, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+23, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+25, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+27, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+29, 2 )) ||' '|| (SELECT CASE int IN ( 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,127,129,141,143,144,157,160,173 ) WHEN 0 THEN CHAR(int) ELSE '_' END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+31, 2 )) ASCII FROM Counter JOIN RAW_DUMP WHERE HEX(substring(fileBody,x*2+1,1)) -- Just check if there is data ORDER BY CAST(x AS INT) ASC
You can also do other stuff like a STRING Search and a DUMP of the contents of files in a directory.
List Strings in a binary file
-- STRINGS Function - Searches a file for strings works with binary data -- VARIABLE $$File path$$ STRING -- VARIABLE $$Max bytes to dump$$ STRING -- VARIABLE $$Minimum string length$$ STRING -- VARIABLE $$String to look for$$ STRING -- Prepare a few tables that we will need to perform the hex dump WITH RECURSIVE -- We want to process each character seperatly so we will create a table with a counter from 0 to the Max Bytes to dump variable Counter(x) AS (VALUES ( ( 0 ) ) UNION ALL SELECT x+1 FROM Counter WHERE x < $$Max bytes to dump$$), -- DUMP the file as a LONG HEX STRING RAW_DUMP AS ( SELECT SUBSTR(GROUP_CONCAT(HEX(line),''),0,$$Max bytes to dump$$) FileBody FROM grep WHERE pattern IN (CHAR(0),CHAR(10),CHAR(32)) AND path = '$$File path$$'), -- Build a single line with unprintable characters converted to CHAR(10) 'NewLine' CLEAN_DUMP AS ( SELECT GROUP_CONCAT( (SELECT CASE CAST(int AS INTEGER) BETWEEN 32 and 127 WHEN 1 THEN CHAR(int) ELSE char(10) END FROM hex_to_int WHERE hex_string = '0x'||SUBSTRING(FileBody,x*2+1, 2 )),'') Clean_Strings FROM Counter JOIN RAW_DUMP WHERE HEX(substring(fileBody,x*2+1,1)) -- Just check if there is data ORDER BY CAST(x AS INT) ASC ), -- Create a table with all the a single String per row Table_of_Strings(String, Line) AS ( SELECT '', (SELECT Clean_Strings FROM CLEAN_DUMP)||CHAR(10) UNION ALL SELECT substr(Line, 0, instr(Line, CHAR(10) )), substr(Line, instr(Line, CHAR(10) )+1) FROM Table_of_Strings WHERE Line!='' ) -- last we select strings that are >= the MIN String Length and match our filter String to look for SELECT DISTINCT '$$File path$$' File_Path, CAST(String AS TEXT) List_Of_Keywords, COUNT(String) 'Instances string appears in file' FROM Table_of_Strings WHERE LENGTH(String) > $$Minimum string length$$ AND String LIKE '%$$String to look for$$%' GROUP BY String ORDER BY String ASC
Search for Key Words
-- SEARCH FILE FOR A KEYWORD -- VARIABLE $$FilePath$$ STRING -- VARIABLE $$Keyword$$ STRING -- GET a list of files (LIMIT to 100MB filesize) WITH File_List AS (SELECT path,size FROM file WHERE path LIKE '$$FilePath$$%' AND size < 104857600) -- ASCII DUMP the Files SELECT replace(f.path, rtrim(f.path, replace(f.path, '\', '')), '') File_name, f.size FileSize, 'Keyword: '||'$$Keyword$$'||' FOUND' Match_Result, (SELECT SUBSTRING(line,0, 2048) FROM grep g WHERE g.pattern = '$$Keyword$$' AND g.path = f.path) Line_from_file, f.path File_Path FROM File_List f WHERE f.path LIKE '$$FilePath$$%' AND file_name > '' AND Line_from_file > '' ORDER BY Match_Result, File_Name
Dump contents of a a file type from a directory and sub directory
-- GET a list of files WITH File_List AS (SELECT path,size FROM file WHERE path LIKE '$$Directory$$%') -- ASCII DUMP the Files SELECT replace(f.path, rtrim(f.path, replace(f.path, '\', '')), '') File_name, f.size FileSize, CAST( SUBSTRING((SELECT CAST(GROUP_CONCAT(line,CHAR(10)) AS TEXT) FROM grep g WHERE g.pattern IN (char(10),char(32),char(13)) AND g.path = f.path),0,$$Max Bytes per File$$) AS TEXT) Contents, f.path File_Path FROM File_List f WHERE f.path LIKE '%$$File extension (eg TXT)$$'