加密与解密之二次开发

描述

  • 二次开发的含义:通过直接编辑二进制,来修改已编译好的程序,实现目标功能
  • 本文的原程序模拟一个windows是最常见的采用事件循环机制的窗口程序,通过二次开发,给这个程序上锁,加上一个验证身份框,只有输对用户名密码,才能正常使用程序功能

原程序

  • 创建一个窗口,加入事件循环,响应窗口消息
  • 代码如下
#include <windows.h>
#include <cstdio>

#define ID_EDIT 1 // Added this line to define ID_EDIT

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CLOSE:
        DestroyWindow(hwnd);
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    default:
        return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;
    HWND hEdit;

    //Step 1: Registering the Window Class
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = 0;
    wc.lpfnWndProc = WndProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszMenuName = NULL;
    wc.lpszClassName = "WindowClass";
    wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

    if (!RegisterClassEx(&wc))
    {
        MessageBox(NULL, "Window Registration Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // Step 2: Creating the Window
    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        "WindowClass",
        "成功",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 500, 300,
        NULL, NULL, hInstance, NULL);

    if (hwnd == NULL)
    {
        MessageBox(NULL, "Window Creation Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // Step 3: Creating the Edit Control
    hEdit = CreateWindowEx(WS_EX_CLIENTEDGE, "EDIT", "",
        WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_AUTOVSCROLL | ES_AUTOHSCROLL,
        50, 50, 200, 100, hwnd, (HMENU)ID_EDIT, hInstance, NULL);

    if (hEdit == NULL)
    {
        MessageBox(NULL, "Edit Control Creation Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    // Step 4: Setting the Text of the Edit Control
    SendMessage(hEdit, WM_SETTEXT, NULL, (LPARAM)"恭喜你成功进入游戏!");

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    for (int i = 0; i++; i < 10) {
        printf("第%d轮\n", i);
    }
    // Step 5: The Message Loop
    while (GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }

    return Msg.wParam;
}
  • 运行效果:运行Msg.exe,效果如下

对原程序进行二次开发,上锁

原理

  • 将要添加的功能编写为dll,注入到目标程序中
  • 在目标程序中找到一个合适的位置,调用dll中的函数,特别要注意堆栈平衡和参数的设置,还要注意不能破坏原有程序的指令,如果原有指令被覆盖,需要在调用dll函数后再将被覆盖的指令执行一遍

步骤

  • 编写dll:创建一个注册窗口函数MyCreateWindow,GetMessage部分不用写,用原程序的就行,并编写窗口回调函数MyWndProc,在其中处理用户输入,将上述两个函数编写到dll中导出,只需要给原程序添加一个对MyCreateWindow的调用,就可以创建注册窗口并响应用户输入,dll部分的代码如下
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include <windows.h>

BOOL APIENTRY DllMain(HMODULE hModule,
    DWORD  ul_reason_for_call,
    LPVOID lpReserved
)
{
    switch (ul_reason_for_call)
    {
    case DLL_PROCESS_ATTACH:
    case DLL_THREAD_ATTACH:
    case DLL_THREAD_DETACH:
    case DLL_PROCESS_DETACH:
        break;
    }
    return TRUE;
}

extern "C" __declspec(dllexport) LRESULT CALLBACK MyWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    static HWND hwndUsername, hwndPassword, hwndButton;
    switch (message)
    {
    case WM_CREATE:
        hwndUsername = CreateWindow(TEXT("edit"), NULL,
            WS_CHILD | WS_VISIBLE | WS_BORDER,
            50, 50, 200, 25,
            hwnd, (HMENU)1, NULL, NULL);
        hwndPassword = CreateWindow(TEXT("edit"), NULL,
            WS_CHILD | WS_VISIBLE | WS_BORDER | ES_PASSWORD,
            50, 100, 200, 25,
            hwnd, (HMENU)2, NULL, NULL);
        hwndButton = CreateWindow(TEXT("button"), TEXT("OK"),
            WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON,
            100, 150, 80, 25,
            hwnd, (HMENU)3, NULL, NULL);
        break;
    case WM_COMMAND:
        if (LOWORD(wParam) == 3)
        {
            TCHAR username[20], password[20];
            GetWindowText(hwndUsername, username, 20);
            GetWindowText(hwndPassword, password, 20);
            if (lstrcmp(password, TEXT("test")) != 0) {
                MessageBox(hwnd, TEXT("密码错误,退出程序!"), TEXT("Error"), MB_OK);
                exit(0);
            }
            else {
                MessageBox(hwnd, TEXT("密码正确,继续你的游戏!"), TEXT("Success"), MB_OK);
                SendMessage(hwnd, WM_CLOSE, 0, 0);
            }
        }
        break;
    case WM_DESTROY:
        exit(0);
        break;
    case WM_CLOSE:
        exit(0);
        break;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);
}

extern "C" __declspec(dllexport) int WINAPI MyCreateWindow(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
    static TCHAR szAppName[] = TEXT("HelloWin");
    HWND hwnd2;
    WNDCLASS wndclass;

    wndclass.style = CS_HREDRAW | CS_VREDRAW;
    wndclass.lpfnWndProc = MyWndProc;
    wndclass.cbClsExtra = 0;
    wndclass.cbWndExtra = 0;
    wndclass.hInstance = hInstance;
    wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
    wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.lpszMenuName = NULL;
    wndclass.lpszClassName = szAppName;

    if (!RegisterClass(&wndclass))
    {
        MessageBox(NULL, TEXT("This program requires Windows NT!"),
            szAppName, MB_ICONERROR);
        return 0;
    }

    hwnd2 = CreateWindow(szAppName,                  // window class name
        TEXT("The Hello Program"), // window caption
        WS_OVERLAPPEDWINDOW,        // window style
        CW_USEDEFAULT,              // initial x position
        CW_USEDEFAULT,              // initial y position
        500,              // initial x size
        300,              // initial y size
        NULL,                       // parent window handle
        NULL,                       // window menu handle
        hInstance,                  // program instance handle
        NULL);                      // creation parameters

    ShowWindow(hwnd2, iCmdShow);
    UpdateWindow(hwnd2);
 }
  • 将dll注入到原程序:通过PE Tools,在输入表中添加MsgDll和MyCreateWindow函数
  • patch原程序:运行x64dbg,在目标程序中选取一处存放不重要代码的位置(更好的做法是选取区块间的间隙并跳转执行,这里偷个懒随便找了个不重要的赋值语句(1B40处))
  • 首先给我们的MyCreateWindow函数传入参数,这里需要传入WinMain的四个参数,可以看到程序刚进入函数时将四个参数保存到了栈上
  • 那么我们只要从栈上取出四个参数并放入寄存器,就可以给MyCreateWindow提供参数了
  • 放好参数好,准备写入call MyCreateWindow,call指令的十六进制为ff 15,地址的计算结果为1552A
  • 写入call MyCreateWindow,可以看到反汇编器成功解析处call函数的地址
  • 由于前面写入的指令破坏了后面的指令,所以加一个jmp跳过这些坏字节
  • 被调用函数MyCrateWindow已经进行了堆栈平衡,所以不需要在调用函数中抬高rsp
  • 本来到这里已经可以了,但出了点小问题,程序中间将rsp抬高了258个字节,所以后面从栈上取参数的位置也要相应变化,加上258个字节
  • 修改后程序如下

运行效果

  • 将MsgDll同Msg.exe放到同一目录下,运行Msg.exe,可以看到在程序正常窗口前,弹出了密码验证框,输错后程序退出
posted @ 2023-04-04 16:49  z5onk0  阅读(58)  评论(0编辑  收藏  举报