Append text to exe then read it

Just starting out? Need help? Post your questions and find answers here.
newcoder
User
User
Posts: 13
Joined: Tue Nov 25, 2003 11:34 pm

Append text to exe then read it

Post 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
User avatar
griz
Enthusiast
Enthusiast
Posts: 167
Joined: Sun Jun 29, 2003 7:32 pm
Location: Canada

Post 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.
newcoder
User
User
Posts: 13
Joined: Tue Nov 25, 2003 11:34 pm

Post 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:
User avatar
griz
Enthusiast
Enthusiast
Posts: 167
Joined: Sun Jun 29, 2003 7:32 pm
Location: Canada

Post 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.
Last edited by griz on Sun Oct 24, 2004 7:17 pm, edited 1 time in total.
newcoder
User
User
Posts: 13
Joined: Tue Nov 25, 2003 11:34 pm

Post by newcoder »

Thank u griz you are brilliant!
User avatar
GeoTrail
Addict
Addict
Posts: 2794
Joined: Fri Feb 13, 2004 12:45 am
Location: Bergen, Norway
Contact:

Post by GeoTrail »

Great example griz :)
I Stepped On A Cornflake!!! Now I'm A Cereal Killer!
TerryHough
Enthusiast
Enthusiast
Posts: 781
Joined: Fri Apr 25, 2003 6:51 pm
Location: NC, USA
Contact:

Post 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
Max.²
Enthusiast
Enthusiast
Posts: 175
Joined: Wed Jul 28, 2004 8:38 am

Post 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
User avatar
griz
Enthusiast
Enthusiast
Posts: 167
Joined: Sun Jun 29, 2003 7:32 pm
Location: Canada

Post 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()
newcoder
User
User
Posts: 13
Joined: Tue Nov 25, 2003 11:34 pm

Post 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
User avatar
fsw
Addict
Addict
Posts: 1603
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

Post by fsw »

griz wrote: Max2 : ADS streams look interesting! Maybe I re-invent the wheel too much?
ADS works only on NTFS file systems.
Max.²
Enthusiast
Enthusiast
Posts: 175
Joined: Wed Jul 28, 2004 8:38 am

Post 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.
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Belated thanks!
@}--`--,-- A rose by any other name ..
Kale
PureBasic Expert
PureBasic Expert
Posts: 3000
Joined: Fri Apr 25, 2003 6:03 pm
Location: Lincoln, UK
Contact:

Post by Kale »

Also take a look at:
viewtopic.php?t=14358
--Kale

Image
Seymour Clufley
Addict
Addict
Posts: 1264
Joined: Wed Feb 28, 2007 9:13 am
Location: London

Post 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.
Post Reply