Code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#ifndef O_BINARY
#define O_BINARY 0
#endif
#define DOS_SIGNATURE 23117
#define EXEPACK_SIGNATURE 16978
struct memstream {
unsigned char *buf;
unsigned int length;
unsigned int pos;
};
struct dos_header {
unsigned short e_magic;
unsigned short e_cblp;
unsigned short e_cp;
unsigned short e_crlc;
unsigned short e_cparhdr;
unsigned short e_minalloc;
unsigned short e_maxalloc;
unsigned short e_ss;
unsigned short e_sp;
unsigned short e_csum;
unsigned short e_ip;
unsigned short e_cs;
unsigned short e_lfarlc;
unsigned short e_ovno;
};
struct exepack_header {
unsigned short real_ip;
unsigned short real_cs;
unsigned short mem_start;
unsigned short exepack_size;
unsigned short real_sp;
unsigned short real_ss;
unsigned short dest_len;
unsigned short skip_len;
unsigned short signature;
};
void reverse(unsigned char *s, size_t length);
void unpack_data(unsigned char *unpacked_data, unsigned char *buf, unsigned int *unpacked_data_size, unsigned int packed_data_len);
void unpack(struct memstream *ms);
unsigned char *create_reloc_table(struct memstream *ms, struct dos_header *dh, struct exepack_header *eh, unsigned int *reloc_table_size);
void writeexe(struct dos_header *dh, unsigned char *unpacked_data, unsigned int unpacked_data_size, unsigned char *reloc, size_t reloc_size, size_t padding);
void craftexec(struct dos_header *dh, struct exepack_header *eh, unsigned char *unpacked_data, unsigned int unpacked_data_size, unsigned char *reloc, unsigned int reloc_size);
// utils
void msopen(const char *filename, struct memstream *ms);
unsigned int msread(struct memstream *ms, void *buf, unsigned int count);
int mscanread(struct memstream *ms, unsigned int count);
unsigned int msgetavailable(struct memstream *ms);
void msseek(struct memstream *ms, unsigned int offset);
void msclose(struct memstream *ms);
void *memmem(const void *l, size_t l_len, const void *s, size_t s_len);
char *output_file = "D:\\Other\\Unpacked.exe";
int main(int argc, char *argv[])
{
struct memstream ms;
msopen(argv[1], &ms);
unpack(&ms);
msclose(&ms);
return 0;
}
void reverse(unsigned char *s, size_t length)
{
size_t i = 0;
size_t j = length - 1;
unsigned char c;
if (length == 0) return;
for (i; i < j; i++) {
c = s[i];
s[i] = s[j];
s[j] = c;
j = j - 1;
}
}
/* buf is already reversed, because EXEPACK use backward processing */
void unpack_data(unsigned char *unpacked_data, unsigned char *buf, unsigned int *unpacked_data_size, unsigned int packed_data_len)
{
unsigned char opcode;
unsigned short count;
unsigned char fillbyte;
unsigned char *save_buf = NULL;
unsigned char *save_unp = NULL;
unsigned int cur_unpacked_data_size = 0;
save_buf = buf;
save_unp = unpacked_data;
while (*buf == 255) {
buf = buf + 1;
}
while (1) {
opcode = *buf;
buf = buf + 1;
count = *(buf) * 256 + *(buf + 1);
buf += 2;
if ((opcode & 254) == 176) {
fillbyte = *buf;
buf = buf + 1;
if ((cur_unpacked_data_size + count) > *unpacked_data_size) printf("overflow\n");
memset(unpacked_data, fillbyte, count);
unpacked_data += count;
cur_unpacked_data_size += count;
}
else if ((opcode & 254) == 178) {
if ((cur_unpacked_data_size + count) > *unpacked_data_size) printf("overflow\n");
memcpy(unpacked_data, buf, count);
unpacked_data += count;
cur_unpacked_data_size += count;
buf += count;
}
else {
printf("unknown opcode\n");
}
if ((opcode & 1) == 1) break;
if (buf - save_buf >= packed_data_len) break;
}
if (buf - save_buf < packed_data_len) {
if ((packed_data_len - (buf - save_buf)) > (*unpacked_data_size - (unpacked_data - save_unp))) printf("Data left are too large!\n");
memcpy(unpacked_data, buf, packed_data_len - (buf - save_buf));
cur_unpacked_data_size += packed_data_len - (buf - save_buf);
}
*unpacked_data_size = cur_unpacked_data_size;
}
unsigned char *create_reloc_table(struct memstream *ms, struct dos_header *dh, struct exepack_header *eh, unsigned int *reloc_table_size)
{
unsigned int exepack_offset = 0;
unsigned int reloc_length;
int nb_reloc;
unsigned char *buf_reloc = NULL;
unsigned char *reloc = NULL;
int i;
int j;
unsigned short count = 0;
unsigned short entry;
unsigned int reloc_position = 0;
exepack_offset = (dh->e_cparhdr + dh->e_cs) * 16;
msseek(ms, exepack_offset);
reloc = memmem(ms->buf + exepack_offset, msgetavailable(ms), "Packed file is corrupt", strlen("Packed file is corrupt"));
if (!reloc) printf("Cannot find string \"Packed file is corrupt\", is it really EXEPACK ?\n");
reloc_length = (unsigned int)(eh->exepack_size - ((reloc - (ms->buf + exepack_offset)) & 0xFFFFFFFF) + strlen("Packed file is corrupt"));
nb_reloc = (reloc_length - 16 * sizeof (unsigned short)) / 2;
*reloc_table_size = nb_reloc * 2 * sizeof(unsigned short);
buf_reloc = malloc(sizeof (char) * *reloc_table_size);
if (buf_reloc == NULL) printf("malloc\n");
reloc += strlen("Packed file is corrupt");
msseek(ms, (reloc - ms->buf) & 0xFFFFFFFF);
for (i = 0; i < 16; i++) {
if (msread(ms, &count, sizeof (unsigned short)) != sizeof (unsigned short)) printf("msread failed\n");
for (j = 0; j < count; j++) {
if (msread(ms, &entry, sizeof (unsigned short)) != sizeof (unsigned short)) printf("msread failed\n");
if (reloc_position >= *reloc_table_size) printf("overflow\n");
*(unsigned short*)(buf_reloc + reloc_position) = entry;
reloc_position += 2;
if (reloc_position >= *reloc_table_size) printf("overflow\n");
*(unsigned short*)(buf_reloc + reloc_position) = (i * 4096) & 65535;
reloc_position += 2;
}
}
*reloc_table_size = reloc_position;
return buf_reloc;
}
void writeexe(struct dos_header *dh, unsigned char *unpacked_data, unsigned int unpacked_data_size, unsigned char *reloc, size_t reloc_size, size_t padding)
{
int fd;
int i;
fd = open(output_file, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644);
if (fd == -1) printf("open\n");
write(fd, dh, sizeof (struct dos_header));
write(fd, reloc, reloc_size);
for (i = 0; i < padding; i++) {
write(fd, "\x00", 1);
}
write(fd, unpacked_data, unpacked_data_size);
close(fd);
}
void craftexec(struct dos_header *dh, struct exepack_header *eh, unsigned char *unpacked_data, unsigned int unpacked_data_size, unsigned char *reloc, unsigned int reloc_size)
{
struct dos_header dhead;
int header_size;
int total_length;
int padding_length;
memset(&dhead, 0, sizeof (struct dos_header));
header_size = sizeof (struct dos_header) + reloc_size;
dhead.e_magic = DOS_SIGNATURE;
dhead.e_cparhdr = (header_size / 16) & 65535;
dhead.e_cparhdr = (dhead.e_cparhdr / 32 + 1) * 32;
padding_length = dhead.e_cparhdr * 16 - header_size;
total_length = header_size + padding_length + unpacked_data_size;
dhead.e_ss = eh->real_ss;
dhead.e_sp = eh->real_sp;
dhead.e_ip = eh->real_ip;
dhead.e_cs = eh->real_cs;
dhead.e_minalloc = dh->e_minalloc;
dhead.e_maxalloc = 65535;
dhead.e_lfarlc = sizeof (struct dos_header);
dhead.e_crlc = (reloc_size / (2 * sizeof (unsigned short))) & 65535;
dhead.e_cblp = total_length % 512;
dhead.e_cp = (total_length / 512 + 1) & 65535;
writeexe(&dhead, unpacked_data, unpacked_data_size, reloc, reloc_size, padding_length);
}
void unpack(struct memstream *ms)
{
struct dos_header dh;
struct exepack_header eh;
unsigned int exepack_offset = 0;
unsigned char *unpacked_data = NULL;
unsigned int unpacked_data_size = 0;
unsigned int packed_data_start;
unsigned int packed_data_end;
unsigned int packed_data_len;
unsigned int reloc_size;
unsigned char *reloc = NULL;
if (msread(ms, &dh, sizeof (struct dos_header)) != sizeof (struct dos_header)) return;
exepack_offset = (dh.e_cparhdr + dh.e_cs) * 16;
msseek(ms, exepack_offset);
if (msread(ms, &eh, sizeof (struct exepack_header)) != sizeof (struct exepack_header)) return;
if ((eh.signature != EXEPACK_SIGNATURE && eh.skip_len != EXEPACK_SIGNATURE) || eh.exepack_size == 0) {
printf("This is not a valid EXEPACK executable\n");
return;
}
printf("Header exepack = %X\n", exepack_offset);
unpacked_data_size = eh.dest_len * 16;
unpacked_data = malloc(sizeof (char) * unpacked_data_size);
if (unpacked_data == NULL) printf("malloc\n");
memset(unpacked_data, 0, sizeof (char) * unpacked_data_size);
packed_data_start = dh.e_cparhdr * 16;
packed_data_end = exepack_offset;
packed_data_len = packed_data_end - packed_data_start;
msseek(ms, packed_data_start);
if (mscanread(ms, packed_data_len) == 0) {
free(unpacked_data);
return;
}
reverse(ms->buf + packed_data_start, packed_data_len);
unpack_data(unpacked_data, ms->buf + packed_data_start, &unpacked_data_size, packed_data_len);
reverse(unpacked_data, unpacked_data_size);
reloc = create_reloc_table(ms, &dh, &eh, &reloc_size);
craftexec(&dh, &eh, unpacked_data, unpacked_data_size, reloc, reloc_size);
free(unpacked_data);
}
void *memmem(const void *l, size_t l_len, const void *s, size_t s_len)
{
register char *cur;
register char *last;
const char *cl = (const char *)l;
const char *cs = (const char *)s;
if (l_len == 0 || s_len == 0) return NULL;
if (l_len < s_len) return NULL;
if (s_len == 1) return (void*)memchr(l, (int)*cs, l_len);
last = (char *)cl + l_len - s_len;
for (cur = (char *)cl; cur <= last; cur++) {
if (cur[0] == cs[0] && memcmp(cur, cs, s_len) == 0) return cur;
}
return NULL;
}
void msopen(const char *filename, struct memstream *ms)
{
int fd;
struct stat st;
if (ms == NULL) exit(EXIT_FAILURE);
fd = open(filename, O_RDONLY | O_BINARY);
if (fd == -1) printf("open\n");
if (fstat(fd, &st) == -1) {
close(fd);
printf("fstat\n");
}
ms->buf = (unsigned char*)malloc(sizeof (char) * st.st_size);
if (ms->buf == NULL) {
close(fd);
printf("malloc()\n");
}
if (read(fd, ms->buf, st.st_size) != st.st_size) {
close(fd);
free(ms->buf);
printf("read()\n");
}
ms->pos = 0;
ms->length = st.st_size;
close(fd);
}
unsigned int msread(struct memstream *ms, void *buf, unsigned int count)
{
unsigned int length;
if (buf == NULL) return 0;
if (ms->pos > ms->length) printf("invalid read\n");
if (count < (ms->length - ms->pos)) length = count; else length = ms->length - ms->pos;
if (length > 0) memcpy(buf, ms->buf + ms->pos, length);
ms->pos += length;
return length;
}
int mscanread(struct memstream *ms, unsigned int count)
{
if (ms->pos > ms->length) return 0;
if (count > (ms->length - ms->pos)) return 0;
return 1;
}
unsigned int msgetavailable(struct memstream *ms)
{
if (ms->pos > ms->length) return 0;
return ms->length - ms->pos;
}
void msseek(struct memstream *ms, unsigned int offset)
{
if (offset > ms->length) printf("invalid seek : 0x%X\n", offset);
ms->pos = offset;
}
void msclose(struct memstream *ms)
{
if (ms != NULL) {
if (ms->buf != NULL) {
free(ms->buf);
ms->buf = NULL;
}
}
}
Note: the code is also somewhat more BASIC like now, and most safety checks will need to be reimplemented if serious use is intended.