2012年2月1日 星期三

仿Minimem程式

在firefox使用memory至高的年代裡, 有人寫出了這個東西來減少它對於記憶體的須求量, 一直都很好奇, 這樣的程式是怎麼寫出來的, 直到reverse了minimem之後才發現, 原來這東西很簡單, 就單單只是呼叫了Windows API EmptyWorkingSet,

BOOL WINAPI EmptyWorkingSet(
  __in  HANDLE hProcess
);

http://msdn.microsoft.com/en-us/library/windows/desktop/ms682606(v=vs.85).aspx

如此一來就可以減低一個程式所使用的記憶體, API怎麼使用就不是重點了。因為很簡單, 只要傳入想要reduce memory的PID就可以。雖然說minimem好用, 但手癢還是想自己寫一個windows service來達成這樣的功能, 所以就動手自己寫了一個(很久沒發程式文了...), 如果再加上UI的話, 就和minimem一模一樣了。


#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#include <psapi.h>
#include <vector>
#include <string>


#pragma comment(lib, "Psapi.lib")
#define SERVICE_NAME L"Give Me More Memory"


#define LOG(x) x, __FUNCTION__, __LINE__




namespace {
    TCHAR* serviceName = SERVICE_NAME;
    SERVICE_STATUS serviceStatus = {0};
    SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
    HANDLE stopServiceEvent = 0;


    void TraceLog(const char* szMessage, const char* szFunctionName, const int nLineNum) {
        char* szPath = "C:\\GiveMeMemory.log";
        FILE* f = fopen(szPath, "a+");
        if(f != NULL) {
            fprintf(f, "[%s][%.4ld]\t\t>>> %s\n", szFunctionName, nLineNum, szMessage);
        }
        fclose(f);
    }
}


void WINAPI ServiceControlHandler(DWORD controlCode);
void WINAPI ServiceMain(DWORD /*argc*/, TCHAR* /*argv*/[]);
void RunService();
void InstallService();
void UninstallService();
void EnumProcessID(std::vector<DWORD>& vecProcessID);
void ReduceWorkingSet(std::vector<DWORD>& vecProcessID);


void WINAPI ServiceControlHandler(DWORD controlCode)
{
    switch(controlCode) {
    case SERVICE_CONTROL_INTERROGATE:
        break;


    case SERVICE_CONTROL_SHUTDOWN:
    case SERVICE_CONTROL_STOP:
        serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        SetServiceStatus(serviceStatusHandle, &serviceStatus);
        SetEvent(stopServiceEvent);
        return;


    case SERVICE_CONTROL_PAUSE:
        break;


    case SERVICE_CONTROL_CONTINUE:
        break;


    default:
        if(controlCode >= 128 && controlCode <= 255)
            // user defined control code
            break;
        else
            // unrecognised control code
            break;
    }


    SetServiceStatus(serviceStatusHandle, &serviceStatus);
}


void WINAPI ServiceMain( DWORD /*argc*/, TCHAR* /*argv*/[] )
{
    // initialise service status
    serviceStatus.dwServiceType = SERVICE_WIN32;
    serviceStatus.dwCurrentState = SERVICE_STOPPED;
    serviceStatus.dwControlsAccepted = 0;
    serviceStatus.dwWin32ExitCode = NO_ERROR;
    serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
    serviceStatus.dwCheckPoint = 0;
    serviceStatus.dwWaitHint = 0;


    serviceStatusHandle = RegisterServiceCtrlHandler(serviceName, ServiceControlHandler);


    stopServiceEvent = CreateEventW(0, 0, 0, L"Give Me Memory Service Event");


    if(serviceStatusHandle)
    {
        // service is starting
        serviceStatus.dwCurrentState = SERVICE_START_PENDING;
        SetServiceStatus( serviceStatusHandle, &serviceStatus );


        // running
        serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
        serviceStatus.dwCurrentState = SERVICE_RUNNING;
        SetServiceStatus(serviceStatusHandle, &serviceStatus);


        do
        {
            TraceLog(LOG("Loop--every 30 seconds will reduce process memory..."));
            std::vector<DWORD> vecProcessID;
            EnumProcessID(vecProcessID);
            ReduceWorkingSet(vecProcessID);


        }
        while(WaitForSingleObject(stopServiceEvent, 30000) == WAIT_TIMEOUT);


        // service was stopped
        serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
        SetServiceStatus( serviceStatusHandle, &serviceStatus );


        // do cleanup here
        CloseHandle(stopServiceEvent);
        stopServiceEvent = 0;


        // service is now stopped
        serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
        serviceStatus.dwCurrentState = SERVICE_STOPPED;
        SetServiceStatus( serviceStatusHandle, &serviceStatus );
    }
}


void RunService()
{
    SERVICE_TABLE_ENTRY serviceTable[] = {
        { serviceName, ServiceMain },
        { 0, 0 }
    };


    BOOL bSetted = StartServiceCtrlDispatcher(serviceTable);
}


void InstallService()
{
    SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);


    if(serviceControlManager)
    {
        TCHAR path[_MAX_PATH + 1] = {0};
        if(GetModuleFileName(0, path, sizeof(path)/sizeof(path[0])) > 0)
        {
            SC_HANDLE service = CreateService(serviceControlManager,
                serviceName, serviceName,
                SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS,
                SERVICE_AUTO_START, SERVICE_ERROR_IGNORE, path,
                0, 0, 0, 0, 0);
            if(service) {
                SERVICE_STATUS status = {0};
                ControlService(service, SERVICE_CONTROL_CONTINUE, &status);
                CloseServiceHandle(service);
            }
        }
        CloseServiceHandle(serviceControlManager);
    }
}


void UninstallService()
{
    SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CONNECT);


    if(serviceControlManager)
    {
        SC_HANDLE service = OpenService(serviceControlManager,
            serviceName, SERVICE_ALL_ACCESS);
        if(service)
        {
            SERVICE_STATUS serviceStatus;
            if(QueryServiceStatus(service, &serviceStatus))
            {
                if(serviceStatus.dwCurrentState == SERVICE_STOPPED)
                    DeleteService(service);
            }
            CloseServiceHandle(service);
        }
        CloseServiceHandle(serviceControlManager);
    }
}


int _tmain(int argc, TCHAR* argv[])
{
    if(argc > 1 && lstrcmpi(argv[1],TEXT("install")) == 0)
    {
        TraceLog(LOG("Install--Give Me More Memory Service..."));
        InstallService();
    }
    else if(argc > 1 && lstrcmpi(argv[1], TEXT("uninstall")) == 0)
    {
        TraceLog(LOG("Uninstall--Give Me More Memory Service..."));
        UninstallService();
    }
    else
    {
        // service manager will call this.
        TraceLog(LOG("Start--Give Me More Memory Service..."));
        RunService();
    }


    return 0;
}


void EnumProcessID(std::vector<DWORD>& vecProcessID) {
    TraceLog(LOG("Enumerate--Enumerate process ID..."));
    unsigned long nProcessNum = 100;
    DWORD* dwProcesses = new DWORD[nProcessNum];
    DWORD dwNeeded = 0, dwTotalProcesses = 0;


    while(true) {
        if (!EnumProcesses(dwProcesses, sizeof(DWORD)*nProcessNum, &dwNeeded )) {
            return;
        }
        dwTotalProcesses = dwNeeded / sizeof(DWORD);
        if(nProcessNum > dwTotalProcesses) {
            break;
        } else {
            nProcessNum *= 2;
            delete[] dwProcesses;
            dwProcesses = new DWORD[nProcessNum];
        }
    }
    // Print the name and process identifier for each process.
    for (unsigned int i = 0; i < dwTotalProcesses; i++ )
    {
        vecProcessID.push_back(dwProcesses[i]);
    }
    delete[] dwProcesses;
}


void ReduceWorkingSet(std::vector<DWORD>& vecProcessID)
{
    // Get a handle to the process.
    TraceLog(LOG("Reduce--Reduce process memory..."));
    std::vector<DWORD>::iterator iter = vecProcessID.begin();
    for (; iter != vecProcessID.end(); ++iter)
    {
        HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION  | PROCESS_SET_QUOTA, FALSE, *iter);
        BOOL bRet = EmptyWorkingSet(hProcess);
        CloseHandle(hProcess);
    }
}



有興趣的人就自己動手寫寫看吧, 這個程式也順便有windows service的功能了, 使用方法可以參考main function裡的參數。

沒有留言: