/* *****************************************************************************
* pp20.dll v1.0.9.4 - decrunch PowerPacker encrypted data files with password
* by Stuart Caie & Peace^Testaware - this software is in the Public Domain
* *****************************************************************************
*
* Website: http://www.testaware.de.tp
*
* Version history
* 0.4 18-Sep-2003 : by Stuart Caie
* - first basic release
*
* 1.0.9.1 12-Nov-2008 : by Peace^Testaware
* - source adapted to Dev-C++ 4.9.9.2
* - support for AMOS(Pro) PPbk format
* - added ppDecrunchFile()
* - added ppDecrunchMemory()
* - added ppGetDecrunchSize()
* - added ppGetCrunchMode()
* - removed ppLoadData()
* - changed PPERR_<CODE> to negative values
* - improved PPMODE_<CODE> constants
* - improved macro SwapLong(ptr)
* - included example source for PureBasic
*
* 1.0.9.2 13-Nov-2008 : by Peace^Testaware
* - ppDecrunch, ppDecrypt now subfunctions
*
* 1.0.9.3 16-Nov-2008 : by Peace^Testaware
* - support for PowerPacker v1.x format (very old)
*
* 1.0.9.4 21-Nov-2008 : by Peace^Testaware
* - added ppEncodeMemory
* ****************************************************************************/
#include "dll.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PPERR_OK (0) /* no error */
#define PPERR_ARGS (-1) /* bad arguments to function */
#define PPERR_OPEN (-2) /* error opening file */
#define PPERR_READ (-3) /* error reading from file */
#define PPERR_SEEK (-4) /* error seeking in file */
#define PPERR_NOMEMORY (-5) /* out of memory */
#define PPERR_DATAFORMAT (-6) /* error in data format */
#define PPERR_PASSWORD (-7) /* bad or missing password */
#define PPERR_DECRUNCH (-8) /* error decrunching data */
#define PPMODE_PP11 (0x50503131)
#define PPMODE_PP20 (0x50503230)
#define PPMODE_PPBK (0x5050626B) /* AMOS PPbk Bank */
#define PPMODE_PPLS (0x50504C53)
#define PPMODE_PX20 (0x50583230)
#define PP_READ_BITS(nbits, var) do { \
bit_cnt = (nbits); (var) = 0; \
while (bits_left < bit_cnt) { \
if (buf < src) return 0; /* out of source bits */ \
bit_buffer |= *--buf << bits_left; \
bits_left += 8; \
} \
bits_left -= bit_cnt; \
while (bit_cnt--) { \
(var) = ((var) << 1) | (bit_buffer & 1); \
bit_buffer >>= 1; \
} \
} while (0)
#define PP_BYTE_OUT(byte) do { \
if (out <= dest) return 0; /* output overflow */ \
*--out = (byte); written++; \
} while (0)
#define SwapLong(ptr) ((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3])
/* *****************************************************************************
* SIZE = ppGetDecrunchSize(*src, src_len)
* *****************************************************************************
* calculates the decrunched size of crunched buffer
* -----------------------------------------------------------------------------
* APTR *src = buffer of PowerPacker crunched datas
* DWORD src_len = size in bytes of *src
* -----------------------------------------------------------------------------
* SIZE = size of decrunched buffer, else #PPERR_<CODE>
* ****************************************************************************/
DLLIMPORT int ppGetDecrunchSize(unsigned char *src, unsigned int src_len)
{
int dest_len = PPERR_DATAFORMAT;
unsigned int mode;
if (!src || !src_len) return PPERR_ARGS;
mode = SwapLong(src);
if ((mode == PPMODE_PP11) || (mode == PPMODE_PP20) || (mode == PPMODE_PPBK) ||
(mode == PPMODE_PPLS) || (mode == PPMODE_PX20)) {
dest_len = (src[src_len-4] << 16) | (src[src_len-3] << 8) | src[src_len-2]; }
return dest_len;
}
/* *****************************************************************************
* MODE = ppGetCrunchMode(*src)
* *****************************************************************************
* returns the crunchmode of crunched buffer
* -----------------------------------------------------------------------------
* APTR *src = buffer of PowerPacker crunched datas
* -----------------------------------------------------------------------------
* MODE = #PPMODE_<CODE> detected format
* ****************************************************************************/
DLLIMPORT int ppGetCrunchMode(unsigned char *src)
{
int mode;
if (src == NULL) return PPERR_ARGS;
/* detect format, decrypt if necessary */
switch SwapLong(src) {
case PPMODE_PP11: mode = PPMODE_PP11; break; /* PP11 */
case PPMODE_PP20: mode = PPMODE_PP20; break; /* PP20 */
case PPMODE_PPBK: mode = PPMODE_PPBK; break; /* PPbk */
case PPMODE_PPLS: mode = PPMODE_PPLS; break; /* PPLS */
case PPMODE_PX20: mode = PPMODE_PX20; break; /* PX20 */
default:
mode = PPERR_DATAFORMAT;
}
return mode;
}
/* *****************************************************************************
* ERR = ppDecrunchMemory(*src, *dest, src_len, dest_len [,password])
* *****************************************************************************
* decrunch PowerPacker crunched buffer, also encrypt by given password
* -----------------------------------------------------------------------------
* APTR *src = buffer of PowerPacker crunched datas
* APTR *dest = buffer to store encrypted/decrunched datas
* DWORD src_len = size in bytes of *src
* DWORD dest_len = size in bytes of decrunched buffer (=ppGetDecrunchSize())
* CHAR password = Password (max. 16 chars only!) to encrypt datas in *src
* -----------------------------------------------------------------------------
* ERR = #PPERR_OK -> buffer decrunched
* ****************************************************************************/
DLLIMPORT int ppDecrunchMemory(unsigned char *src, unsigned char *dest,
unsigned int src_len, unsigned int dest_len, unsigned char *password)
{
int err = PPERR_OK, eff;
if (!src || !dest || !src_len || !dest_len) return PPERR_ARGS;
/* detect format, decrypt if necessary */
switch SwapLong(src) {
case PPMODE_PP11: eff = 4; break; /* PP11 */
case PPMODE_PP20: eff = 4; break; /* PP20 */
case PPMODE_PPBK: eff = 20; break; /* PPbk */
case PPMODE_PPLS: eff = 8; break; /* PPLS */
case PPMODE_PX20: eff = 6; /* PX20 */
if (!password || (ppCalcChecksum(password) != ((src[4]<<8)|src[5])))
err = PPERR_PASSWORD;
else
ppDecrypt(&src[10], src_len-14, ppCalcPasskey(password));
break;
default:
err = PPERR_DATAFORMAT;
}
if (err) { return err; }
if (!ppDecrunch(&src[eff], dest, src_len-(eff+8), dest_len)) {
err = PPERR_DECRUNCH; }
return err;
}
/* *****************************************************************************
* ERR = ppEncodeMemory(*src, *dest, src_len, dest_len, passkey [,*restore])
* *****************************************************************************
* encrypt and decrunch crunched buffer by given passkey
* -----------------------------------------------------------------------------
* APTR *src = buffer of PowerPacker encrypted PX20 datas
* APTR *dest = buffer to copy encrypted/decrunched datas
* DWORD src_len = size in bytes of *src
* DWORD dest_len = size in bytes of decrunched buffer (=ppGetDecrunchSize())
* ULONG passkey = 32 bit-key to encrypt datas in *src
* APTR *restore = optional: clone of *src to restore buffer if wrong passkey
* -----------------------------------------------------------------------------
* ERR = #PPERR_OK -> all done
* ****************************************************************************/
DLLIMPORT int ppEncodeMemory(unsigned char *src, unsigned char *dest,
unsigned int src_len, unsigned int dest_len,
unsigned int passkey, unsigned char *restore)
{
int err = PPERR_OK, eff;
if (!src || !dest || !src_len || !dest_len) return PPERR_ARGS;
/* detect format, only for encrypted data (PX20) */
switch SwapLong(src) {
case PPMODE_PX20: eff = 6; /* PX20 */
passkey &= 0xFFFFFFFF;
ppDecrypt(&src[10], src_len-14, passkey);
break;
default:
err = PPERR_DATAFORMAT;
}
/* error: no encrypted datas in *src */
if (err) { return err; }
if (!ppDecrunch(&src[eff], dest, src_len-(eff+8), dest_len)) {
/* if restore buffer is set, copy into *src (recommended!) */
if ((restore) != NULL) {
memmove(src, restore, src_len);
}
err = PPERR_PASSWORD;
}
return err;
}
/* *****************************************************************************
* ERR = ppDecrunchFile(*filename, *savename [,password])
* *****************************************************************************
* decrunch given file direct to a new file
* -----------------------------------------------------------------------------
* APTR *filename = name of file with PowerPacker packed datas
* APTR *savename = filename to store encrypted/decrunched datas
* CHAR password = optional: (max. 16 chars only!) to encrypt datas
* -----------------------------------------------------------------------------
* ERR = #PPERR_OK -> all done
* ****************************************************************************/
DLLIMPORT int ppDecrunchFile(unsigned char *filename, unsigned char *savename,
unsigned char *password)
{
unsigned char *src = NULL, *dest, *amos;
unsigned int srclen, destlen;
int err = PPERR_OK, eff;
FILE *fh;
HANDLE hFile;
if (!filename || !savename) return PPERR_ARGS;
/* open file, find out srclen, allocate src and read file */
if ((fh = fopen(filename, "rb"))) {
if ((fseek(fh, 0, SEEK_END) == 0) &&
(srclen = (unsigned int) ftell(fh)) &&
(fseek(fh, 0, SEEK_SET) == 0))
{
if ((src = malloc(srclen))) {
if (fread(src, 1, srclen, fh) != srclen) {
free(src); err = PPERR_READ;
}
}
else err = PPERR_NOMEMORY;
}
else err = PPERR_SEEK;
fclose(fh);
}
else err = PPERR_OPEN;
if (err) return err;
fclose(fh);
/* detect format, decrypt if necessary */
switch SwapLong(src) {
case PPMODE_PP11: eff = 4; break; /* PP11 */
case PPMODE_PP20: eff = 4; break; /* PP20 */
case PPMODE_PPBK: eff = 20; break; /* PPbk */
case PPMODE_PPLS: eff = 8; break; /* PPLS */
case PPMODE_PX20: eff = 6; /* PX20 */
if (!password || (ppCalcChecksum(password) != ((src[4]<<8)|src[5])))
err = PPERR_PASSWORD;
else
ppDecrypt(&src[10], srclen-14, ppCalcPasskey(password));
break;
default:
err = PPERR_DATAFORMAT;
}
if (err) {
free(src);
return err;
}
/* allocate memory for decrunch buffer, then decrunch */
destlen = (src[srclen-4] << 16) | (src[srclen-3] << 8) | src[srclen-2];
if ((dest = malloc(destlen))) {
if (!ppDecrunch(&src[eff], dest, srclen-(eff+8), destlen))
err = PPERR_DECRUNCH;
else
hFile = CreateFile(savename, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (hFile != INVALID_HANDLE_VALUE) {
switch SwapLong(src) {
case PPMODE_PPBK: eff = 8; break; /* AMOS PPbk Bank = 8 Bytes ID String */
default:
eff = 0;
}
DWORD dwWritten;
if (WriteFile(hFile, &dest[eff], destlen-eff, &dwWritten, NULL)) {
if ((destlen-eff) != (dwWritten)) { err = PPERR_DECRUNCH; }
}
CloseHandle(hFile);
}
}
if (src) { free(src); }
if (dest) { free(dest); }
return err;
}
/* *****************************************************************************
* CKSUM = ppCalcChecksum(password)
* *****************************************************************************
* calculate a 16 bit checksum of given password, needed to control PX20 cksum
* -----------------------------------------------------------------------------
* CHAR password = max. 16 chars
* -----------------------------------------------------------------------------
* CKSUM = 16 bit checksum of password
* ****************************************************************************/
DLLIMPORT int ppCalcChecksum(unsigned char *password)
{
unsigned int cksum = 0;
unsigned char c, shift;
/* for each byte in the password */
while ((c = *password++)) {
/* barrel-shift the 16 bit checksum right by [c] bits */
shift = c & 0x0F;
if (shift) cksum = (cksum >> shift) | (cksum << (16-shift));
/* add c to the cksum, with 16 bit wrap */
cksum = (cksum + c) & 0xFFFF;
}
return cksum;
}
/* *****************************************************************************
* PWKEY = ppCalcPasskey(password)
* *****************************************************************************
* calculate a 32 bit checksum of given password, needed to crack crypted buffer
* -----------------------------------------------------------------------------
* CHAR password = max. 16 chars
* -----------------------------------------------------------------------------
* PWKEY = 32 bit passkey of password
* ****************************************************************************/
DLLIMPORT int ppCalcPasskey(unsigned char *password)
{
unsigned int key = 0;
unsigned char c;
/* for each byte in the password */
while ((c = *password++)) {
/* rotate 32 bit key left by one bit */
key = (key << 1) | (key >> (32-1));
key &= 0xFFFFFFFF;
/* add c to the key, with 32 bit wrap */
key = (key + c) & 0xFFFFFFFF;
/* swap lower and upper 16 bits */
key = (key << 16) | (key >> 16);
key &= 0xFFFFFFFF;
}
return key;
}
/* *****************************************************************************
* INT ppDecrunch(*src, *dest, src_len, dest_len)
* *****************************************************************************
* intern: 1 all decrunched, 0 = error occured
* -----------------------------------------------------------------------------
* APTR *src = buffer of PowerPacker encrypted PX20 datas
* APTR *dest = buffer to copy encrypted/decrunched datas
* DWORD src_len = size in bytes of *src
* DWORD dest_len = size in bytes of decrunched buffer
* ****************************************************************************/
int ppDecrunch(unsigned char *src, unsigned char *dest, unsigned int src_len,
unsigned int dest_len)
{
unsigned char *buf, *out, *dest_end, *off_lens, bits_left = 0, bit_cnt;
unsigned int bit_buffer = 0, x, todo, offbits, offset, written = 0;
if (src == NULL || dest == NULL) return 0;
/* set up input and output pointers */
off_lens = src; src = &src[4];
buf = &src[src_len];
out = dest_end = &dest[dest_len];
/* skip the first few bits */
PP_READ_BITS(src[src_len + 3], x);
/* while there are input bits left */
while (written < dest_len)
{
PP_READ_BITS(1, x);
if (x == 0)
{
/* bit==0: literal, then match. bit==1: just match */
todo = 1; do { PP_READ_BITS(2, x); todo += x; } while (x == 3);
while (todo--) { PP_READ_BITS(8, x); PP_BYTE_OUT(x); }
/* should we end decoding on a literal, break out of the main loop */
if (written == dest_len) break;
}
/* match: read 2 bits for initial offset bitlength / match length */
PP_READ_BITS(2, x);
offbits = off_lens[x];
todo = x+2;
if (x == 3) {
PP_READ_BITS(1, x);
if (x == 0) offbits = 7;
PP_READ_BITS(offbits, offset);
do { PP_READ_BITS(3, x); todo += x; } while (x == 7); }
else {
PP_READ_BITS(offbits, offset);
}
if (&out[offset] >= dest_end) return 0; /* match_overflow */
while (todo--) { x = out[offset]; PP_BYTE_OUT(x); }
}
/* all output bytes written without error */
return 1;
}
/* *****************************************************************************
* VOID ppDecrypt(*data, len, key)
* *****************************************************************************
* intern: decrypt buffer with given passkey
* -----------------------------------------------------------------------------
* APTR *data = buffer of PowerPacker encrypted PX20 datas
* DWORD len = size in bytes of *data
* ULONG key = 32 bit key (=ppCalcPasskey())
* ****************************************************************************/
void ppDecrypt(unsigned char *data, unsigned int len, unsigned int key)
{
unsigned char k0 = (key >> 24) & 0xFF;
unsigned char k1 = (key >> 16) & 0xFF;
unsigned char k2 = (key >> 8) & 0xFF;
unsigned char k3 = (key ) & 0xFF;
len = ((len + 3) >> 2) - 1;
/* to replicate unofficial powerpacker.library v37.3 bug, uncomment line */
len &= 0xFFFF;
/* XOR data with key */
do {
*data++ ^= k0;
*data++ ^= k1;
*data++ ^= k2;
*data++ ^= k3;
} while (len--);
}
BOOL APIENTRY DllMain (HINSTANCE hInst /* Library instance handle. */ ,
DWORD reason /* Reason this function is being called. */ ,
LPVOID reserved /* Not used. */ )
{
switch (reason)
{
case DLL_PROCESS_ATTACH:
break;
case DLL_PROCESS_DETACH:
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
break;
}
/* Returns TRUE on success, FALSE on failure */
return TRUE;
}