Auteur Topic: VB.NET File Binder  (gelezen 2240 keer)

0 leden en 1 gast bekijken dit topic.

RaMp6

  • DigitalPlace Lid
  • *
  • Berichten: 187
  • Karma: 8
    • Bekijk profiel
VB.NET File Binder
« Gepost op: oktober 17, 2015, 09:58:40 pm »
  • [+1]0
  • [-1]0

  • Ik heb deze tutorial voor een ander forum geschreven vandaar dat de afbeelding achtergronden niet de goede kleur zijn.
    In deze tutorial ga ik jullie uitleggen hoe je een simpele file binder maakt in VB.NET.
    Een file binder maakt het mogelijk om meerdere bestanden in 1 te binden; Je hoeft maar 1 programma uit te voeren en alle bestanden of programma's die je gekozen hebt worden tegelijkertijd geopend.

    Dit wordt vaak gedaan om bijvoorbeeld virussen te combineren met 'echte' software.

    Deze binder kan in principe alle bestandsformaten uitvoeren, maar het 'output' bestand zal altijd een executable (.exe) bestand zijn.

    Ik probeer in deze tutorial zo veel mogelijk uit te leggen. Maar als er een functie gebruikt wordt die je niet begrijpt, kan je je muis er een paar seconden op laten staan en krijg je precies te zien wat deze functie doet, welke argumenten hij nodig heeft en wat hij voor waarde returnt.


    Ik raad aan om de code over te nemen in Visual Studio (2015 Community Edition) of te lezen op de pastebin links die ik er bij zet.
    De code is dan veel leesbaarder, en zie je ook snel wat een comment is.


    Als eerst een korte uitleg wat we precies gaan doen.

    We gaan 2 programma's maken:

    Het binder programma;
    Dit is het programmatje wat jij, wanneer je de binder gebruikt uit zal voeren.
    Dit programma heeft, in tegenstelling tot de stub, een GUI (Grafische interface)

    1. Maakt een nieuwe executable aan op de harddisk.
    2. Schrijft daar de stub executable naartoe.
    3. Schrijft het totale aantal bestanden in de volgende 4 Bytes. (In een Int32: dit is een getal die 4 Bytes in beslag neemt)
    3. Schrijft de grootte in Bytes van het eerste bestand (die je geselecteerd hebt om te binden) in de volgende 4 Bytes (weer een Int32)
    4. Schrijft de extensie van het bestand in de volgende 8 Bytes (met ASCII encoding gebruikt elk karakter 1 Byte, we kunnen dus maximaal 8 tekens van de extensie opslaan). Als je wilt kan je dit zo groot maken als je zelf wilt, vergeet dat dan niet in de stub ook aan te passen.
    5. Schrijft de binaire data van het bestand.

    Stap 3 t/m 5 worden voor elk gekozen bestand herhaalt.

    Zo komt de output executable van de binder er uit te zien


    Als je nu het 'output.exe' programma uitvoert leest Windows als eerste het stub gedeelte en voert dat uit. De data daarna zal door Windows genegeerd worden.

    De stub;
    Dit is een programmatje die de 'gebinde' executable uitleest (waar dus alle bestanden die uitgevoerd moeten worden in zitten) en alle bestanden een voor een uitvoert.

    In onze simpele binder worden de bestanden eerst opgeslagen in een tijdelijke map en daarna uitgevoerd.

    Dit is meer detecteerbaar door virusscans dan wanneer de programma's gelijk vanuit het RAM worden uitgevoerd.

    De programma's gelijk vanuit het RAM uitvoeren is iets ingewikkelder, dit kan in .NET met System.Reflection.Assembly.Load()


    Als eerste maken we een nieuw project Windows Forms Project.
    Noem deze bijvoorbeeld 'File Binder'.

    Design
    Voeg 2 buttons en 1 ListView toe.
    Geef een button de tekst "Select files..." en een "Bind".
    Zet de ListView.View op "Details" en voeg 3 kolommen toe, met de tekst:
    "Filename", "Size" en "Extension"
    Dit doe je door op het kleine pijltje rechtsboven bij de ListView te klikken.


    Voeg ook een OpenFileDialog en een SaveFileDialog toe.

    Code
    Ik heb geprobeerd zo veel mogelijk comments in de code te stoppen zodat je snapt wat er gebeurt.
    Comments in Visual Basic worden aangegeven met en enkele quote '

    Select Files...

    Dubbelklik op de "Select files..." button die je hebt toegevoegd en voeg de volgende code tussen Private Sub (...) en End Sub toe: http://pastebin.com/qw3SvLbc
    '
            'Als eerste openen we de OpenFileDialog en kijken we of hij is gesloten met de OK knop.
            If OpenFileDialog1.ShowDialog() = DialogResult.OK Then

                'dan loopen we door elke file
                For Each fileName As String In OpenFileDialog1.FileNames

                    'Hier openen we het huidige bestand als een FileStream en noemen hem fs
                    Dim fs = File.OpenRead(fileName)

                    'dan maken we een Array()genaamd listArr met 3 string items
                    Dim listArr(3) As String
                    'in het eerste item slaan we de filename op
                    listArr(0) = fileName
                    'in het tweede de de grootte van het bestand in Bytes
                    'daar hebben we dus die FileStream voor nodig
                    listArr(1) = fs.Length
                    'Dan pakken we de bestandsnaam (inclusief map) en splitsen we de naam bij elke punt
                    'Dan nemen we het laatste gedeelte. De bestandsextensie
                    'Die slaan we in het 3e item van de Array op
                    listArr(2) = fileName.Split(".").Last

                    'dan pakken we die array, maken er een ListViewItem van
                    'en voegen die toe aan ListView1
                    ListView1.Items.Add(New ListViewItem(listArr))

                    'Hier sluiten we de FileStream zodat het RAM geheugen niet vol raakt
                    fs.Close()
                Next

            End If

    Zorg ook dat je hélemaal bovenaan je code, dus nog boven Public Class Form1, het volgende toevoegt:
    Imports System.IO 'Dit wordt gebruikt om bestanden te lezen en te schrijven.
    Imports System.Text 'Dit wordt gebruikt voor het omzetten van strings naar Byte Array's

    Bind!

    Dubbelklik op de "Bind" knop en voeg daar deze code toe: http://pastebin.com/z1Yd4g3L
    '
            'Eerst openen we de SaveFileDialog
            'en kijken weer of deze is gesloten met de OK knop
            If SaveFileDialog1.ShowDialog = DialogResult.OK Then

                'Dan lezen we de stub als een Byte Array
                'de stub maken we in het volgende deel van deze tutorial
                Dim stub() As Byte = File.ReadAllBytes("stub.exe")

                'newFile is de FileStream van de nieuwe 'gebinde' executable
                'Met FileMode.Create wordt het bestand gelijkt aangemaakt
                Dim newFile As New FileStream(SaveFileDialog1.FileName, FileMode.Create)

                'Schrijf de Bytes van de stub.exe naar het nieuwe bestand
                newFile.Write(stub, 0, stub.Length)
               
                'dan schrijven we hoeveel bestanden we precies gaan binden
                'deze informatie heeft de stub straks nodig
                'onze stub is zo ingesteld dat hij straks deze 4 bytes leest als de totale aantal files
                'Waarom 4 Bytes? omdat een Int32 (Integer) uit 32bits bestaat. 32 bits / 8 = 4 Bytes
                newFile.Write(BitConverter.GetBytes(Convert.ToInt32(ListView1.Items.Count)), 0, 4)

                'Oke, tijd om de bestanden toe te voegen!
                'Loop door elk bestand en noem het 'item'
                For Each item In ListView1.Items

                    'Weet je nog dat we de bestandsnaam, grootte en extensie hebben toegevoegd?
                    'Die kunnen we nu bereiken met
                    'item.SubItems(0).Text = de bestandsnaam
                    'item.SubItems(1).Text = de grootte van het bestand in Bytes
                    'item.SubItems(2).Text = de bestandsextensie

                    'Dan openen we het bestand door weer een FileStream te maken
                    'en deze noemen we ook weer 'fs'
                    Dim fs = File.OpenRead(item.SubItems(0).Text)

                    'Nu maken we een Byte Array (een array met Bytes)
                    'en noemen die 'ext' voor de extensie
                    'Weet je nog dat we in het begin 'Imports System.Text' hebben gedaan?
                    'Dat hebben we nu nodig om de extensie String om te zetten naar een Byte Array.
                    Dim ext() As Byte = Encoding.ASCII.GetBytes(item.SubItems(2).Text)

                    'ASCII encoding gebruikt 1 Byte voor elk karakter
                    'We hebben 8 Bytes gereserveerd voor de extensie
                    'Maar wat als de extensie maar 3 karakters (dus 3 Bytes) is?

                    'Als we deze Bytes niet opvullen krijgen we een error bij het schrijven
                    'We willen namelijk 8 Bytes schrijven en hebben er misschien minder

                    'Dus vullen we de Byte array genaamd 'ext' op tot 8 items, met lege items (Null Bytes)
                    ReDim Preserve ext(7) '7 = 8 items, want hij begint bij 0

                    'Dan kijken we of we het bestand kunnen lezen
                    'Weet je nog dat we het bestand geopend hadden met de naam 'fs' ?
                    If fs.CanRead Then
                        'We maken een Byte Array met dezelfde hoeveelheid items als het bestand Bytes heeft
                        'We gebruiken nu fs.Length maar we konden net zo goed item.SubItems(1).Text gebruiken
                        'Alleen is dat een String en zouden we dat eerst om moeten zetten naar een Integer
                        'Vandaar dat ik opnieuw de grootte van het bestand uitlees
                        Dim fileBytes(fs.Length - 1) As Byte 'waarom '- 1'? Een array begint bij 0 en 'Length' bij 1
                        'Dan lezen we het bestand en schrijven de inhoud ervan naar de 'fileBytes' array
                        fs.Read(fileBytes, 0, fs.Length)

                        'Nu gaan we weer in de nieuwe 'gebinde' executable schrijven.
                        'Deze FileStream hadden 'newFile' genoemd

                        'Na de stub en de hoeveelheid files, wat we hierboven al geschreven hebben,
                        'schrijven we nu, in de volgende 4 Bytes de grootte van dit bestand die we willen binden
                        'Zoals je bij de uitleg misschien hebt gelezen: een Int32 (Integer) is 4 Bytes
                        newFile.Write(BitConverter.GetBytes(Convert.ToInt32(fs.Length)), 0, 4)
                        'in de volgende 8 bytes schrijven we de extensie
                        'als we 'ext' niet hadden opgevuld en de extensie heeft minder dan 8 tekens
                        'dan zouden we hier een error krijgen
                        newFile.Write(ext, 0,
                        'En als laatst schrijven we de bytes van het bestand zelf naar de nieuwe executable
                        newFile.Write(fileBytes, 0, fs.Length)

                        'En natuurlijk moeten we de FileStream weer sluiten om RAM vrij te maken
                        fs.Close()
                    Else 'Als we het bestand niet kunnen lezen:
                        'Verwijder dan het nieuwe bestand wat we tot nu toe geschreven hebben,
                        File.Delete(SaveFileDialog1.FileName)
                        'Sluit weer de FileStream
                        fs.Close()
                        'en geef een error.
                        MsgBox("File cannot be read:" & vbCrLf & item.SubItems(0).Text)
                        'Exit For stopt de 'For' Loop zodat er niet nieuwe bestanden toegevoegd worden wanneer we een error hebben
                        Exit For
                    End If
                Next

                'als alle bestanden successvol geschreven zijn, sluit het bestand
                newFile.Close()
                'en laat een berichtje zien dat we klaar zijn!
                MsgBox(ListView1.Items.Count & " files successfully bound.")

            End If


    Ga naar File -> Add -> New Project...
    Kies voor Windows Console Application
    Noem deze bijvoorbeeld "stub"

    Zorg dat je Module1.vb open heb staan


    Voeg helemaal bovenaan weer deze code toe:
    Imports System.IO 'Dit wordt gebruikt om bestanden te lezen en te schrijven.
    Imports System.Text 'Dit wordt gebruikt voor het omzetten van strings naar Byte Array's

    Zet nu tussen Module Module1 en Sub Main() dit:
    '
        'stubLen is de grootte van de stub.exe in Bytes
        'die van mij is 10240, die van jou misschien kleiner
        'de exacte grootte kunnen we pas zien als we de stub compiled hebben
        Public stubLen = 10240
        'De volgende waardes staan voor hoeveel ruimte we voor
        'het totaal aantal bestanden, grootte van t bestand en extensie hebben gereserveerd
        Public fileCountLen = 4 'int32 = 4 Bytes
        Public fileLenLen = 4
        Public fileExtLen = 8

    En nu het echte werk tussen Sub Main() en End Sub: http://pastebin.com/4AdUNKt8
    '
            'Try zodat het programma niet stopt bij een error
            'Zo kunnen we ook de error naar de console schrijven
            Try
                'System.Reflection.Assembly.GetExecutingAssembly.Location
                'is het volledige pad naar het huidige programma wat wordt uitgevoerd
                'ik hoop dat je onderhand snapt dat thisFile nu een FileStream is
                'zodat we dus DIT bestand zichzelf kunnen laten lezen
                Dim thisFile As New FileStream(System.Reflection.Assembly.GetExecutingAssembly.Location, FileMode.Open, FileAccess.Read)

                'bin wordt een BinaryReader
                'zodat we functies als Seek() kunnen gebruiken
                Dim bin As New BinaryReader(thisFile)

                'Nu hebben we de grootte van de stub (in Bytes) nodig
                'we willen namelijk beginnen met lezen vanaf waar de stub eindigt
                'dit doen we met de Seek() functie

                'Verplaats de reader naar het einde van de stub
                bin.BaseStream.Seek(stubLen, SeekOrigin.Begin)

                'Zoals je je misschien kan herinneren zijn de 4 Bytes na de stub
                'een Int32 integer waar we het totaal aantal gebinde bestanden in hebben geschreven

                'Lees dus de volgende 4 Bytes (fileCountLen = 4)
                'en converteer die 4 Bytes naar een Int32 integer
                Dim fileCount As Integer = BitConverter.ToInt32(bin.ReadBytes(fileCountLen), 0)

                'Nu hebben we dus in fileCount het totaal aantal bestanden staan.
                'En die hebben we hier weer nodig, namelijk om door alle bestanden te loopen
                For i As Integer = 1 To fileCount

                    'de BinaryReader (bin) is nu aangekomen bij de plek waar we
                    'de grootte van het bestand hebben opgeslagen.
                    'Zo weten we straks hoeveel Bytes we moeten lezen
                    'Lees dus de volgende 4 Bytes uit en maak daar weer een integer van
                    Dim fileLength As Integer = BitConverter.ToInt32(bin.ReadBytes(fileLenLen), 0) 'die noemen we fileLength

                    'In de volgende 8 Bytes heeft de binder de bestandsextensie opgeslagen
                    'Lees die dus weer uit en maak daar een String van (met ASCII encoding)
                    Dim fileExtension As String = Encoding.ASCII.GetString(bin.ReadBytes(fileExtLen))

                    'Weet je nog dat we in de binder de extensie hebben opgevuld met lege (Null) Bytes?
                    'Die Null Bytes moeten we weer verwijderen, dat doen we met Trim()
                    fileExtension.Trim(Chr(0))
                    'Elk teken heeft een getal, de Null Byte heeft het getal 0, vandaar Chr(0)
                    'Bekijk hier alle tekens: [url]http://www.roubaixinteractive.com/PlayGround/Binary_Conversion/The_Characters.asp[/url]

                    'En nu kunnen we eindelijk het bestand uitlezen en opslaan as Byte Array in fileBytes
                    Dim fileBytes() As Byte = bin.ReadBytes(fileLength)

                    'sla het bestand op en voer het bestand uit!
                    saveNrun(fileBytes, fileExtension)
                'ga naar het volgende bestand, totdat we het aantal in "fileCount" (2, 3 of misschien 20 bestanden?) hebben bereikt
                Next

                'sluit de BinaryReader en de FileStream om RAM vrij te maken
                bin.Close()
                thisFile.Close()
            'Als er een fout is opgetreden
            Catch ex As Exception
                'Kan je de volgende regel gebruiken om de foutmelding naar de console te schrijven
                'Console.WriteLine(ex.Message)

                'en sluit daarna weer de BinaryReader en de FileStream om RAM vrij te maken
                bin.Close()
                thisFile.Close()
            End Try

    Zo bijna klaar!

    Nog 1 functie en we zijn klaar om te compilen.
    Namelijk de functie saveNrun(), om het uitgelezen bestand op te slaan en uit te voeren.
    Dit kan je ook vervangen met een eigen runFromRAM() functie, gebruikmakend van System.Reflection.Assembly.

    Buiten je Sub Main(), dus na End Sub, maar nog wel in de Module, dus vóór End Module maak je de Sub saveNrun():
    '
        'Maak een "Sub", een functie zonder return waarde
        'saveNrun() heeft 2 argumenten, fileBytes en fileExtenstion
        Public Sub saveNrun(fileBytes() As Byte, fileExtension As String)
            'tmpFile is een String met een tijdelijke bestandsnaam in de Temp Windows map + de bestandsextensie
            Dim tmpFile = Path.GetTempFileName & "." & fileExtension
            'Sla het bestand in de tijdelijke map op
            File.WriteAllBytes(tmpFile, fileBytes)
            'De volgende regel kan handig zijn voor debugging
            'Console.WriteLine("Starting tmpFile: " & tmpFile)
           
            'En uiteindelijk voeren we het tijdelijke bestand uit en zijn we klaar!
            Process.Start(tmpFile)
        End Sub

    Nu compilen we het project door op Build -> Build Solution of CTRL + Shift + B te drukken.
    Het enige wat we nu nog moeten doen is checken hoe groot onze stub.exe precies is geworden.

    Ga naar je project map (C:\Users\JouwNaam\Documents\Visual Studio 2015\Projects\Binder\stub\bin\Debug)
    Rechtsklik op stub.exe en ga naar Properties.

    Vervang het getal bij Public stubLen = 10240 met dit aantal bytes en hercompileer je project.

    Nu moet je stub.exe in de zelfde map zetten als je binder, anders kan hij hem niet vinden.
    Je kan ook zelf proberen de stub.exe met je binder te binden zodat je maar 1 bestand overhoud voor je binder.



    Als er vragen zijn wil ik je graag helpen, maar probeer wel goed te lezen en alle stappen exact te volgen.

    Fout gevonden? Ik hoor het graag.

    Einderesultaat
    « Laatst bewerkt op: november 17, 2015, 07:13:41 pm door RaMp6 »

    AKA whiteRabbit