Link to home
Start Free TrialLog in
Avatar of Lab_Rat
Lab_RatFlag for United Kingdom of Great Britain and Northern Ireland

asked on

Self modifying C code.

The technique used to be big to prevent assembly programs being hacked.
Can/Has this been done for C?
Just a link or two :)
Avatar of siabod
siabod

Avatar of Lab_Rat

ASKER

Give it a bit longer, and if noone else surfs by, post an answer.
I have only ever done this in assembler. The ICL 1301 machine had no modifier so that indexing an array consisted of fishing out the accessor instruction, adding the array offset to the address part, sticking it back into the code and falling into it to get the damm thing executed. And that of couse in 64 words of page memory because we then have to fetch the next code page from drum otherwise we wrap round and start fromn the beginning again. All good clean fun and very warm in winter with this 80K Watt machine.
   An interesting anecdote which I might be permitted to add here is that the Programming Manual for the built-in Line Printer explicitly warns you about print sequences which could make the printer burst into flames. As the paper feed drum went round you had to raise and lower a pawl on a ratchet. The timing was critical to get a certain amount of paper through. One line feed or a vertical tab or whatever.  If you raised and lowered the pawl too fast the electromagnet would get hot which in turn could alight the paper and eventually the printer would catch fire.
   We ought to start a thread in the lounge for this machine. It was a marvel of complexity and intricacy only for those who were, for example, undaunted at the task of doing the Times Crossword during the tea break (as a relaxing diversion).
In Win32 this has become harder. You are not allowed to write to your own code segment always..
You use to have programs like lzexe who compressed executables as executables, so they use what you said.
The problem with this code is the jump tables and scheduling.
I once read how to write to a code segment in the book Undocumented Windows.  There's an undocumented system call PrestoChangeSelector that converts a read-only code segment to a writeable data segment and back again by toggling a bit in the selector.

I'm afriad I don't have the book with me at work right now, it's at home, so I can't provide the details until tomorrow.
/*
This is something I got out of a book on C Techniques.  If I remember, I'll try to check the full name of it when I get home.  2 files -> modify.h and modify.c
Hope this helps.
*/

/*MODIFY.H - Header file for MODIFY.C.*/


struct exeheader
  {
  unsigned id;            /* EXE file id. */
  unsigned bytemod;      /* Load module image size mod 512. */
  unsigned pages;            /* File size (including header) div 512. */
  unsigned reloc_items;      /* Number of relocation table items. */
  unsigned size;            /* Header size in 16-byte paragraphs. */
  unsigned minparagraphs;  /* Min number of paragraphs above program. */
  unsigned maxparagraphs;  /* Max number of paragraphs above program. */
  unsigned stackseg;      /* Displacement of stack segment. */
  unsigned spreg;            /* Initial SP register value. */
  int checksum;            /* Negative checksum (not used). */
  unsigned ipreg;            /* Initial IP register value. */
  unsigned codeseg;      /* Displacement of code segment. */
  unsigned first_reloc;      /* First relocation item. */
  unsigned ovln;            /* Overlay number. */
  };

#define EXEH_SIZE  sizeof(struct exeheader)

/* EXE file signature. */
#define EXE_ID  0x5A4D


#if !defined(TRUE)
#define FALSE  0
#define TRUE   (!FALSE)
#endif


/* Function prototypes. */
int exemodify(const char *progname, int keepdt, ...);



/*****  END .H FILE *****/

/*
MODIFY.C - Modify global variables in EXE or COM file.
For Turbo C versions 2.0 and above.  This code is public domain.
*/

#include <dir.h>
#include <dos.h>
#include <errno.h>
#include <fcntl.h>
#include <io.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#include <modify.h>

/*
int exemodify(char *progname, int keepdt, [void *data1,
     unsigned n1, [void *data2, unsigned n2, [ ... ]]]
                        NULL);
*/

int exemodify(const char *progname, int keepdt, ...)
{
char _progname[MAXPATH];      /* Program name. */
char ext[MAXEXT];                  /* Program extension. */
struct exeheader exe;            /* EXE file header. */
char *pathptr;                  /* Pointer to program path. */
int progfile;                  /* Program file handle. */
int iscomfile;                  /* TRUE if program is a COM file. */
long filelen;                  /* Program file length. */
struct ftime dtstamp;            /* Program date/time stamp. */
va_list datalist;            /* List of data items to be written to file. */
char *data;                  /* Pointer to current data item. */
unsigned n;                  /* Size of current data item. */
long seeklen;                  /* Seek length into program file. */
int retcode;                  /* Function return code. */

if (_osmajor < 3)
  /* Search DOS PATH for program. */
  if ((pathptr = searchpath(progname)) != NULL)
    strcpy(_progname, pathptr);
  else
    _progname[0] = 0;
else
  /* Program name is 0th parameter in DOS versions 3.0 and up. */
  strcpy(_progname, _argv[0]);

/* Assume error. */
retcode = -1;

if (_progname[0])
  {
  if ((progfile = open(_progname, O_RDWR | O_BINARY)) != -1)
    {
    iscomfile = fnsplit(_progname, NULL, NULL, NULL, ext) & EXTENSION && !strnicmp(ext, ".COM", 4);

    if (!iscomfile && (read(progfile, &exe, EXEH_SIZE) != EXEH_SIZE || exe.id != EXE_ID))
      /* File is not a COM file and does not have a valid EXE file header. */
      errno = EINVFMT;
    else
      {
      /* Cannot write beyond end of file. */
      filelen = filelength(progfile);

      if (keepdt)
      /* Save date/time stamp. */
      getftime(progfile, &dtstamp);

      retcode = 0;
      va_start(datalist, keepdt);

      while ((data = va_arg(datalist, char *)) != NULL)
      {
      if (iscomfile)
        /* Seek length is data offset - PSP size. */
        seeklen = FP_OFF(data) - 256;
      else
        /* Seek length is (data segment - (PSP + PSP size) + EXE size) * 16 + data offset. */
        seeklen = ((unsigned long)(FP_SEG(data) - (_psp + 16) + exe.size) << 4) + (unsigned long)FP_OFF(data);

      n = va_arg(datalist, unsigned);

      if (seeklen + n <= filelen && lseek(progfile, seeklen, SEEK_SET) != -1L && write(progfile, data, n) == n)
        /* Seek and write was successful. */
        retcode++;
      }

      va_end(datalist);

      if (keepdt)
      /* Restore date/time stamp. */
      setftime(progfile, &dtstamp);
      }

    close(progfile);
    }
  }
else
  /* File not found. */
  errno = ENOENT;

return (retcode);
}


Avatar of jkr
Mirkwood:

>>In Win32 this has become harder. You are not allowed to
>>write to your own code segment always..

This will do it ;-)

VirtualProtect( ..., PAGE_EXECUTE_READWRITE, ...);

Doing something is harder than doing nothing...
You have to agree with me. It's a lot harder to make
LZEXE for NT than it is to make LZEXE for Dos.
ASKER CERTIFIED SOLUTION
Avatar of jhurst
jhurst

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
What, no more anecdotes? I was looking forward to old hands dithering on about what it was like in the old days when you HAD to do this sort of thing.
Self-modifying code is a pain in the arse.  I had to do it on the Commodore 64 a few times...there are only three 8-bit registers for you to use, so sometime the only way to get things done fast was to modify operands rather than use the turtle-on-ritalin indexed indirect mode.
Avatar of Lab_Rat

ASKER

BigRat, I'm too younf to be using anecdotes, give me a few more years and I'll be waffling on about the Sinclair ZX Spectrum 128k+2a (Revision 2), now THERE was a computer that you could spill coke on and it.........
Self modifying code is easy.  Just use LISP! :)
(dodges C-coder launched rotten fruits)
Avatar of Lab_Rat

ASKER

Thanks everyone,
but it's time to lock up and go home.