holandes777 has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks:  
I am in need of conceptual help in blending C and perl.
 
I am trying to protect a perl program that is complied with perl2EXE using a key device. Here is the approach I am taking:
 
A C program is launched which interrogates a key.
If the key is valid the C program reads in an encrypted file.
The file is decrypted in memory.
The decrypted file is a perl program (xx.pl) which goes into a string variable (xxpldecrypted).
The program has some 3000 lines.
The C program then launches the xx.pl via system(perl -e 'eval{xxpldecrypted}' ) perl -e gets snagged by the internal single quotes (since that is what surronds de eval's code). I cant just system("perl kk.pl parm1 parm2") becasue that will show up in the process stack, defeating the key would be easy.
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <hasp_hl.h> #include <hasp_vcode.h> int kk () { hasp_status_t status; hasp_handle_t handle; int i; char filename[] = "prg"; printf("start : "); status = hasp_login(HASP_PROGNUM_DEFAULT_FID, (hasp_vendor_code_t *)vendor_code, &handle); switch (status) { case HASP_STATUS_OK: printf("OK\n"); ################################################### # this is where the read, decrypt and eval would go ################################################### hasp_logout(handle); return(1); break; default: printf("BAD\n"); return(0); } } int main(void) { int result; result = kk(); printf("result was %d\n",result); }
The other approach I am using is Inline::C where I use the kk function as a function within perl. The idea is the function would stop the program from working if the key was invalid. Unfortunately, there seem to be some symbols that can't be resolved.
 
Any advice is appreciated.
 
Thanks!
Gary

Replies are listed 'Best First'.
Re: Blending perl and C (two approaches)
by dave_the_m (Monsignor) on Oct 28, 2007 at 15:48 UTC
    As far as I can see, any scheme to hide the decrypted perl source is doomed to failure; for example:
    # mv /usr/bin/perl /perl/bin/perl- # vi /usr/bin/perl ... # cat /usr/bin/perl echo "ARGS=$@" > /tmp/out # chmod 755 /usr/bin/perl
    Update as appropriate depending on how the source gets passed to the perl binary.

    Dave.

Re: Blending perl and C (two approaches)
by graff (Chancellor) on Oct 28, 2007 at 15:51 UTC
    I think a more fundamental question here is: what benefit do you think is being created by keeping the perl source code hidden? This is obviously not a benefit for the end user -- the only conceivable benefit would be to satisfy some perception you have (or your employer has) about the importance of retaining exclusive ownership of the code and not allowing anyone else to view it.

    So this raises two additional questions:

    • Are you really confident that the benefits of exclusive ownership outweigh the competing advantages of feedback from peer review and informed users that could improve your code?

    • Might there be some other method to gain the perceived benefits of exclusive ownership, without having to conceal the source code?

    If exclusive protection of source code is unavoidably essential to you, maybe you should just re-implement the perl script in a compiled language.

    Apart from that, your idea of having a C program do system( "perl -e 'eval{hugestring}'" ) might work if your perl source code is tweaked with this idea in mind: don't use single-quotes anywhere in the perl source code. (Perl gives you plenty of ways to avoid using single-quotes.)

    UPDATE: Having just seen this reply from dave_the_m, I should say that this approach "works" in the sense that it will probably run -- not that it will actually protect your source code from exposure. (/update)

    Changing the perl source code just to support that sort of application is admittedly a kluge, and might raise maintenance issues later on ("oops! when that new function was added, we forgot about avoiding apostrophes..."), but if you are stuck in the mindset of hiding the source code, some folks would tend to expect that there are bound to be kluges, because there's no way to prove otherwise.

    If the "protection" of source code is a matter of trying to preserve your "market share", you should understand that eventually you are bound lose market share to anyone who finds a way to address the same task but doesn't care about hiding their source code.

    If the source code protection is a matter of preserving particular data from unauthorized viewers, that's a different matter: just separate the data from the source code, and encrypt the data, not the source code.

      I hear you loud and clear!
       
      Actually the data can be made completely public. It is the method whereby it is gathered and organized that requires protection. Essentially we have to create a barrier that shows the program was tampered with if the results are wrong. Think of the law: If your door is unlocked then the burglar will probably be in less trouble than if he has to break something to get in.
       
      The protection need not be perfect becasue the potential hacker base is small and NTTB (Not Too Terribly Bright)
       
      In any event, to all of you, the comments are really valuable. I am impressed with the genuine interest each of your responses show. I am proud to belong to this organization.
Re: Blending perl and C (two approaches)
by mwah (Hermit) on Oct 28, 2007 at 16:51 UTC
    holandes777
    Any advice is appreciated

    OK, then I'd say: drop both approaches and embed the Perl interpreter in the C program (means: link the perl58.lib to the executable and initiate an internal perl instance after going trough main).

    You could then read a totally garbled data file from there and decode it to an runable perl source internally and invoke it through the "internal" Perl instance.

    I did this in Unix and Win32 environments and it works good and is perfectly portable (did it without the garbling/decoding step ;-).

    see: perlembed

    Regards

    mwa

      This approach sems promising so I went through the steps and .. ran into a briick wall, HARD (ouch!) Here are the notes as I progressed:  
      How to Embed Perl in C
       
      From perlembed's information:  
      every C program that uses Perl must link in the perl library you can't use Perl from your C program unless Perl has been compiled on your machine, or installed properly--that's why you shouldn't blithely copy Perl executables from machine to machine without also copying the lib directory.)  
      perl -V output:
      1) find / -name perl.h - take note of the subdir, the perl library (and EXTERN.h and perl.h, which you'll also need) will reside in that directory
      2) where? (confirm with perl -MConfig -e 'print $Config{archlib}')
      3) what compiler? perl -MConfig -e 'print $Config{cc}' - usually gcc
      4) what to add to the gcc compiler command, use perl -V and look at something like this:
      cc='gcc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DDEBUGGING -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm',
      5) extra libraries are shown by perl -MConfig -e 'print $Config{libs}'
      -lresolv -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lpthread -lc[
       
       
      Summary of my perl5 (revision 5 version 8 subversion 6) configuration:
      Platform:
      osname=linux, osvers=2.4.21-27.0.2.elsmp, archname=i386-linux-thread-multi
      uname='linux decompose.build.redhat.com 2.4.21-27.0.2.elsmp #1 smp wed jan 12 23:35:44 est 2005 i686 i686 i386 gnulinux '
      config_args='-des -Doptimize=-O2 -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -m32 -march=i386 -mtune=pentium4 -fasynchronous-unwind-tables -Dversion=5.8.6 -Dmyhostname=localhost -Dperladmin=root@localhost -Dcc=gcc -Dcf_by=Red Hat, Inc. -Dinstallprefix=/usr -Dprefix=/usr -Darchname=i386-linux -Dvendorprefix=/usr -Dsiteprefix=/usr -Duseshrplib -Dusethreads -Duseithreads -Duselargefiles -Dd_dosuid -Dd_semctl_semun -Di_db -Ui_ndbm -Di_gdbm -Di_shadow -Di_syslog -Dman3ext=3pm -Duseperlio -Dinstallusrbinperl=n -Ubincompat5005 -Uversiononly -Dpager=/usr/bin/less -isr -Dd_gethostent_r_proto -Ud_endhostent_r_proto -Ud_endprotoent_r_proto -Ud_endservent_r_proto -Ud_sethostent_r_proto -Ud_setprotoent_r_proto -Ud_setservent_r_proto -Dinc_version_list=5.8.5 5.8.4 5.8.3'
      hint=recommended, useposix=true, d_sigaction=define
      usethreads=define use5005threads=undef useithreads=define usemultiplicity=define
      useperlio=define d_sfio=undef uselargefiles=define usesocks=undef
      use64bitint=undef use64bitall=undef uselongdouble=undef
      usemymalloc=n, bincompat5005=undef Compiler: cc='gcc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DDEBUGGING -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm',
      optimize='-O2 -g -pipe -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -m32 -march=i386 -mtune=pentium4 -fasynchronous-unwind-tables',
      cppflags='-D_REENTRANT -D_GNU_SOURCE -DDEBUGGING -fno-strict-aliasing -pipe -I/usr/local/include -I/usr/include/gdbm'
      ccversion='', gccversion='4.0.0 20050516 (Red Hat 4.0.0-6)', gccosandvers=''
      intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234
      d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12
      ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
      alignbytes=4, prototype=define
      Linker and Libraries:
      ld='gcc', ldflags =' -L/usr/local/lib'
      libpth=/usr/local/lib /lib /usr/lib

      libs=-lresolv -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lpthread -lc

      perllibs=-lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread -lc
      libc=/lib/libc-2.3.5.so, so=so, useshrplib=true, libperl=libperl.so
      gnulibc_version='2.3.5'
      Dynamic Linking:
      dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E -Wl,-rpath,/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE'
      cccdlflags='-fPIC', lddlflags='-shared -L/usr/local/lib'
       
      Characteristics of this binary (from libperl):
      Compile-time options: DEBUGGING MULTIPLICITY USE_ITHREADS USE_LARGE_FILES PERL_IMPLICIT_CONTEXT
       
      the simpler approach appeared to be:
      gcc -o interp interp.c 'perl -MExtUtils::Embed -e ccopts -e ldopts'
      produces these errors
      gcc: perl -MExtUtils::Embed -e ccopts -e ldopts: No such file or directory
      interp.c:1:20: error: EXTERN.h: No such file or directory
      interp.c:2:18: error: perl.h: No such file or directory
      interp.c:4: error: syntax error before â*â token
      interp.c:4: warning: data definition has no type or storage class
      interp.c: In function âmainâ:
      interp.c:8: warning: assignment makes pointer from integer without a cast
      interp.c:10: error: âNULLâ undeclared (first use in this function)
      interp.c:10: error: (Each undeclared identifier is reported only once
      interp.c:10: error: for each function it appears in.)
      ------------------------------------------------------------------
      so I tried another approach (the file below should be read from the last line up and shows what went wrong:
      Here are the errors:
       
      /tmp/ccLefb5c.o(.text+0x12): In function `main':
      interp.c: undefined reference to `perl_alloc'
      /tmp/ccLefb5c.o(.text+0x20):interp.c: undefined reference to `perl_construct'
      /tmp/ccLefb5c.o(.text+0x36):interp.c: undefined reference to `perl_parse'
      /tmp/ccLefb5c.o(.text+0x44):interp.c: undefined reference to `perl_run'
      /tmp/ccLefb5c.o(.text+0x50):interp.c: undefined reference to `perl_destruct'
      /tmp/ccLefb5c.o(.text+0x66):interp.c: undefined reference to `perl_free'
      collect2: ld returned 1 exit status
       
      Here are the executions of the compiler (from bottom to top:
      gcc -O2 -D_REENTRANT -D_GNU_SOURCE -DDEBUGGING -fno-strict-aliasing -pipe -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm -Dbool=char -DHAS_BOOL -I/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -L/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -o interp interp.c -lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread
      # still complains about not finding perl_alloc gcc -O2 -D_REENTRANT -D_GNU_SOURCE -DDEBUGGING -fno-strict-aliasing -pipe -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm -Dbool=char -DHAS_BOOL -I/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -L/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -o interp interp.c -lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread
      # above, adding the ccflags
      # complains about not finding perl_alloc (again) gcc -O2 -Dbool=char -DHAS_BOOL -I/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -L/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -o interp interp.c -lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread
      # complains about -lc[ gcc -O2 -Dbool=char -DHAS_BOOL -I/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -L/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -o interp interp.c -lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread -lc[
      # complains about -ldb gcc -O2 -Dbool=char -DHAS_BOOL -I/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -L/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -o interp interp.c -lresolv -lnsl -ldb -ldl -lm -lcrypt -lutil -lpthread -lc[
      # complais it cant find lgdbm gcc -O2 -Dbool=char -DHAS_BOOL -I/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -L/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -o interp interp.c -lresolv -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lpthread -lc[
      # complains about perl_alloc gcc -O2 -Dbool=char -DHAS_BOOL -I/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -L/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -o interp interp.c -lm
      # complains about -lperl gcc -O2 -Dbool=char -DHAS_BOOL -I/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -L/usr/local/include -I/usr/lib/perl5/5.8.6/i386-linux-thread-multi/CORE -o interp interp.c -lperl -lm

        The perl-relevant part of the Makefile under Linux or *BSD would look like:

        ... VPATH = $(MYSOURCES) : $(MYTOOLS) ... #Linux + Freebsd: use the magic configurator "ExtUtils::Embed" PERLCC=`perl -MExtUtils::Embed -e perl_inc` PERLLD=`perl -MExtUtils::Embed -e ldopts -- -std` PLSTATIC= # -static # if static libperl.a, set this to -static, otherwise to (none) CXXFLAGS=-O3 COMPILE.cxx=$(CXX) $(CFLAGS) $(CXXFLAGS) $(PERLCC) -I$(MYHEADERS) -c .cxx.o: $(COMPILE.cxx) $< -o $(@F) $(EXE): $(F_MYSOURCE) $(F_MYTOOLS) Makefile $(CXX) -o $@ $(F_MYSOURCE) $(F_MYTOOLS) $(PERLLD) $(PLSTATIC) -lst +dc++ -s ...

        The 'MYHEADERS', 'F_MYSOURCE', 'F_MYTOOLS' etc. designate the apps source files and directories (and are set elsewhere), the Perl-stuff goes into 'PERLCC' (headers) and 'PERLLD' (libs).

        This will magically construct a perl.lib linked app ;-).

        Regards

        mwa

      Better yet, perhaps you could store the byte-code, and pass *that* through an embedded interpreter? (Heck, while you're at it, encrypt the byte-code as well!)

      This may help in preventing some clever hacker from attaching to the process via a debugger and capturing the program 's contents as it's being passed for the eval.

        Perl does not have bytecode. The non-existent bytecode can't be stored and then retrieved later. If it existed, this bytecode could be deparsed back to perl code.

        ⠤⠤ ⠙⠊⠕⠞⠁⠇⠑⠧⠊

Re: Blending perl and C (two approaches)
by BrowserUk (Patriarch) on Oct 28, 2007 at 19:56 UTC

    You could try opening a pipe to the perl executable and feeding it the decrypted program line by line. Once you close the pipe, perl will compile and run the code you feed it and it will never have existed as a file anywhere. This trivial example works for me on Win32, and only uses POSIX calls:

    #include <stdio.h> #include <stdlib.h> char* prog = "\ #! perl\n\ use strict;\n\ $\\ = qq[\\n]; \n\ $|=1;\n\ print qq[hello world];\n\ print for 1 .. 10;\n\ "; char* cmd = "/perl/bin/perl.exe"; int main( void ) { FILE* pipe; if( !( pipe = _popen( cmd, "wt" ) ) ) { fprintf( stderr, "Failed to create pipe\n" ); exit( -1 ); } fprintf( pipe, prog ); close( pipe ); flushall(); return 0; }

    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.
      Surely this would be vulnerable to a man in the middle attack? Just replace system Perl with a recording proxy socket...
        recording proxy socket... sounds super sexy!
          What is it? I googled "recording proxy socket..." and your comment came up as the top item! I thought you'd like to know that.
          I'll start looking around for information on this, but if you can send some hints this way they would be apreciated. (Thx)
Re: Blending perl and C (two approaches)
by syphilis (Archbishop) on Oct 28, 2007 at 18:17 UTC
    Unfortunately, there seem to be some symbols that can't be resolved.

    It should just be a matter of linking to the library that resolves the symbols:
    use Inline C => Config => LIBS => '-L/your/lib/path -lyourlib';
    For additional info, see perldoc Inline::C-Cookbook and look for LIBS

    Cheers,
    Rob
Re: Blending perl and C (two approaches)
by dsheroh (Monsignor) on Oct 28, 2007 at 14:28 UTC
    I cant just system("perl kk.pl parm1 parm2") becasue that will show up in the process stack, defeating the key would be easy.

    $0 goes both ways...

    #!/usr/bin/perl use strict; use warnings; $0 = 'The phantom strikes again - so ha!'; sleep 60;
    $ ./phantom.pl $ ps x ... 10732 pts/5 S+ 0:00 The phantom strikes again - so ha!
      If the point of the OP's exercise is to avoid exposure of the perl source code, then storing the decrypted program as a file anywhere in the user's disk space would defeat the purpose.

      Having the script change its "name" in the process table (by setting $0) would not be sufficient protection. Anyone savvy enough to figure out that a perl script is being run from a disk file can search the disk for recently created files and potentially find the perl source code. So the point is to avoid saving the program to a disk file.

      UPDATE: Having said that, of course, there is a work-around that might supply some level of protection:

      • The C program decrypts the perl script, stores the clear-text source code as a file, and issues a system call to execute the script.

      • The perl script begins with  unlink $0; -- at the point where this step is executed, the script is already in memory, so the disk file is no longer needed. The exposure as a disk file lasts for just a fraction of a second.

      Even with that, though, the sort of trick demonstrated by dave_the_m below can be tweaked to reveal the source code -- e.g. replace /usr/bin/perl with a shell script that launches the given perl script using the perl debugger. (I haven't tried it, but this or something similar is bound to be possible and not that hard to do.)

        Your and dave_the_m's responses to the effect of "trying to hide the source is futile and probably wouldn't have good results even if you succeed" are absolutely correct, as the movie and music industries have spent many millions (if not billions) of dollars proving. I was just correcting the OP's claim that he can't exec the decrypted code because then it would show up in the process table.

        As to the possibility you mentioned of looking for recently-created files, that could be defeated by the Perl code immediately unlinking itself on startup, which in turn could be defeated by a user savvy enough to locate the appropriate inode and undelete the file, which could be defeated by the Perl overwriting itself before unlinking, so the user renames perl to perl-real and replaces it with a program which copies the source (whether supplied in a file or on the command line) into a new file before passing it on to perl-real, and on and on it goes, wasting a lot of both the developer's and the users' time until either the developer or all of the users give up. Given that developers are generally vastly outnumbered by their users, it's a pretty safe bet that, in the end, the users will win that battle of attrition.

        Unlink is only useful if you make sure the sectors the temporary file existed on in disk were also overwritten. Otherwise the user could just recover the deleted file.
Re: Blending perl and C (two approaches)
by dcd (Scribe) on Oct 29, 2007 at 20:17 UTC
    Just a quick question: Did you already look at perlfilter - Source Filters
    perldoc perlfilter
      no I have not and thank you for the suggestion.
        the protection need not be perfect, just too difficult for the average bear.