LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Programming (https://www.linuxquestions.org/questions/programming-9/)
-   -   Make a self extractor (https://www.linuxquestions.org/questions/programming-9/make-a-self-extractor-914292/)

Zssfssz 11-18-2011 03:14 PM

Make a self extractor
 
OK, I want o make a self extractor(C++)(CLI).
I know how to use binary fstream but dont know how to put a file in the EXE or how to use it once its in. Can anyone help?
MinGW Compiler

millgates 11-19-2011 08:15 AM

A simple, straightforward way of doing that would be storing the data in a variable:
Code:

unsigned char file[FILE_SIZE] = {0x01, 0x02, 0x03, ...};
For large files, that would not be very practical. You might write a simple program to generate a file containing the data in this form for you.

Alternatively, you may try a more sophisticated aproach.

You may also find this interesting.

Zssfssz 11-19-2011 03:41 PM

HOw would I write a program to do it for me? (it sounds awesome!)

millgates 11-19-2011 05:53 PM

Quote:

Originally Posted by Zssfssz (Post 4528504)
HOw would I write a program to do it for me? (it sounds awesome!)

well, you could probably use od and sed (I found this somewhere)
Code:

(echo "const unsigned char binary_data[] = {"; od -txC -v myfile.bin | sed -e "s/^[0-9]*//" -e s"/ \([0-9a-f][0-9a-f]\)/0x\1,/g" -e"\$d" | sed -e"\$s/,$/};/")
in C, on the other hand, it could look like this (yes, I just love doing everything in C/C++ while there are better tools to do it)

Code:

#include <stdio.h>

int main(int argc, char *argv[]) {
        FILE *fpin, *fpout;
        int counter = 0;
        int c;
        if (argc != 3) {
                fprintf(stderr, "usage: %s [INPUT FILE] [OUTPUT FILE]", argv[0]);
                return 1;
              }

        fpin = fopen(argv[1], "rb");
        if ( ! fpin ) {
                fprintf(stderr, "err: could not open file \"%s\" for reading", argv[1]);
                return 2;
        }

        fpout = fopen(argv[2], "w");
        if ( ! fpout ) {
                fprintf(stderr, "err: could not open file \"%s\" for writing", argv[2]);
                fclose(fpin);
                return 2;
        }

        fprintf(fpout, "const unsigned char data[] = {\n");
        while ( fread(&c, 1, 1, fpin) ) {
                if (counter == 10) { counter = 0; fprintf(fpout, ",\n"); }
                fprintf(fpout, counter++ == 0 ? "\t0x%02x" : ", 0x%02x", c);
        }
        fprintf(fpout, "\n};");
        fclose(fpin);
        fclose(fpout);
        return 0;
}

Could use some improvement, though, such as using a buffer to store the input.

There are probably as many ways of doing this as there are programming languages. You can also try exporting the file as C source from a hex editor or something.

Zssfssz 11-19-2011 06:05 PM

I tried that with a rather large file and the output was only 3 bytes (Supposed to be 501 kilos) (the text hexadecimal output thing was fine the actual binary file wasn't)
I know (took a half hour for the precompiler to get done) but any help?

SigTerm 11-19-2011 09:08 PM

Quote:

Originally Posted by Zssfssz (Post 4527828)
OK, I want o make a self extractor(C++)(CLI).
I know how to use binary fstream but dont know how to put a file in the EXE or how to use it once its in. Can anyone help?
MinGW Compiler

Append file at the end of exe. When program starts, let it read its own header in order to determine actual size of executable code, then open its own executable file, skip number of executable bytes, and read the data. Or store size of exe somewhere within program code as a constant, and patch it with correct value once program has been compiled.

Quote:

Originally Posted by millgates (Post 4528282)
For large files, that would not be very practical.

For a larger files some compilers may be unable to process the array.

Zssfssz 11-19-2011 09:25 PM

Um... Mr.SigTerm I AM NOT DENNIS RITCHIE!!!!! Could you please explain that more clearly? I have no idea how to do almost any of that!
How do I append the file!?!? How do I get the executable's size!?!? ECT!?!?

SigTerm 11-19-2011 09:51 PM

7zip is an opensource archiver and it has self-extractor module. You could try to see how it works - source code should be available.

Quote:

Originally Posted by Zssfssz (Post 4528655)
How do I append the file!?!?

You compile self-extractor that contains NO data to be extracted, then open (fopen("program.exe", "wba")) it with external program, seek to the end of file, and write data you want module to unpack into file using fwrite. For that windows platform has "copy /b" shell command and linux has "cat".

When resulting exe launches, it should skip to the beginning of data, and read from it. To do that you need to find file that corresponds to current module and read from it (GetModuleFilename on windows platform), or get a pointer to module as it is loaded into memory (in some versions of windows you could cast GetModuleHandle() result into pointer, but I'm not sure if this is even documented functionality).

The trick is to determine offset of data.
You can try to do that by:
  1. making self-extractor parse its own header (IMAGE_FILE_HEADER on msdn), calculate size of exe, and skip required number of bytes. This is not guaranteed to work, and might be complicated.
  2. (easiest) You could store size of program file within program in global constant variable (static const long programSize = 0;) and update this variable after program has been compiled, by opening file, locating required offset and writing new value into it. This is the easiest method. The trick is to locate the variable once program has been compiled. To do so, you could either plase a "marker" around it, or use disassembler to locate it. There might be a better way to do it. If your compiler produces exe of same size during every compilation, then you simply could compile program twice - first time to determine resulting program size, and next time - with correctly set constant value. The problem with this approach is that somebody tries to run "gnu strip" on your program, it most likely will break everything.

Zssfssz 11-20-2011 12:27 AM

I have looked at 7zip but I can't find the command line version of it and I just got to diologs In GUI programing. So how would I use the size of the program to determine what to extract? How would I make the program write this data? Is there a easy way to get my IDE (Code::Blocks) to do this for me (as in add file to executable)? Am I hopeless? I loved the first hexadecimal thing (could you give me an outline of what it is doing? I want to port it to C++) but the file I want to try it on is ~500 Kilobytes.

firstfire 11-20-2011 02:46 AM

Hi.

Suppose you want to incorporate string "123" (3 bytes length) to the executable file "a.out". For simplicity I assume that "a.out", when run, just prints to stdout its payload (that is the string 123).

It is not a problem to append "123" to a.out. For example, on linux:

Code:

echo -n "123" >> a.out
(`-n' suppress trailing newline)

Your program should first open its own binary, then seek to 3 bytes back from its end and print contents of the file to the end. To do that your program have to know that its payload is 3 bytes length. This may be specified at compile time.

Here is an example.
Code:

// file self-extract.c

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

#ifndef N
        #define N 0
#endif

int main(int argc, char* argv[])
{
        int fd = open(argv[0], O_RDONLY);

        if (fd == -1)
        {
                perror("Can't open file\n");
                return 1;
        }

        if (lseek(fd, -N, SEEK_END) == -1)
        {
                perror("Can't seek\n");
                return 1;
        }

        char buf;
        while ( read(fd, &buf, 1) )
        {
                putchar(buf);
        }
        return 0;
}

Usage is as follows:
Code:

$ gcc -D N=3  self-extract.c
$ echo -n "123" >> ./a.out
$ ./a.out
123$

$-sign in th last line is a shell prompt.

Of course your program can do more complicated things with payload than just printing it. The code may require some modification to compile on Windows.

Hope this helps.

PS. If you don't know how to append to the file, follow SigTerm's suggestion -- use `copy' utility or write your own.

Zssfssz 11-21-2011 03:17 PM

How would I seek throughthe file? Would this be easyer in Perl? I know perl but i thought scripted languiges had a weekness in storing binary files in them. (if the file was outside of that then it would be easy.)
I also know minute ammounts of python and ruby (weekest being ruby) but ruby wont start on my computer...
So, how do i seek through the file, and would this be easyer in Perl?

I love Perl......

firstfire 11-21-2011 11:14 PM

Hi.

The situation is different for compiled languages (like C) and for interpreted languages (like Perl).

1. In C you write your program in a text file, say `prog.c', then you compile it to binary file, on linux just `prog', and then run it: `./prog' on linux. Executable file is different from program source. In this situation your program (self-extractor) should open its own binary file and locate the payload inside. You need some kind of `seek' function for this. And in C you already have one, it is called `lseek' (in linux you can run `man 2 lseek' for details or see here and this example). In my previous post I use lseek to go to specified number of bytes back from the end of file.

2. There are no such thing as executable (binary) file for interpreted languages. So you don't need to load it and then find something there. Instead you just put the information you want straight to the script, for example using here document and do what you want with it. This approach is very similar to putting the data to C-array (see first post), but much simpler and standard for such languages. In fact, some time ago this approach was used to make self-extracting shell archives (shar) -- see here and here.
Example in perl:
Code:

print << 'END'
Your
data
goes
here
END

Note that here-documents are designed to work with textual data only so binary data will require some preprocessing (like encoding to base64 or to hex format).

Note also that you will need an interpreter (correctly installed perl executable in your case) in order to extract your data automatically.

Hope this helps.

Zssfssz 11-22-2011 12:03 AM

This pice of info might also chage some things:
The file is a non OS/2\Windows specific DLL. I wrote it and have the endure source (der).
Would it be possible to place the code inside of a variable or something and when it's comPiled it gets turned into binary data.
I acctualy have both strawberrie and ActiveState perl on my computer (the Linux side doesnt have one because I dont have Internet nor the time to to download all dependicys). Still deciding which one get in the regishtry and path... They both have ups n downs.

Thanks!


All times are GMT -5. The time now is 12:01 PM.