NetCore2K.net - Brought to you by Napalm and Typhoon
HOME PROJECTS ARTICLES FORUM LINKS
Username: Password:



Duty cycle: n. A washing machine setting thats particularly effective for getting skid marks out of underwear.

 Projects

 FreeRAM

> HelpPC 2.10 Quick Ref. Utility
An invaluable resource of real-mode programming related information.
> Windows GUI Glass Buttons
Small lecture on Windows GUI and GDI.
> FreeRAM
Memory boosters, optimizers, and washers.
> BlackCORE
x86 Operating System

Memory boosters, optimizers, and washers - whatever the name, they all do much the same thing, free up physical RAM. They do this by forcing as much allocated physical RAM into the page file as possible. Does this make your computer run faster? The short answer is No. In fact, just the opposite is usually the case.

How Memory Optimizers Work

Memory optimizers force all possible pages in RAM to the page file. Thus, the amount of free RAM is increased, but the amount of virtual memory in use is not affected. When the applications whose memory was paged out access that memory again it must be paged back into RAM.

The memory that is paged out must be paged back in for you to use the other applications running on your computer. Thus, pages that are used by the operating system components or background processes are often paged back in immediately after the memory "cleanup".

These memory optimization applications interfere with the ability of Windows to efficiently manage virtual memory. Furthermore, many of them don't even free RAM in a proper way. Often, they do so by simply allocating as much RAM as possible, forcing Windows to page out the memory of all other applications. The correct technique is to use the SetProcessWorkingSetSize API on each running process to force as much virtual memory to be paged out as possible.

These programs are ridiculously easy to write and sell. They usually don't live up to the claims they make. Savvy users should beware these applications, and for that matter, any other software that makes outlandish claims. So we are going to show you how to make one.

Building An Optimizer

What does FreeRAM actually do?

FreeRAM uses correct method of paging out memory, instead of allocating gobs of memory itself and forcing windows to page out virtual memory it uses the SetProcessWorkingSetSize[1] Win32 API.

Calling Definition:
BOOL SetProcessWorkingSetSize(HANDLE hProcess, SIZE_T dwMinimumWorkingSetSize, SIZE_T dwMaximumWorkingSetSize);

This is quite an interesting API it lets you set the working physical memory limits of a process, but more than that there's one line in the PSDK states,

"If both dwMinimumWorkingSetSize and dwMaximumWorkingSetSize have the value (SIZE_T)-1, the function temporarily trims the working set of the specified process to zero. This essentially swaps the process out of physical RAM memory."

"Perfect" I here you say, yes indeed its quite simple from now on in. We get a list of processes and run the API on them... Well no actually its not that simple... When is it ever!

The Planning Stage

Ok, first of all. The PSDK states the the SetProcessWorkingSetSize API needs a NT based OS. So people on Win9X can go jump ship. This does however open up the possibility of using the undocumented NtQuerySystemInformation[2] API. This is only available on Win2K upwards so lets base the process list retrieval on this.

Next getting access to modify the limits on every process running including SYSTEM owned processes. We'll from previous experience we need the SE_DEBUG_NAME privledge to do this.

Now assuming that we want to show the user the improvement that the program has made, we would want to get the previous usage and the new memory usage. NtQuerySystemInformation returns an array of processes and there current information including there memory usage, so that answers the previous usage. But to get the new usage it would be a bit ridiculous to call NtQuerySystemInformation all over again. So we take use of the GetProcessMemoryInfo[3] API this will fill a PROCESS_MEMORY_COUNTERS structure with all the data we need.

In addition to this we might like to give a nice representation of the figures so we need a helper function to convert the byte usage to a humanly readable one. This is quite simple to do, we carry on dividing the number of bytes by 1024 the maximum limit of each format type. When we reach a figure below this then we have the nearest readable figure. We then append the format type.

Now its just a case of wrapping this all up in to a nice GUI. Well mine is just basic, but it does the job. Suggestions have been made that you can display these in a nice colored graph. Well to be honestly I couldn't be bothered as it is not for commercial purposes (Maybe i'll add another article explaining how you do such things).

Explanations of the code

Here's my previous code to enable process privileges. I'm not going to explain every line but we are basically retreaving the current processes security token and updating the privileges associated with it.

Privilege Enabling:

BOOL EnablePriv(LPCSTR lpszPriv)
{
    HANDLE hToken;
    LUID luid;
    TOKEN_PRIVILEGES tkprivs;
    ZeroMemory(&tkprivs, sizeof(tkprivs));
    
    if(!OpenProcessToken(GetCurrentProcess(), (TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY), &hToken))
        return FALSE;
    
    if(!LookupPrivilegeValue(NULL, lpszPriv, &luid)){
        CloseHandle(hToken); return FALSE;
    }
    
    tkprivs.PrivilegeCount = 1;
    tkprivs.Privileges[0].Luid = luid;
    tkprivs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
    
    BOOL bRet = AdjustTokenPrivileges(hToken, FALSE, &tkprivs, sizeof(tkprivs), NULL, NULL);
    CloseHandle(hToken);
    return bRet;
}

Calling the function:
EnablePriv(SE_DEBUG_NAME);
EnablePriv(SE_INC_BASE_PRIORITY_NAME);

The other main functions are below. These are what we caller helper functions they just provide an answer to a question but there is no real logic that affects the program flow.

BOOL FreeProcessMemory(DWORD dwPID)
{
    SIZE_T MinimumWorkingSetSize, MaximumWorkingSetSize;
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_SET_QUOTA, FALSE, dwPID);
    if(hProcess != NULL){
        BOOL bResult = GetProcessWorkingSetSize(hProcess, &MinimumWorkingSetSize, &MaximumWorkingSetSize);
        if(bResult == TRUE) SetProcessWorkingSetSize(hProcess, -1, -1);
        CloseHandle(hProcess);
        return bResult;
    }
    return FALSE;
}

SIZE_T GetProccessUsage(DWORD dwPID)
{
    PROCESS_MEMORY_COUNTERS pmcInfo;
    ZeroMemory(&pmcInfo, sizeof(pmcInfo));
    HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwPID);
    if(hProcess != NULL){
        GetProcessMemoryInfo(hProcess, &pmcInfo, sizeof(pmcInfo));
        CloseHandle(hProcess);
    }
    return pmcInfo.WorkingSetSize;
}

LPSTR HumanizeBytes(DWORD dwBytes)
{
    LPSTR szTemp = new CHAR[32];
    LPSTR szType[] = { "B","Kb","Mb","Gb","Tb","Pb","Eb","Zb","Yb" };
    int i = 0;
    float fBytes = (float)dwBytes;
    while(fBytes >= 1024){ fBytes /= 1024; i++; }
    wsprintf(szTemp, "%d%s", (int)fBytes, szType[i]);
    return szTemp;
}


To be continued...


Copyright © Netcore2K.net.
All rights reserved.
Contact Us