/*
   CyberRax Programming (C) 2001 - 2003
   TITLE:   ADCODE.H--
   DESCRIPTION:   Several procedures for crashing/detecting/disabling debuggers
                  and automatic-executable-unpackers. Basically code for
                  making the debugging/cracking/reverse engineering of program
                  harder
   LAST MODIFIED:   7 Sep 2003
   PROCEDURES DEFINED IN THIS FILE:
       : void DETECT_DG()
       : void TD86TRAP()
       : void ANTITD86TRAP()
       : void UNPTRAP()
       : void ANTIUNPTRAP()
       : void TRACERTRAP()
       : void ANTITRACERTRAP()
       : void DEBUGGERTRAP(offset_address_of_DEBUGGERTRAP)
       : void ANTIDEBUGGERTRAP(offset_address_of_ANTIDEBUGGERTRAP)
       : void FLAGCHECK()
       : void ANTIFLAGCHECK()
       : byte CLEARMEM(exit_code)
       : void DUMPPREVENT()
       : void CRASH_RM_DEBUGGER1()
       : void CRASH_RM_DEBUGGER2()
       : void DETECT_RM_DEBUGGER1()
       : void LOCK_KEYBOARD()
       : void UNLOCK_KEYBOARD()
       : void TINY_NEBELBOMB1()
       : void TINY_NEBELBOMB2()
       : void SMALL_NEBELBOMB()
       : void MEDIUM_NEBELBOMB()
       : void LARGE_NEBELBOMB()
       : void DETECT_SOFTICE()
       : void DETECT_LTR()
       : void FOOL_INTRUDER()
       : void FOOL_UPC()
       : word CRC16(start_of_code,size_of_code_in_bytes,checksum)
*/

? ifndef _ADCODE_
? define _ADCODE_ TRUE

// Declare procedures
word CRC16 (SI, CX, DX);



: void TD86TRAP ()   // ver. 1.2, without INT3 again
/* A little trap for Borland's Turbo Debugger in 86 mode, so the debugger
   will crash by debugging. The trick is the HLT command which TD doesn't
   like
   Size: 5 bytes
   PS! The program will work correctly under other debuggers or when the
       program isn't debugged. You have to use this procedure as a macro
       (by putting @ in front of TD86TRAP() when calling) to disable TD's
       'Step over' (F8) feature.
*/
{
$JMP SHORT TD_TRAP
$DB 0xEA

TD_TRAP:
$STI
$HLT
}
/* Returns: nothing
*/



: void ANTITD86TRAP ()
/* A trap "replacement" for TD86TRAP() for use during testing (I use TD myself
   and I'd hate to continously shut the computer off and on because my own
   trap forces me to)
   Size: also 6 bytes
*/
{
$DB 5 dup 0x90
}



: void UNPTRAP ()   // ver. 1.1, doesn't stop DEBUG anymore
/* A trap for UNP: UNP will fail tracing because of the INT 1
   Size: 1 bytes
*/
{
$INT 1
}



: void ANTIUNPTRAP ()
/* A replacement for UNPTRAP() for testing.
   Size: 1 bytes
*/
{
$NOP
}



: void TRACERTRAP ()   // ver. 1.01, no AX preserving anymore
/* A general (I hope) trap for tracers and some debuggers (atleast it stops
   UNP), which works by clearing the Trap flag.
   Size: 7 bytes
*/
{
$PUSHF
$POP AX
$AND AX,0xFEFF
$PUSH AX
$POPF
}
/* Returns: AX = destroyed
*/



: void ANTITRACERTRAP ()
/* A secure replacement for TRACERTRAP()
   Size: 7 bytes
*/
{
$DB 7 dup 0x90
}



: void DEBUGGERTRAP ()
/* A stack trick to make debuggers crash, which exploits the fact that most
   real mode debuggers don't have their own stack but use the stack of
   debugged program. This procedure sets the stack inside itself and by that
   some of the program is overwritten with debugger's stack (if no debugger
   is present or a protected mode debugger is used (probably) then the program
   will work without any problems).
   Size: 26 bytes
*/
{
AX += 16;
$CLI
BX = SS;
CX = SP;
$PUSH CS
$POP SS
SP = AX;
$NOP
$NOP
$JMP SHORT OVERTRAP
$INT 0x20
OVERTRAP:
SP = CX;
SS = BX;
$STI
}
/* Returns: AX = BX = CX = destroyed
*/



: void ANTIDEBUGGERTRAP ()
/* A replacement for DEBUGGERTRAP()
   PS! You MUST call this procedure like DEBUGGERTRAP(), this means calling
       ANTIDEBUGGERTRAP(offline_address_of_ANTIDEBUGGERTRAP()) is RIGHT, but
       ANTIDEBUGGERTRAP() is WRONG.
   Size: 26 bytes
*/
{
$DB 23 dup 0x90
}
/* Returns: AX = destroyed, all other registers are preserved
*/



: void FLAGCHECK ()
/* Add-on for TRACERTRAP() which should be called after TRACERTRAP()
   and checks if Trap flag is still cleared. If not then a real mode
   debugger might be present.
   Size: 5 bytes
*/
{
$PUSHF
$POP AX
AX &= 0x100;
}
/* Returns: AX = 0 if debugger present
            AX <> 0 if no debugger
*/



: void ANTIFLAGCHECK ()
/* A replacement for FLAGCHECK(). This procedure sets AX <> 0 as there
   wouldn't be any debugger.
   Size: 5 bytes
*/
{
AX = 0x23;
$NOP
$NOP
}
/* Returns: AX = BX = 35
*/



: void ANTISETINTVECT ()
/* A temporary replacement for SETINTVECT(). This procedure here has a
   limitation: ES won't be destroyed so you have to remember to PUSH
   and POP it if you need to preserve this register even if this procedure
   here doesn't kill it
   Size: 17 bytes
*/
{
$DB 17 dup 0x90
}
/* Returns: nothing changed
*/



: byte CLEARMEM ()   // ver. 1.02, optimized by 2 bytes
/* Erases the program (COM file) from memory. That happens by filling the
   memory from CS:100h to the beginning of this codeblock with spaces. A
   security mesurment. After that the program terminates with the specified
   exit-code
   Size: 28-30 bytes
*/
{
$CALL THE_END_OF_FILL
THE_END_OF_FILL:
$POP CX
DI=0x100;
CX -= DI;

#ifdef codesize
 $PUSH AX
 $PUSH CS
 $POP ES
 AL=0x20;
 $REP $STOSB
 $POP AX
#else
 loop(CX)
    {
    CSBYTE[DI]=0x20;
    DI++;
    }
#endif

AH = 0x4C;
$INT 0x21
}
/* Returns: nothing
*/



: void DUMPPREVENT ()   // ver. 1.02, no need for offset anymore
/* Prevents the program from being dumped by programs a'la LCDump (by
   CyberRax  :) by checking a byte and if it's not what it supposed to
   be then the program terminates. If the byte is OK then it'll be
   set wrong, so the dumped version will terminate.
   Call at the beginning of the program to get the best result.
   PS!  This code won't stop anyone who knows assembler
   Size: 11 bytes
*/
{
$CALL THE_CHECK_LINE
$NOP
THE_CHECK_LINE:
$POP DI
IF (DSBYTE[DI] <> 0x90)
   $INT 0x20
DSBYTE[DI] = 0xF4;
}
/* Returns: DI = not destroyed
*/



: void DETECT_DG ()
/* Detects DeGlucker debugger (an awesome piece of code, I must say) by using
   it's own installation check(?)
   Size: 6 bytes
*/
{
$STC
AX = 0xFF00;
$INT 0x15
}
/* Returns: carry set if DeGlucker NOT installed
            carry not set if DG in memory
*/



: void CRASH_RM_DEBUGGER1 ()
/* Crashes real-mode debuggers by leting them overwrite a part of interrupt
   table
*/
{
$CLI
AX = SP;
SP = 3;     // Crashs rm debuggers which do not keep their own stack.
SP = AX;
$STI
}
/* Returns: nothing if some RM debugger was active (the procedures simply
               crashes)
            AX destroyed
*/



: void CRASH_RM_DEBUGGER2 ()
/* Crashes real-mode debuggers by seting INT1 to an invalid area (RM debuggers
   have hooked INT1 which is called after every programline if TRAPFLAG is on)
*/
{
$PUSH DS
AX = 0;
DS = AX;
$NOT DSWORD[4]
$JMP SHORT J1
$DB 0x9A
J1:
$NOT DSWORD[4]
$POP DS
}
/* Returns: nothing if some RM debugger was active (the procedures simply
               crashes)
            AX = 0
*/



: void DETECT_RM_DEBUGGER1 ()
/* Detects real-mode debuggers by puting a word into stack and then checking
   if it's been altered
*/
{
$CLI
BX = SP;
SSWORD[BX-2] = -1;
$CMP SSWORD[BX-2],-1
$STI
}
/* Returns: ZEROFLAG set if no RM debugger active
            ZEROFLAG >>not<< set if no debugger
            BX destroyed
*/



: void LOCK_KEYBOARD ()
/* Locks the keyboard so debugging isn't possible anymore
   Works under less-advanced debuggers MS-DOS Debug, TD, etc.
   NOTE: use UNLOCK_KEYBOARD() before keyboard input (or before program's
         termination)
*/
{
AL = 0xAD;
$OUT 0x64,AL
}
/* Returns: AL destroyed
*/



: void UNLOCK_KEYBOARD ()
/* Unlocks the keyboard
*/
{
AL = 0xAE;
$OUT 0x64,AL
}
/* Redurns: AL destroyed
*/



: void TINY_NEBELBOMB1()
/* Irritation macro who's mission is to make the diassembly more difficult or
   atleast more annoying and works by jumping in the middle of another opcode
   MUST be used as macro to have any effect
*/
{
$DB 0xEB,3,0x66,0x81,0xC3
}


: void TINY_NEBELBOMB2()
/*
*/
{
$DB 0xEB,3,0x66,0x81,0x1D
}


: void SMALL_NEBELBOMB ()
/* A slightly larger and tougher nebelbomb
*/
{
AX = 0x3EB;
$DB 0xEB,0xFC,0xEA
}
/* Returns: AX destroyed
*/



: void MEDIUM_NEBELBOMB ()
/* A somewhat larger irritation macro
*/
{
$PUSH BX                      // Save BX
BX  = 0x4EB;
$POP BX                       // Restore BX
IKKA2:
$JMP SHORT IKKA2-3            // Jumps straight into the "0EBh, 04h" jump
$DB 0x9A                      // Irritation opcode
}
/* Returns: nothing
*/



: void LARGE_NEBELBOMB ()
/* The largest irritation macro
*/
{
$PUSH DX                      // Save DX
DX = 0x5EB;
$POP DX                       // Restore DX
IKKA3:
$JMP SHORT IKKA3-3            // Jump into "0EBh, 06h"
$DB 0x66,0x69                 // 8 byte opcode irritation
}
/* Returns: nothing
*/



: void DETECT_SOFTICE ()
/* Detects SoftICE (if I'm not wrong then only the WindBlows version)
*/
{
AH = 0x43;
$INT 0x68             // winice has int 68h handler and returns following value
$CMP AX,0xF386
$JE WICE
AX = 0;
WICE:
}
/* Returns: AX = 0 if (Win)SICE not installed (or patched)
            AX = F386h if SoftICE installed
*/



: void DETECT_LTR ()
/* Detects LTR debugger by exploiding a bug which accours if only the file
   name was given to LTR in commandline (ie if there was no additional syntax)
*/
{
$CMP DSWORD[0x80],0x2001
$JNE NOT_LTR
$CMP DSWORD[0x82], 0xD
$JNE NOT_LTR
NOT_LTR:
}
/* Returns: ZEROFLAG set if LTR in memory
            ZEROFLAG >>not<< set if LTR not present
*/



: void FOOL_INTRUDER ()
/* Fools the first library-based unpacker, Intruder, into thinking that
   the program has been written in Pascal so Intruder would make a dump.
   You get the best result if this code is included before decryption process
   (so the dump is also encrypted)
*/
{
AH = 0x30;
$INT 0x21                    // Now Intruder will get back control and will
BP = DSWORD[2];              // find the following few bytes so that a
BX = DSWORD[0x2C];           // wrong dump will appear.
}



: void FOOL_UPC ()
/* Fools UPC into thinking that the program has been compiled in Zortech C
   so it would dump it. Also works by 'XPack -ux' (which uses UPC's code)
   You get the best result if this code is included before decryption process
   (so the dump is also encrypted)
*/
{
$PUSH DS
$POP AX
AH = 0x30;
$INT 0x21
$JMP SHORT SKIP

$DB 0x8B,0x5E,4,0x82,0x37,0
AX = 0x4C01;
$INT 0x21                        // Exit with Errorlevel=01
$PUSH ES
$POP DS
$PUSH SS
$POP ES
SI = 0x80;
$CLD                             // Clear direction
$LODSB                           // String [si] to al
$XOR AH,AH
CX = 0xCD2;
DI = 0x2000;

SKIP:
}



: word CRC16 ()
/* Calculates CRC16 (Cyclic Redudance Check) checksum of the given code.
   This codepiece uses the given checksum to overwrite 2 bytes right AFTER the
   code which should be checked (so put two NOPs or something like that in
   this place). It's mainly meant for validating the checksum.
   There are two possible ways for the validation:
      a) you give 0 (zero) as checksum and compare the calculated checksum
         against a previous checksum
      b) you give a previously caclulated checksum as the last parameter and if
         the procedure returns 0 (zero) then the crc is OK. Othervise the code
         has been tampered with.
   DS has to equal to the segment where the checked code is (usually CS)
   Size: 
*/
{
BX = CX;
DSWORD[BX+SI] = DX;  // Append 0 word or checksum to DS:SI+BX
DX = 0;              // Set initial register value (zero DX)
CX++;                // Increment length for the zeroes/checksum...
CX++;                // ...by two (a word)
AX = 0xA000; //1;         // AX is reversed CRC-16 polynomial (divisor)

GO_ON:
BP = 0008;           // BP is current bit pointer
BL = DSBYTE[SI];     // Load byte at DS:SI into BL

GO_ON_X:
$RCR BL,1            // Rotate bits in BL right by one bit,
                     //    in/out through Carry Flag
                     //    RCL: Bytes normal (bit 7 first)
                     //    RCR: Bytes reflected (bit 0 first)
$RCR DX,1            // Rotate bits in DX right by one bit,
                                        // in/out through Carry Flag and...
$JNB GO_ON_1         // ...if result is CF=1, skip over...
$XOR DX,AX           // ..."subtraction" of AX from DX
GO_ON_1:
BP--;                // Decrement bit pointer by one and...
$JZ GO_ON_2          // ...if result is BP=0000, skip over...
$JMP GO_ON_X         // ...jump to test next bit
GO_ON_2:
SI++;                // Increment byte offset value by one
$LOOP GO_ON          // Looping to CS:0113 until CX=0001
AX >< DX;
}
/* Returns: if 0 was seeded as last parameter
               AX = checksum
            if checksum was seeded
               AX = 0 if checksum was correct
               AX <> 0 if checksum was NOT correct (code has changed)
            DX = 0xA001
            AX, BX, CX, SI and BP destroyed
*/



? endif


/* end of ADCODE.H-- */
