/* **********************************************************
 * Copyright (c) 2005-2007 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of VMware, Inc. nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

#include <stdio.h>

#include "share.h"
#include "utils.h"
#include "hotp-test.h"

#define OUTFILE L"tester.out"

#define ATTACK_NONE 0
#define ATTACK_STACK 1
#define ATTACK_HEAP 2

int attack_status = ATTACK_NONE;

DWORD
WINAPI
heap_attack(LPVOID param);

DWORD
WINAPI
stack_attack(LPVOID param);

DWORD
WINAPI
ls_attack(LPVOID param);

enum { stack = 1, heap, liveshield };

/* disable type case warning for attacks below. */
#pragma warning(disable : 4055)

void
usage()
{
    fprintf(
        stderr,
        " usage: tester [initial_sleep_ms] [attack] [num_attacks]\n"
        "\n"
        " tester will then do:\n"
        "  (1) [initial_sleep_ms] sleep before doing anything\n"
        "  (2) if [attack]/[num_attacks] params are not set, exits\n"
        "  (3) [attack=1,2 or 3] (executes 1=stack, 2=heap or 3=liveshield attack \n"
        "      (1) liveshield attacks execute test_reg and test_control_flow hotpatches\n"
        "          (equivalent of araktest liveshield buttons 1 and 2)\n"
        "      (2) write to \"tester.out.xx\" two characters:\n"
        "          -- 0 or 1 according to whether test_reg was patched\n"
        "          -- 0 or 1 according to whether test_control_flow was patched\n"
        "  (4) repeat step (3) [num_attacks] times\n"
        "\n"
        " To run under DR setup appropriate registry settings for tester.exe\n");
}

int
main(int argc, char **argv)
{
    int timeout = 5000;
    /*char output[MAX_PATH];*/
    if (argc == 2 && (_stricmp(argv[1], "-h") == 0 || _stricmp(argv[1], "-help") == 0)) {
        usage();
        exit(0);
    }

    if (argc >= 2)
        timeout = atoi(argv[1]);

    Sleep(timeout);

    if (argc >= 3) {
        int i, count = 1;
        int attack = atoi(argv[2]);
        if (attack == 0) {
            fprintf(stderr, "attack=%d not expected\n", attack);
            usage();
            exit(-1);
        }

        if (argc >= 4)
            count = atoi(argv[3]);

        for (i = 0; i < count; i++) {
            HANDLE athr;
            fprintf(stderr, "loop %d\n", i);
            if (attack == 1)
                athr = CreateThread(NULL, 0, stack_attack, NULL, 0, NULL);
            else if (attack == 2)
                athr = CreateThread(NULL, 0, heap_attack, NULL, 0, NULL);
            else if (attack == 3)
                athr = CreateThread(NULL, 0, ls_attack, (void *)i, 0, NULL);
            else {
                fprintf(stderr, "attack=%d can take 1,2 or 3\n", attack);
                usage();
                exit(-1);
            }

            if (athr == NULL) {
                fprintf(stderr, "tc %d\n", GetLastError());
                exit(-1);
            }
            WaitForSingleObject(athr, INFINITE);
            CloseHandle(athr);
        }
    }

    return 0;
}

/*
123:
124:  void test(void(*f)(int),  int i)
125:
126:  {
004012A0 55                   push        ebp
004012A1 8B EC                mov         ebp,esp
004012A3 83 EC 40             sub         esp,40h
004012A6 53                   push        ebx
004012A7 56                   push        esi
004012A8 57                   push        edi
004012A9 8D 7D C0             lea         edi,[ebp-40h]
004012AC B9 10 00 00 00       mov         ecx,10h
004012B1 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
004012B6 F3 AB                rep stos    dword ptr [edi] ****GET RID OF THIS CRAP ****
127:      (f)(i);
004012B8 8B F4                mov         esi,esp
004012BA 8B 45 0C             mov         eax,dword ptr [ebp+0Ch]
004012BD 50                   push        eax
004012BE FF 55 08             call        dword ptr [ebp+8]
004012C1 83 C4 04             add         esp,4
004012C4 3B F4                cmp         esi,esp
004012C6 E8 55 10 00 00       call        _chkesp (00402320) ***GET RID OF THIS B'COZ ABS
ADDR **** 128:      return; 129:  } 004012CB 5F                   pop         edi 004012CC
5E                   pop         esi 004012CD 5B                   pop         ebx
004012CE 83 C4 40             add         esp,40h
004012D1 3B EC                cmp         ebp,esp
004012D3 E8 48 10 00 00       call        _chkesp (00402320) ***GET RID OF THIS B'COZ ABS
ADDR **** 004012D8 8B E5                mov         esp,ebp 004012DA 5D pop         ebp
004012DB C3                   ret


*/
unsigned char test_compiled_with_debug[] = {
    0x55, 0x8B, 0xEC, 0x83, 0xEC, 0x40, 0x53, 0x56, 0x57, 0x8D, 0x7D, 0xC0, 0xB9, 0x10,
    0x00, 0x00, 0x00, 0xB8, 0xCC, 0xCC, 0xCC, 0xCC,
    /* 0xF3, 0xAB, */
    0x8B, 0xF4, 0x8B, 0x45, 0x0C, 0x50, 0xFF, 0x55, 0x08, 0x83, 0xC4, 0x04, 0x3B, 0xF4,
    /* 0xE8, 0xC5, 0x0E, 0x00, 0x00, */
    0x5F, 0x5E, 0x5B, 0x83, 0xC4, 0x40, 0x3B, 0xEC,
    /* 0xE8, 0xB8, 0x0E, 0x00, 0x00, */
    0x8B, 0xE5, 0x5D, 0xC3, 0x00, 0x00, 0x00, 0x00
};

void
test(void (*f)(int), int i)
{
    (f)(i);
    return;
}

/*
123:
124:  void test(void(*f)(int),  int i)
125:
126:  {
004012A0 55                   push        ebp
004012A1 8B EC                mov         ebp,esp
127:      (f)(i);
004012BA 8B 45 0C             mov         eax,dword ptr [ebp+0Ch]
004012BD 50                   push        eax
004012BE FF 55 08             call        dword ptr [ebp+8]
128:      return;
129:  }
004012D8 8B E5                mov         esp,ebp
004012DA 5D                   pop         ebp
004012DB C3                   ret


*/
unsigned char sendfunc[] = { 0x55, 0x8B, 0xEC, 0x8B, 0x45, 0x0C, 0x50, 0xFF, 0x55,
                             0x08, 0x8B, 0xE5, 0x5D, 0xC3, 0x00, 0x00, 0x00, 0x00 };

void
set_attack(int i)
{
    attack_status = i;
}

void
fool_opt_compiler(unsigned char *foo)
{
    foo[0] = 1;
}

DWORD
WINAPI
stack_attack(LPVOID param)
{
    unsigned char myfunc[1024];
    int i;

    /* use the param to make compiler happy */
    if (param == NULL)
        i = 0;

    if (sizeof(sendfunc) >= sizeof(myfunc))
        MessageBox(NULL, L"ERROR", L"Memory allocation problem", MB_OK);

    for (i = 0; i < sizeof(sendfunc); i++)
        myfunc[i] = sendfunc[i];

    ((void (*)(void (*)(int), int))((void *)myfunc))(set_attack, ATTACK_STACK);
    fool_opt_compiler(myfunc);
    return ERROR_SUCCESS;
}

DWORD
WINAPI
heap_attack(LPVOID param)
{
    unsigned char *myfunc;
    int i;

    /* use the param to make compiler happy */
    if (param == NULL)
        i = 0;

    myfunc = (unsigned char *)malloc(sizeof(sendfunc));

    if (myfunc == NULL)
        MessageBox(NULL, L"ERROR", L"Memory allocation problem", MB_OK);

    for (i = 0; i < sizeof(sendfunc); i++)
        myfunc[i] = sendfunc[i];

    ((void (*)(void (*)(int), int))((void *)myfunc))(set_attack, ATTACK_HEAP);
    free(myfunc);
    return ERROR_SUCCESS;
}

DWORD
WINAPI
ls_attack(LPVOID param)
{
    char output[MAX_PATH];
    WCHAR filename[MAX_PATH];

    _snwprintf(filename, MAX_PATH, L"%s.%d", OUTFILE, (int)param);
    delete_file_rename_in_use(filename);

    _snprintf(output, MAX_PATH, "%d%d\n", hotp_test_reg(), hotp_test_control_flow());
    write_file_contents(filename, output, TRUE);

    return ERROR_SUCCESS;
}