Announcement

Collapse

Forum Guidelines

This forum is for finished source code that is working properly. If you have questions about this or any other source code, please post it in one of the Discussion Forums, not here.
See more
See less

Extract files from Visual Basic VB5/VB6 .FRX form resource files

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • PBWin/PBCC Extract files from Visual Basic VB5/VB6 .FRX form resource files

    See here for a Discussion Thread regarding this thread and the FRX file format.
    Thanks to Mike Simmonds for his help during the reverse engineering stages
    ___

    Microsoft Visual Basic's FRX file format is used to store resources (icons, bitmaps etc) for VB Forms. An FRX file is very similar in nature (but different in structure) to a Powerbasic PBR file. The location and type of each object in the file is described in the form's accompanying FRM file.

    Unlike all the other "FRX reader" demos that I've seen, this is the only one that actually properly "walks" the file using the FRX file format. All other demos I've seen use brute-force scanning techniques, which are often inaccurate, and are also incapable of properly detecting the start and end of text and list datatypes.

    FRX supports three basic datatypes - Text, List, and Binary. This demo supports all three. In the case of Binary (which includes pictures, icons, cursors, multimedia and so on), the file header is analysed to determine which type of file it is (ie. BMP, JPG, GIF...). Unknown binaries are saved as .DAT.

    Code:
    #COMPILE EXE
    
    GLOBAL gsDumpDir AS STRING
    
    
    SUB SaveFile (sFilename AS STRING, sData AS STRING)
    LOCAL hFile AS DWORD
    hFile = FREEFILE
    OPEN gsDumpDir & sFilename FOR BINARY ACCESS WRITE AS #hFile
     PUT #hFile, 1, sData
    CLOSE #hFile
    END SUB
    
    
    FUNCTION FRXGetText(sFRXFile AS STRING, BYVAL dwOffset AS DWORD) AS STRING
    LOCAL hFile AS DWORD, sBuf AS STRING, bPtr AS BYTE PTR, wPtr AS WORD PTR, dwPtr AS DWORD PTR
    hFile = FREEFILE
    OPEN sFRXFile FOR BINARY ACCESS READ LOCK SHARED AS #hFile BASE = 0
     SEEK #hFile, dwOffset
     GET$ #hFile, LOF(hFile), sBuf
    CLOSE #hFile
    bPtr = STRPTR(sBuf)
    IF @bPtr = &hFF THEN
        wPtr = bPtr + 1
        FUNCTION = MID$(sBuf, 4, @wPtr)
    ELSEIF @bPtr = &h00 THEN
        dwPtr = bPtr
        FUNCTION = MID$(sBuf, 5, @dwPtr)
    ELSE
        FUNCTION = MID$(sBuf, 2, @bPtr)
    END IF
    END FUNCTION
    
    
    FUNCTION FRXGetList(sFRXFile AS STRING, BYVAL dwOffset AS DWORD) AS STRING
    LOCAL hFile AS DWORD, sBuf AS STRING, wPtr AS WORD PTR, dwListItems AS DWORD, i AS LONG, sList AS STRING
    hFile = FREEFILE
    OPEN sFRXFile FOR BINARY ACCESS READ LOCK SHARED AS #hFile BASE = 0
     SEEK #hFile, dwOffset
     GET$ #hFile, LOF(hFile), sBuf
    CLOSE #hFile
    wPtr = STRPTR(sBuf)
    dwListItems = @wPtr
    wPtr = wPtr + 4
    FOR i = 1 TO dwListItems
        sList = sList & MID$(sBuf, wPtr - STRPTR(sBuf) + 3, @wPtr) & $CRLF
        wPtr = wPtr + @wPtr + 2
    NEXT i
    FUNCTION = sList
    END FUNCTION
    
    
    FUNCTION FRXGetBinary(sFRXFile AS STRING, BYVAL dwOffset AS DWORD) AS STRING
    LOCAL hFile AS DWORD, sBuf AS STRING, dwPtr AS DWORD PTR, sData AS STRING
    hFile = FREEFILE
    OPEN sFRXFile FOR BINARY ACCESS READ LOCK SHARED AS #hFile BASE = 0
     SEEK #hFile, dwOffset
     GET$ #hFile, LOF(hFile), sBuf
    CLOSE #hFile
    dwPtr = STRPTR(sBuf) + 8
    sData = MID$(sBuf, 13, @dwPtr)
    FUNCTION = sData
    END FUNCTION
    
    
    FUNCTION GetFileExt(sData AS STRING) AS STRING
    SELECT CASE LEFT$(sData,2)
     CASE CHR$(&h42,&h4D): FUNCTION = ".bmp"
     CASE CHR$(&hFF,&hD8): FUNCTION = ".jpg"
     CASE CHR$(&h47,&h49): FUNCTION = ".gif"
     CASE CHR$(&h49,&h49): FUNCTION = ".tif"
     CASE CHR$(&h89,&h50): FUNCTION = ".png"
     CASE CHR$(&hD7,&hCD): FUNCTION = ".wmf"
     CASE ELSE: FUNCTION = ".dat"
    END SELECT
    END FUNCTION
    
    
    SUB DumpVBForm (sVBFile AS STRING)
    LOCAL sFRMFile AS STRING, sFRXFile AS STRING, hFile AS DWORD, sLine AS STRING, i AS LONG, sOffset AS STRING
    LOCAL sCurObj AS STRING, sCurVal AS STRING, sData AS STRING, sObject AS STRING
    sFRMFile = LEFT$(sVBFile, LEN(sVBFile) - 1) & "m"
    sFRXFile = LEFT$(sVBFile, LEN(sVBFile) - 1) & "x"
    MID$(sFRMFile, LEN(sFRMFile) - 2, 3) = LCASE$(RIGHT$(sFRMFile,3))
    MID$(sFRXFile, LEN(sFRXFile) - 2, 3) = LCASE$(RIGHT$(sFRXFile,3))
    hFile = FREEFILE
    ERRCLEAR
    OPEN sFRMFile FOR INPUT LOCK SHARED AS #hFile
     IF ERR THEN
         ? "ERROR: Couldn't open " & sFRMFile: EXIT SUB
     END IF
     LINE INPUT #hFile, sLine
     IF sLine <> "VERSION 5.00" THEN    '// 5.00 = VB5 & VB6
         ? "ERROR: FRM file version invalid (not VB5/6)": EXIT SUB
     END IF
     DO UNTIL EOF(hFile)
        LINE INPUT #hFile, sLine
        sLine = TRIM$(sLine)
        IF sLine = "" THEN ITERATE
        DO
            IF INSTR(1, sLine, "  ") = 0 THEN EXIT DO
            REPLACE "  " WITH " " IN sLine
        LOOP
        IF LEFT$(sLine,6) = "Begin " THEN
            sObject = sObject & RIGHT$(sLine, LEN(sLine) - INSTR(-1, sLine, " ")) & "."
        ELSEIF sLine = "End" THEN
            sObject = LEFT$(sObject, INSTR(-2, sObject, "."))
        ELSEIF sLine = "Attribute" THEN 'Ignored
        ELSE
            IF sObject = "" THEN ITERATE
            sCurObj = LEFT$(sLine, INSTR(1, sLine, " =") - 1)
            sCurVal = RIGHT$(sLine, LEN(sLine) - INSTR(1, sLine, " =") - 2)
            IF RIGHT$(sCurVal,1) <> CHR$(34) THEN
             IF INSTR(1, sCurVal, ":") THEN
              sOffset = RIGHT$(sCurVal, LEN(sCurVal) - INSTR(-1, sCurVal, ":"))
              IF sOffset <> "" THEN
                SELECT CASE sCurObj
                 CASE "Text": SaveFile sObject & sCurObj & ".txt", FRXGetText(sFRXFile, BYVAL VAL("&h0" & sOffset))
                 CASE "List", "ItemData": SaveFile sObject & sCurObj & ".txt", FRXGetList(sFRXFile, BYVAL VAL("&h0" & sOffset))
                 CASE "Picture", "DisabledPicture", "DownPicture", "Palette": sData = FRXGetBinary(sFRXFile, BYVAL VAL("&h0" & sOffset))
                                          SaveFile sObject & sCurObj & GetFileExt(sData), sData
                 CASE "Icon", "MouseIcon", "DragIcon": sData = FRXGetBinary(sFRXFile, BYVAL VAL("&h0" & sOffset))
                                          SaveFile sObject & sCurObj & ".ico", sData
                 CASE ELSE: sData = FRXGetBinary(sFRXFile, BYVAL VAL("&h0" & sOffset))
                                          SaveFile sObject & sCurObj & ".dat", sData
                END SELECT
              END IF
             END IF
            ELSE
                IF sCurObj = "Text" THEN SaveFile sObject & "Text.txt", MID$(sCurVal, 2, LEN(sCurVal) - 2)
            END IF
        END IF
     LOOP
    CLOSE #hFile
    END SUB
    
    
    FUNCTION PBMAIN () AS LONG
    LOCAL sVBForm AS STRING, i AS LONG
    
    '// Specify the VB Form to extract files from
    sVBForm = "e:\dev\vb98\temp\frx\multi\form1.frx"   '// .FRM or .FRX
     
    '// Specify directory to extract to, and ensure it exists
    gsDumpDir = "c:\frxdump"
    IF RIGHT$(gsDumpDir,1) = "\" THEN gsDumpDir = LEFT$(gsDumpDir, LEN(gsDumpDir) - 1)
    MKDIR gsDumpDir
    gsDumpDir = gsDumpDir & "\"
    
    '// Extract files from the VB Form
    DumpVBForm sVBForm
    
    '// Show the results
    i = SHELL("explorer " & gsDumpDir)
    END FUNCTION
    Last edited by Wayne Diamond; 21 Nov 2014, 05:58 AM.
    -

  • #2
    Visual Basic .FRX form resource file - structure example

    --
    Last edited by Wayne Diamond; 21 Nov 2014, 05:37 AM.
    -

    Comment


    • #3
      Here is an improved drop-in-replacement version of the SaveToFile function...
      1. Detects if a .ICOn file is actually a .CURsor (the two formats are nearly identical/often interchangeable), and saves the file extension as .cur accordingly if required
      2. Detects RIFF multimedia files in OLE data, extracting the multimedia files by detecting their exact size via header reading, and saving the file extension according to content type (.wav, .avi, .rmi, .cdr, .aco, .dls, .xma, .webp, and generic .riff for all others - it should be future-proof as far as RIFF goes due to its long history and simple format)

      Code:
      SUB SaveToFile (sFilename AS STRING, sData AS STRING)
       LOCAL hFile AS DWORD, i AS LONG
       IF LCASE$(RIGHT$(sFilename,4)) = ".ico" THEN '// Check if icon file is actually a cursor
          IF MID$(sData,3,1) = CHR$(2) THEN sFilename = LEFT$(sFilename, LEN(sFilename)-4) & ".cur"
       END IF
       hFile = FREEFILE
       OPEN gsDumpDir & "\" & sFilename FOR BINARY ACCESS WRITE AS #hFile
        PUT #hFile, 1, sData
       CLOSE #hFile
       IF LCASE$(RIGHT$(sFilename,4)) <> ".dat" THEN EXIT SUB
       i = INSTR(1, sData, "RIFF")   '// Check if content type is from RIFF-family
       IF i = 0 THEN EXIT SUB
       SELECT CASE MID$(sData, i+8, 3)
         CASE "WAV", "AVI", "RMI", "CDR", "ACO", "DLS", "XMA":
                     sFilename = sFilename & "." & LCASE$(MID$(sData, i+8, 3))
         CASE "WEB": sFilename = sFilename & ".webp"
         CASE ELSE : sFilename = sFilename & "." & LCASE$(MID$(sData, i+8, 3)) & ".riff"
       END SELECT
       sData = RIGHT$(sData, LEN(sData) - i + 1)
       i = PEEK(DWORD, STRPTR(sData) + 4) + 8  '// 2nd dword of RIFF header = data length (minus 8-byte header)
       IF i > LEN(sData) OR i <= 0 THEN i = LEN(sData)
       sData = LEFT$(sData, i)
       hFile = FREEFILE
       OPEN gsDumpDir & "\" & sFilename FOR BINARY ACCESS WRITE AS #hFile
        PUT #hFile, 1, sData
       CLOSE #hFile
      END SUB
      Last edited by Wayne Diamond; 19 Feb 2015, 03:42 PM.
      -

      Comment

      Working...
      X