Page 1 of 2

Append text to exe then read it

Posted: Sun Oct 24, 2004 3:35 am
by newcoder
I was wondering if someone could tell me how to bind text to an exe then let the program read itself and get the text. See i'm creating a very small interpreter and need to be a ble to create standalone exe's

If someone has a better method please tell me

Posted: Sun Oct 24, 2004 5:32 am
by griz
1. Create your EXE

2. Write a utility that makes a copy of your EXE, adds your text/data and a long (or byte or whatever) which contains the length of the text/data. (textsize)

3. In your original EXE PB code, open the EXE file and read the last 4 bytes (if you used a long) to get the text size. Use Fseek()

4. Fseek(filelength-textlength-4) and read in the text/data.

Hope this helps.

Posted: Sun Oct 24, 2004 5:49 am
by newcoder
I'm so sorry but I just cant get that to work, can I maybe get an example if it is not too much to ask? :cry:

Posted: Sun Oct 24, 2004 10:34 am
by griz
Save the following file as program.pb and compile it to program.exe. Don't run it just yet

Code: Select all

; program.pb
; EXE Message Extractor by Griz Oct 2004

; API Call to get EXE Name
Procedure.s GetExeName() 
  sApp.s=Space(256) 
  GetModuleFileName_(GetModuleHandle_(0), @sApp, 256) 
  ProcedureReturn sApp 
EndProcedure 

; read last 4 bytes of exe as a long to determine how large
; the appended message is, and then retrieve it.
Procedure GetExeData()
  pfile.s=getexename()
  psize=FileSize(pfile)
  infile=ReadFile(#pb_any, pfile)
  If infile
    FileSeek(psize-4)
    msize=ReadLong()
    FileSeek(psize-4-msize)
    message.s=ReadString()
    MessageRequester("Success!", message)
    CloseFile(infile)
    ProcedureReturn 1
  Else
    MessageRequester("Error", "Can't open "+pfile)
    ProcedureReturn 0
  EndIf
EndProcedure

getexedata()
Save the following listing as append.pb and compile to append.exe. Launch the append exe and a new exe will be created called output.exe. Make sure both program.exe and append.exe are in the same directory. Output.exe is a copy of program.exe with the message appended to it, plus a 4 byte long describing it's length.

Code: Select all

; append.pb
; EXE Message Appender by Griz Oct 2004

Global exein.s, exeout.s, messageout.s
exein="program.exe" ; this is the source program file
exeout="output.exe" ; this is the output exe filename
messageout="Made with PURE BASIC!" ; the message to append

; API Call to get EXE Name
Procedure.s GetExeName() 
  sApp.s=Space(256) 
  GetModuleFileName_(GetModuleHandle_(0), @sApp, 256) 
  ProcedureReturn sApp 
EndProcedure 

; Append a string to the end of an EXE + 4 byte long for size
Procedure PutExeData(pfile.s, message.s)
  ; add a cr+lf to identify end of string if required
  If FindString(message,Chr(13),1)=0
    message+Chr(13)+Chr(10)
  EndIf 
  psize=FileSize(pfile)
  If psize>0
    ; delete old output exe file if one exists
    DeleteFile(GetPathPart(pfile)+exeout)
    ; make a copy of the original exe
    If CopyFile(pfile,GetPathPart(pfile)+exeout)
      infile=OpenFile(#pb_any, exeout)
      If infile
        FileSeek(psize)
        WriteString(message) ; add the message    
        WriteLong(Len(message)) ; last 4 bytes (long) = the length of message
        MessageRequester("Status", "Message appended to EXE!")
        CloseFile(infile)
        ProcedureReturn 1
      Else
        MessageRequester("Error", "Can't open "+exeout)
        ProcedureReturn 0
      EndIf
    Else
      MessageRequester("Error", "Can't copy "+pfile)
      ProcedureReturn 0
    EndIf
  Else
    MessageRequester("Error", "Can't open "+pfile)
    ProcedureReturn 0
  EndIf
EndProcedure

PutExeData(GetPathPart(getexename())+exein,messageout)
When you launch output.exe it should display the appended message. I wrote this code very late at night so please excuse any errors.
This code only works with a single line message (ending with CRLF). It would need to be enhanced to support multi-line strings or memory buffers. It's just a starting point.

Posted: Sun Oct 24, 2004 5:15 pm
by newcoder
Thank u griz you are brilliant!

Posted: Tue Oct 26, 2004 12:09 am
by GeoTrail
Great example griz :)

Posted: Tue Oct 26, 2004 2:25 am
by TerryHough
Something like this might do what you want easier.

Code: Select all

TextString.s

Restore NextLabel:
Read TextString
MessageRequester("Example","The last embedded text is: " + Chr(10) + TextString,0)

TextString = ""
Restore TextLabel:
Read TextString
MessageRequester("Example","The first embedded text is: " + Chr(10) + TextString,0)

DataSection
  TextLabel:
  IncludeBinary "C:\Autoexec.bat"
  Data.s ""  ; Make sure we have a nul to stop the string read
  NextLabel:
  IncludeBinary "C:\Purebasic\Beta-R~1.txt"
  Data.s ""  ; Make sure we have a nul to stop the string read
EndDataSection

Posted: Tue Oct 26, 2004 10:08 am
by Max.²
Maybe another suitable approach to this is to use ADS streams.

First some links:

Streams overview http://lists.gpick.com/pages/NTFS_Alter ... treams.htm
StreamsExplorer Utility http://www.rekenwonder.com/streamexplorer.htm
Purebasic Code by Rings http://www.robsite.de/php/pureboard/vie ... ht=streams

Code by Rings very slightly adopted:

Code: Select all

;1. Compile this program to C:\Streams.exe and run it
;2. Use the StreamsExplorer or the second snippet to check the results

FILENAME.s="C:\streams.exe" 
Filetoadd.s=":testme.txt" 

F.s=FILENAME+Filetoadd 
If CreateFile(1,F) 
 Debug "yes" 
 WriteStringN("Test me") 
 CloseFile(1) 
 If ReadFile(1,F) 
  nop.s=ReadString() 
  Debug nop.s 
  CloseFile(1) 
 EndIf 
EndIf
Test if it worked

Code: Select all

;run this after compiling and executing streams.exe
;leave the debugger enabled

FILENAME.s="C:\streams.exe" 
Filetoadd.s=":testme.txt" 

F.s=FILENAME+Filetoadd 
If ReadFile(1,F) 
  nop.s=ReadString() 
  Debug nop.s 
  CloseFile(1) 
EndIf 
Works also if streams.exe is still in memory. :D

Posted: Tue Oct 26, 2004 8:34 pm
by griz
Hi guys,

I've added to my code above so it now supports any file to be attached as a data stub to an EXE. As well, it supports packing and RC4 encryption. I haven't tested it much but it has worked for me on several attempts.

newcoder : I think you would appreciate packing and encryption on the program listings you intend to imbed on your interpreter runtime exe.

terryhough : I believe newcoder wanted to attach a data stub to an existing EXE rather than creating it in the PB IDE as you suggest.

Geotrail : thanks!

Max2 : ADS streams look interesting! Maybe I re-invent the wheel too much?

append.pb is a simple, generic utility that allows you to attach a data stub to any executable. The data stub can be a text file or any binary file.

Code: Select all

; ... EXE Data Stub Appender 0.1d by Griz October 2004 ...
; Appends a data stub to EXE : exe+data+packedsize(long)+unpackedsize(long)

Global datafile.s   : datafile=""
Global exein.s      : exein=""
Global exeout.s     : exeout=""
Global messageout.s : messageout=""
Global password.s   : password=""
Global originalsize

Procedure Mod(a,b) 
   ProcedureReturn a-(a/b)*b 
EndProcedure 

; RC4 from Pille / Rings
Procedure.l RC4Mem(Mem.l, memLen.l, Key.s) 
  Dim S.w(255) 
  Dim K.w(255) 
  i.l=0: j.l=0: t.l=0: x.l=0 
  temp.w=0: Y.w=0 
  Outp.s="" 
    For i = 0 To 255 
        S(i) = i 
    Next 
    j = 1 
    For i = 0 To 255 
        If j > Len(key) 
            j = 1 
        EndIf 
        K(i) = Asc(Mid(key, j, 1)) 
        j = j + 1 
    Next i 
    j = 0 
    For i = 0 To 255 
        j = Mod(j + S(i) + K(i), 256) 
        temp = S(i) 
        S(i) = S(j) 
        S(j) = temp 
    Next i 
    i = 0 
    j = 0 
    For x = 0 To memLen-1 
        i = Mod(i + 1, 256) 
        j = Mod(j + S(i),256) 
        temp = S(i) 
        S(i) = S(j) 
        S(j) = temp 
        t = Mod(S(i) + Mod(S(j), 256) , 256) 
        Y = S(t) 
        PokeB(Mem+x, PeekB(Mem+x)!Y) 
    Next 
  ProcedureReturn Mem 
EndProcedure

MessageRequester("Appender","version 0.1d by Griz",#PB_MessageRequester_Ok)
exein=OpenFileRequester("Choose Source EXE", "", "Executable (*.exe)|*.exe|All files (*.*)|*.*" , 0)
If exein
  datafile=OpenFileRequester("Choose Source Data File", "", "Text (*.txt)|*.txt;*.bat|All files (*.*)|*.*" , 0)
  If datafile
    exeout=SaveFileRequester("Filename for output EXE", "", "Executable (*.exe)|*.exe;*.bat|All files (*.*)|*.*" , 0)
    If exeout
      password.s=InputRequester("RC4 Encryption","Please enter a password :","test123")
      If ReadFile(0, datafile)
        FileLength = Lof()
        source=AllocateMemory(FileLength)
        If filelength And source
          ; read data stub into memory
          ReadData(source,filelength)
          ; pack the data 
          originalsize=filelength
          sourcetemp=AllocateMemory(filelength+8)
          If sourcetemp
            f=PackMemory(source,sourcetemp,filelength)
            FreeMemory(source)
            source=AllocateMemory(f)
            CopyMemory(sourcetemp,source,f)
            FreeMemory(sourcetemp)
            filelength=f
            ; read the exe into memory
            If ReadFile(1,exein)
              exefilelength=Lof()
              destination=AllocateMemory(filelength+exefilelength+4+4)
              If destination
                ReadData(destination,exefilelength)
                CopyMemory(source,destination+exefilelength,filelength)
                ; size of data (packed)
                PokeL(destination+exefilelength+filelength,filelength) 
                ; size of data (unpacked)
                PokeL(destination+exefilelength+filelength+4,originalsize) 
                ; encrypt the data stub
                rc4mem(destination+exefilelength,filelength,password)
                ; write the destination exe
                If OpenFile(2,exeout)
                  WriteData(destination,exefilelength+filelength+4+4)
                  MessageRequester("Success!", "EXE Generated")
                EndIf
                FreeMemory(destination)
              EndIf
            EndIf 
          EndIf
          FreeMemory(source)
        EndIf
        CloseFile(0)
      EndIf
    EndIf
  EndIf 
EndIf
End 
program.pb demonstrates accessing the data stub that append.pb added to the exe. This demonstration will show the
first 512 bytes of a text file, but it should work on binary files too.

Code: Select all

; .. EXE Data Stub Decoder 0.1d by Griz October 2004 ..
; Read the last 4 bytes (long) as Data size then
; Read the data stub appended to end of the EXE

Global password.s : password="test123" ; rc4 password
Global originalsize

; API Call to get EXE Name
Procedure.s GetExeName() 
  sApp.s=Space(256) 
  GetModuleFileName_(GetModuleHandle_(0), @sApp, 256) 
  ProcedureReturn sApp 
EndProcedure 

Procedure Mod(a,b) 
   ProcedureReturn a-(a/b)*b 
EndProcedure 

; RC4 from Pille / Rings
Procedure.l RC4Mem(Mem.l, memLen.l, Key.s) 
  Dim S.w(255) 
  Dim K.w(255) 
  i.l=0: j.l=0: t.l=0: x.l=0 
  temp.w=0: Y.w=0 
  Outp.s="" 
    For i = 0 To 255 
        S(i) = i 
    Next 
    j = 1 
    For i = 0 To 255 
        If j > Len(key) 
            j = 1 
        EndIf 
        K(i) = Asc(Mid(key, j, 1)) 
        j = j + 1 
    Next i 
    j = 0 
    For i = 0 To 255 
        j = Mod(j + S(i) + K(i), 256) 
        temp = S(i) 
        S(i) = S(j) 
        S(j) = temp 
    Next i 
    i = 0 
    j = 0 
    For x = 0 To memLen-1 
        i = Mod(i + 1, 256) 
        j = Mod(j + S(i),256) 
        temp = S(i) 
        S(i) = S(j) 
        S(j) = temp 
        t = Mod(S(i) + Mod(S(j), 256) , 256) 
        Y = S(t) 
        PokeB(Mem+x, PeekB(Mem+x)!Y) 
    Next 
  ProcedureReturn Mem 
EndProcedure

Procedure GetExeData()
  pfile.s=getexename()
  psize=FileSize(pfile)
  infile=ReadFile(#pb_any, pfile)
  If infile
    ; read packed size of data stub
    FileSeek(psize-8)
    msize=ReadLong() 
    ; read unpacked size of data stub
    FileSeek(psize-4)
    originalsize=ReadLong() 
    FileSeek(psize-8-msize)
    source=AllocateMemory(msize)
    If source
      ; read data stub into memory buffer
      ReadData(source,msize) 
      ; decrypt the data stub
      rc4mem(source,msize,password)
      ; unpack the data stub
      sourcetemp=AllocateMemory(originalsize)
      If sourcetemp
        UnpackMemory(source,sourcetemp)
        FreeMemory(source)
        source=AllocateMemory(originalsize)
        CopyMemory(sourcetemp,source,originalsize)
        FreeMemory(sourcetemp)
        ; ----------------------------------------
        ; data is now contained in 'source' memory bank
        ; below we simply capture it all as a string (for example)
        d$=PeekS(source,originalsize)  
        If Len(d$)>512 ; only show the first 100 characters in messagebox
          d$=Left(d$,512)+" <more...>"
        EndIf       
        MessageRequester("Success!", "data size ="+Str(msize)+"/"+Str(originalsize)+Chr(13)+Chr(10)+Chr(13)+Chr(10)+d$)
        FreeMemory(source)
        ; ----------------------------------------
      EndIf 
    EndIf
    CloseFile(infile)
  EndIf
EndProcedure

getexedata()

Posted: Tue Oct 26, 2004 9:56 pm
by newcoder
Wow griz you are a great programmer, Actually the same day you posted that code I managed to get a very very very simple interpreter going. It was meant to be simple batch language replacement for my own use.
The thing about it thanks to PureBasic and your tight code, the runtime is only 10 kb.

It has Commands like MoveFile ,Run , CopyFile, just simple functions. But I have plans for another interpreter that is actually useful. Its going to be a console mode interpeter. That is designed to create real small exe's.

Thank you Griz and Fred! for purebasic

Posted: Tue Oct 26, 2004 10:30 pm
by fsw
griz wrote: Max2 : ADS streams look interesting! Maybe I re-invent the wheel too much?
ADS works only on NTFS file systems.

Posted: Wed Oct 27, 2004 7:19 am
by Max.²
griz wrote:Max2 : ADS streams look interesting! Maybe I re-invent the wheel too much?
As fsw said, it works only on NTFS (since NT 3.1, btw) and also some commands, like ren & copy, don't work with them.

On the other hand it is a really simple approach that might come handy for specific needs.

Posted: Sun Jan 22, 2006 2:35 am
by Dare2
Belated thanks!

Posted: Sun Jan 22, 2006 9:00 pm
by Kale
Also take a look at:
viewtopic.php?t=14358

Posted: Mon Jul 30, 2007 5:01 pm
by Seymour Clufley
Hi,

I'm trying to do exactly what the OP was trying to do.

I've made an app using Griz's code but the exe's it produces don't work. When you run them, they give this error:

Multimedia Player
Corrupted Stand Alone File (1)


"Multimedia Player" is probably to do with the input exe, which was made with Multimedia Builder.

The problem may be that (I think) Griz's code is not PB 4.0 compliant. I got a few debugging messages with the Lof, ReadData and WriteData lines, so I added the 1 and 0 as I thought appropriate. For example, I changed:
If OpenFile(2,exeout)
WriteData(destination,exefilelength+filelength+4+4)
MessageRequester("Success!", "EXE Generated")
EndIf
to:
If OpenFile(2,exeout)
WriteData(destination,2,exefilelength+filelength+4+4)
MessageRequester("Success!", "EXE Generated")
EndIf
I also tried it this way:
If OpenFile(2,exeout)
WriteData(2,destination,exefilelength+filelength+4+4)
MessageRequester("Success!", "EXE Generated")
EndIf
but the result is the same.

Can anyone help?

Thanks in advance,
Seymour.