//------------------------------------------------------------------------
// PtInRect Logger
//
// * User-friendly GUI that logs addresses that call PtInRect and allows
// the user to filter out specific calls.
// * Hopefully thread-safe
// Coded by Franc[e]sco
//------------------------------------------------------------------------
// 04/02/2012 - rev1
// [*] First Revision
//------------------------------------------------------------------------
#include "GUI.h"
#include "resource.h"
#include "PtInRectLog.hpp"
#include <CommCtrl.h>
#include <algorithm>
#include <list>
#include <sstream>
#pragma region STRUCTS_AND_MACROS
struct CALL_ITEM
{
LPVOID lpvAddress;
int index;
};
extern HWND hDialog;
extern BOOL PIRLOGAPI SendDlgItemMessage(UINT uItem, UINT uMessage, WPARAM wParam, LPARAM lParam);
#define PERROR(szMessage) MessageBoxW(hDialog ? hDialog : NULL, szMessage, L"PtInRectLog", 0);
#define PDIE() TerminateProcess(GetCurrentProcess(), 0);
BOOL IS_CHECKED(UINT uCheckBox) { return (SendDlgItemMessage(uCheckBox, BM_GETCHECK, 0, 0) == BST_CHECKED); };
DWORD wshextoi(LPWSTR lpsz)
{
DWORD res = 0;
wstringstream ss(lpsz);
ss << hex << lpsz;
ss >> res;
return res;
}
#pragma endregion
HWND hDialog = NULL;
HINSTANCE hInstance = NULL;
list<CALL_ITEM *> lvItems;
HANDLE Mutex = NULL;
extern CPtInRectLog *pLogger;
LPCWSTR LV_COLUMN_TEXT = L"Return Address";
LPCWSTR APP_VERSION = L"PtInRectLog rev1 (04/02/2012)\nCoded by Franc[e]sco";
BOOL PIRLOGAPI SendMessage(UINT uMessage, WPARAM wParam, LPARAM lParam)
{
return SendMessage(hDialog, uMessage, wParam, lParam);
}
BOOL PIRLOGAPI SendDlgItemMessage(UINT uItem, UINT uMessage, WPARAM wParam, LPARAM lParam)
{
return SendDlgItemMessage(hDialog, uItem, uMessage, wParam, lParam);
}
VOID PIRLOGAPI OnCreate(HWND hDialog)
{
HFONT hFont = NULL;
HICON hIcon = NULL;
LVCOLUMN Column = {0};
::hDialog = hDialog;
Column.pszText = const_cast<LPWSTR>(LV_COLUMN_TEXT);
Column.mask = LVCF_TEXT;
Column.fmt = LVCFMT_LEFT;
Column.cx = -1;
Column.cchTextMax = wcslen(LV_COLUMN_TEXT);
hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
hFont = CreateFont(14, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, ANSI_CHARSET,
OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, L"Consolas");
SendMessage(WM_SETICON, ICON_BIG, reinterpret_cast<LPARAM>(hIcon));
SendMessage(WM_SETICON, ICON_SMALL, reinterpret_cast<LPARAM>(hIcon));
SendDlgItemMessage(IDC_LIST1, WM_SETFONT, reinterpret_cast<WPARAM>(hFont), TRUE);
SendDlgItemMessage(IDC_LIST2, WM_SETFONT, reinterpret_cast<WPARAM>(hFont), TRUE);
SendDlgItemMessage(IDC_CHECK1, BM_SETCHECK, BST_CHECKED, 0);
SendDlgItemMessage(IDC_CHECK2, BM_SETCHECK, BST_CHECKED, 0);
SendDlgItemMessage(IDC_CHECK3, BM_SETCHECK, BST_CHECKED, 0);
SendDlgItemMessage(IDC_LIST1, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
SendDlgItemMessage(IDC_LIST1, LVM_INSERTCOLUMN, 0, reinterpret_cast<LPARAM>(&Column));
SendDlgItemMessage(IDC_LIST2, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, LVS_EX_FULLROWSELECT);
SendDlgItemMessage(IDC_LIST2, LVM_INSERTCOLUMN, 0, reinterpret_cast<LPARAM>(&Column));
CPtInRectLog::Hook();
ShowWindow(hDialog, SW_SHOW);
}
VOID PIRLOGAPI OnClose()
{
int mbResult = MessageBox(hDialog, L"Do you also want to terminate the application"
L" this was injected to?", L"PtInRectLog", MB_YESNO | MB_ICONINFORMATION);
DestroyGUI();
if (mbResult == IDYES)
PDIE();
}
VOID PIRLOGAPI About()
{
MessageBox(hDialog, APP_VERSION, L"PtInRectLog", MB_OK | MB_ICONINFORMATION);
}
VOID PIRLOGAPI AddToIgnoreList()
{
WCHAR szItemText[MAX_PATH] = {0};
LVITEM lvItem = {0};
LVITEM lvDeselectItem = {0};
lvDeselectItem.stateMask = LVIF_STATE;
lvDeselectItem.state = 0;
INT iSelected;
if (SendDlgItemMessage(IDC_LIST2, LVM_GETNEXTITEM, -1, LVNI_SELECTED) < 0)
return;
WaitForSingleObject(Mutex, INFINITE);
do
{
iSelected = SendDlgItemMessage(IDC_LIST2, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
if (iSelected >= 0)
{
list<CALL_ITEM *>::iterator it;
SendDlgItemMessage(IDC_LIST2, LVM_SETITEMSTATE, iSelected,
reinterpret_cast<LPARAM>(&lvDeselectItem));
SendDlgItemMessage(IDC_LIST2, LVM_DELETEITEM, iSelected, 0);
for (it = lvItems.begin(); it != lvItems.end() &&
(*it)->index != iSelected; it++);
if (it != lvItems.end())
pLogger->Ignore((*it)->lpvAddress);
else
continue;
for each(CALL_ITEM *Call in lvItems)
if (Call->index > iSelected)
Call->index--;
wsprintf(szItemText, L"0x%.8X", (*it)->lpvAddress);
lvItem.pszText = szItemText;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 0x7FFFFFFF;
SendDlgItemMessage(IDC_LIST1, LVM_INSERTITEM, 0, reinterpret_cast<LPARAM>(&lvItem));
delete *it;
lvItems.erase(it);
}
}
while (iSelected >= 0);
ReleaseMutex(Mutex);
}
VOID PIRLOGAPI RemoveFromIgnoreList()
{
INT iSelected;
DWORD dwAddress = 0;
WCHAR szItemText[MAX_PATH] = {0};
LVITEM lvItem = {0};
WaitForSingleObject(Mutex, INFINITE);
do
{
iSelected = SendDlgItemMessage(IDC_LIST1, LVM_GETNEXTITEM, -1, LVNI_SELECTED);
if (iSelected >= 0)
{
lvItem.pszText = szItemText;
lvItem.cchTextMax = MAX_PATH;
SendDlgItemMessage(IDC_LIST1, LVM_GETITEMTEXT,
iSelected, reinterpret_cast<LPARAM>(&lvItem));
dwAddress = wshextoi(szItemText+2);
SendDlgItemMessage(IDC_LIST1, LVM_DELETEITEM, iSelected, 0);
pLogger->UnIgnore(reinterpret_cast<LPVOID>(dwAddress));
}
}
while (iSelected >= 0);
ReleaseMutex(Mutex);
}
VOID PIRLOGAPI ClearLogList()
{
WaitForSingleObject(Mutex, INFINITE);
SendDlgItemMessage(IDC_LIST2, LVM_DELETEALLITEMS, 0, 0);
lvItems.clear();
pLogger->ResetCallCount();
ReleaseMutex(Mutex);
}
VOID PIRLOGAPI DispatchCall(LPVOID lpvReturnAddress)
{
WCHAR szItemText[MAX_PATH] = {0};
LVITEM lvItem = {0};
list<CALL_ITEM *>::iterator it;
WaitForSingleObject(Mutex, INFINITE);
wsprintf(szItemText, L"0x%.8X (%d)", lpvReturnAddress, pLogger->GetCallCount(lpvReturnAddress));
lvItem.pszText = szItemText;
lvItem.mask = LVIF_TEXT;
lvItem.iItem = 0x7FFFFFFF;
for (it = lvItems.begin(); it != lvItems.end() && (*it)->lpvAddress != lpvReturnAddress; it++);
if (it != lvItems.end())
SendDlgItemMessage(IDC_LIST2, LVM_SETITEMTEXT, (*it)->index, reinterpret_cast<LPARAM>(&lvItem));
else
{
INT Index;
CALL_ITEM *pCallItem = new CALL_ITEM;
pCallItem->lpvAddress = lpvReturnAddress;
Index = SendDlgItemMessage(IDC_LIST2, LVM_INSERTITEM, 0, reinterpret_cast<LPARAM>(&lvItem));
pCallItem->index = Index;
lvItems.push_back(pCallItem);
}
ReleaseMutex(Mutex);
Sleep(1);
}
BOOL CALLBACK DlgProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
{
switch(Message)
{
#pragma region SYSTEM_MESSAGES
case WM_INITDIALOG:
OnCreate(hWnd);
return TRUE;
case WM_CLOSE:
OnClose();
return TRUE;
case WM_DESTROY:
PostQuitMessage(0);
return TRUE;
#pragma endregion
#pragma region CONTROLS
case WM_COMMAND:
switch(LOWORD(wParam))
{
#pragma region BUTTONS
case IDC_BUTTON1:
AddToIgnoreList();
return TRUE;
case IDC_BUTTON2:
RemoveFromIgnoreList();
return TRUE;
case IDC_BUTTON3:
ClearLogList();
return TRUE;
#pragma endregion
#pragma region MENU
case ID_FILE_EXIT:
SendMessage(WM_CLOSE, 0, 0);
return TRUE;
case ID_ABOUT_EXIT:
About();
return TRUE;
#pragma endregion
#pragma region CHECKBOXES
case IDC_CHECK1:
if(IS_CHECKED(IDC_CHECK1))
CPtInRectLog::Hook();
else
CPtInRectLog::UnHook();
return TRUE;
case IDC_CHECK2:
pLogger->ToggleIgnoring(IS_CHECKED(IDC_CHECK2));
return TRUE;
case IDC_CHECK3:
pLogger->ToggleExternCalls(!IS_CHECKED(IDC_CHECK3));
return TRUE;
#pragma endregion
}
break;
#pragma endregion
}
return FALSE;
}
VOID PIRLOGAPI CreateGUI(HINSTANCE hInstance)
{
Mutex = CreateMutex(NULL, FALSE, NULL);
::hInstance = hInstance;
hDialog = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DlgProc);
SendDlgItemMessage(IDC_LIST1, LVM_SETCOLUMNWIDTH, 0, 100);
SendDlgItemMessage(IDC_LIST2, LVM_SETCOLUMNWIDTH, 0, 240);
}
VOID PIRLOGAPI DestroyGUI()
{
for each (CALL_ITEM *pItem in lvItems)
delete pItem;
lvItems.clear();
CloseHandle(Mutex);
DestroyWindow(hDialog);
}
VOID PIRLOGAPI MessageLoop()
{
MSG msg;
INT status;
while ((status = GetMessage(&msg, 0, 0, 0)) != 0)
{
if (status == -1)
return;
if (!IsDialogMessage(hDialog, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
Ly8tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KLy8gUHRJblJlY3QgTG9nZ2VyCi8vCi8vICogVXNlci1mcmllbmRseSBHVUkgdGhhdCBsb2dzIGFkZHJlc3NlcyB0aGF0IGNhbGwgUHRJblJlY3QgYW5kIGFsbG93cyAKLy8gICB0aGUgdXNlciB0byBmaWx0ZXIgb3V0IHNwZWNpZmljIGNhbGxzLgovLyAqIEhvcGVmdWxseSB0aHJlYWQtc2FmZQovLyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvZGVkIGJ5IEZyYW5jW2Vdc2NvCi8vLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCi8vIDA0LzAyLzIwMTIgLSByZXYxCi8vIFsqXSBGaXJzdCBSZXZpc2lvbgovLy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKI2luY2x1ZGUgIkdVSS5oIgojaW5jbHVkZSAicmVzb3VyY2UuaCIKI2luY2x1ZGUgIlB0SW5SZWN0TG9nLmhwcCIKI2luY2x1ZGUgPENvbW1DdHJsLmg+CiNpbmNsdWRlIDxhbGdvcml0aG0+CiNpbmNsdWRlIDxsaXN0PgojaW5jbHVkZSA8c3N0cmVhbT4KCiNwcmFnbWEgcmVnaW9uIFNUUlVDVFNfQU5EX01BQ1JPUwpzdHJ1Y3QgQ0FMTF9JVEVNCnsKCUxQVk9JRCBscHZBZGRyZXNzOwoJaW50IGluZGV4Owp9OwoKZXh0ZXJuIEhXTkQgaERpYWxvZzsKZXh0ZXJuIEJPT0wgUElSTE9HQVBJIFNlbmREbGdJdGVtTWVzc2FnZShVSU5UIHVJdGVtLCBVSU5UIHVNZXNzYWdlLCBXUEFSQU0gd1BhcmFtLCBMUEFSQU0gbFBhcmFtKTsKI2RlZmluZSBQRVJST1Ioc3pNZXNzYWdlKSBNZXNzYWdlQm94VyhoRGlhbG9nID8gaERpYWxvZyA6IE5VTEwsIHN6TWVzc2FnZSwgTCJQdEluUmVjdExvZyIsIDApOwojZGVmaW5lIFBESUUoKSBUZXJtaW5hdGVQcm9jZXNzKEdldEN1cnJlbnRQcm9jZXNzKCksIDApOwpCT09MIElTX0NIRUNLRUQoVUlOVCB1Q2hlY2tCb3gpIHsgcmV0dXJuIChTZW5kRGxnSXRlbU1lc3NhZ2UodUNoZWNrQm94LCBCTV9HRVRDSEVDSywgMCwgMCkgPT0gQlNUX0NIRUNLRUQpOyB9OwoKRFdPUkQgd3NoZXh0b2koTFBXU1RSIGxwc3opCnsKCURXT1JEIHJlcyA9IDA7Cgl3c3RyaW5nc3RyZWFtIHNzKGxwc3opOwoJc3MgPDwgaGV4IDw8IGxwc3o7CglzcyA+PiByZXM7CglyZXR1cm4gcmVzOwp9CiNwcmFnbWEgZW5kcmVnaW9uCgpIV05EIGhEaWFsb2cgPSBOVUxMOwpISU5TVEFOQ0UgaEluc3RhbmNlID0gTlVMTDsKbGlzdDxDQUxMX0lURU0gKj4gbHZJdGVtczsKSEFORExFIE11dGV4ID0gTlVMTDsKZXh0ZXJuIENQdEluUmVjdExvZyAqcExvZ2dlcjsKCkxQQ1dTVFIgTFZfQ09MVU1OX1RFWFQgPSBMIlJldHVybiBBZGRyZXNzIjsKTFBDV1NUUiBBUFBfVkVSU0lPTiA9IEwiUHRJblJlY3RMb2cgcmV2MSAoMDQvMDIvMjAxMilcbkNvZGVkIGJ5IEZyYW5jW2Vdc2NvIjsKCkJPT0wgUElSTE9HQVBJIFNlbmRNZXNzYWdlKFVJTlQgdU1lc3NhZ2UsIFdQQVJBTSB3UGFyYW0sIExQQVJBTSBsUGFyYW0pCnsKCXJldHVybiBTZW5kTWVzc2FnZShoRGlhbG9nLCB1TWVzc2FnZSwgd1BhcmFtLCBsUGFyYW0pOwp9CgpCT09MIFBJUkxPR0FQSSBTZW5kRGxnSXRlbU1lc3NhZ2UoVUlOVCB1SXRlbSwgVUlOVCB1TWVzc2FnZSwgV1BBUkFNIHdQYXJhbSwgTFBBUkFNIGxQYXJhbSkKewoJcmV0dXJuIFNlbmREbGdJdGVtTWVzc2FnZShoRGlhbG9nLCB1SXRlbSwgdU1lc3NhZ2UsIHdQYXJhbSwgbFBhcmFtKTsKfQoKVk9JRCBQSVJMT0dBUEkgT25DcmVhdGUoSFdORCBoRGlhbG9nKQp7CglIRk9OVCBoRm9udCA9IE5VTEw7CglISUNPTiBoSWNvbiA9IE5VTEw7CglMVkNPTFVNTiBDb2x1bW4gPSB7MH07CgoJOjpoRGlhbG9nID0gaERpYWxvZzsKCglDb2x1bW4ucHN6VGV4dCA9IGNvbnN0X2Nhc3Q8TFBXU1RSPihMVl9DT0xVTU5fVEVYVCk7CglDb2x1bW4ubWFzayA9IExWQ0ZfVEVYVDsKCUNvbHVtbi5mbXQgPSBMVkNGTVRfTEVGVDsKCUNvbHVtbi5jeCA9IC0xOwoJQ29sdW1uLmNjaFRleHRNYXggPSB3Y3NsZW4oTFZfQ09MVU1OX1RFWFQpOwoKCWhJY29uID0gTG9hZEljb24oaEluc3RhbmNlLCBNQUtFSU5UUkVTT1VSQ0UoSURJX0lDT04xKSk7CgloRm9udCA9IENyZWF0ZUZvbnQoMTQsIDAsIDAsIDAsIEZXX0RPTlRDQVJFLCBGQUxTRSwgRkFMU0UsIEZBTFNFLCBBTlNJX0NIQVJTRVQsIAoJCU9VVF9UVF9QUkVDSVMsIENMSVBfREVGQVVMVF9QUkVDSVMsIENMRUFSVFlQRV9RVUFMSVRZLCBERUZBVUxUX1BJVENILCBMIkNvbnNvbGFzIik7CgoJU2VuZE1lc3NhZ2UoV01fU0VUSUNPTiwgSUNPTl9CSUcsIHJlaW50ZXJwcmV0X2Nhc3Q8TFBBUkFNPihoSWNvbikpOwoJU2VuZE1lc3NhZ2UoV01fU0VUSUNPTiwgSUNPTl9TTUFMTCwgcmVpbnRlcnByZXRfY2FzdDxMUEFSQU0+KGhJY29uKSk7CglTZW5kRGxnSXRlbU1lc3NhZ2UoSURDX0xJU1QxLCBXTV9TRVRGT05ULCByZWludGVycHJldF9jYXN0PFdQQVJBTT4oaEZvbnQpLCBUUlVFKTsKCVNlbmREbGdJdGVtTWVzc2FnZShJRENfTElTVDIsIFdNX1NFVEZPTlQsIHJlaW50ZXJwcmV0X2Nhc3Q8V1BBUkFNPihoRm9udCksIFRSVUUpOwoJU2VuZERsZ0l0ZW1NZXNzYWdlKElEQ19DSEVDSzEsIEJNX1NFVENIRUNLLCBCU1RfQ0hFQ0tFRCwgMCk7CglTZW5kRGxnSXRlbU1lc3NhZ2UoSURDX0NIRUNLMiwgQk1fU0VUQ0hFQ0ssIEJTVF9DSEVDS0VELCAwKTsKCVNlbmREbGdJdGVtTWVzc2FnZShJRENfQ0hFQ0szLCBCTV9TRVRDSEVDSywgQlNUX0NIRUNLRUQsIDApOwoJU2VuZERsZ0l0ZW1NZXNzYWdlKElEQ19MSVNUMSwgTFZNX1NFVEVYVEVOREVETElTVFZJRVdTVFlMRSwgMCwgTFZTX0VYX0ZVTExST1dTRUxFQ1QpOwoJU2VuZERsZ0l0ZW1NZXNzYWdlKElEQ19MSVNUMSwgTFZNX0lOU0VSVENPTFVNTiwgMCwgcmVpbnRlcnByZXRfY2FzdDxMUEFSQU0+KCZDb2x1bW4pKTsKCVNlbmREbGdJdGVtTWVzc2FnZShJRENfTElTVDIsIExWTV9TRVRFWFRFTkRFRExJU1RWSUVXU1RZTEUsIDAsIExWU19FWF9GVUxMUk9XU0VMRUNUKTsKCVNlbmREbGdJdGVtTWVzc2FnZShJRENfTElTVDIsIExWTV9JTlNFUlRDT0xVTU4sIDAsIHJlaW50ZXJwcmV0X2Nhc3Q8TFBBUkFNPigmQ29sdW1uKSk7CgoJQ1B0SW5SZWN0TG9nOjpIb29rKCk7CglTaG93V2luZG93KGhEaWFsb2csIFNXX1NIT1cpOwp9CgpWT0lEIFBJUkxPR0FQSSBPbkNsb3NlKCkKewoJaW50IG1iUmVzdWx0ID0gTWVzc2FnZUJveChoRGlhbG9nLCBMIkRvIHlvdSBhbHNvIHdhbnQgdG8gdGVybWluYXRlIHRoZSBhcHBsaWNhdGlvbiIgCgkJTCIgdGhpcyB3YXMgaW5qZWN0ZWQgdG8/IiwgTCJQdEluUmVjdExvZyIsIE1CX1lFU05PIHwgTUJfSUNPTklORk9STUFUSU9OKTsKCglEZXN0cm95R1VJKCk7CgoJaWYgKG1iUmVzdWx0ID09IElEWUVTKQoJCVBESUUoKTsKfQoKVk9JRCBQSVJMT0dBUEkgQWJvdXQoKQp7CglNZXNzYWdlQm94KGhEaWFsb2csIEFQUF9WRVJTSU9OLCBMIlB0SW5SZWN0TG9nIiwgTUJfT0sgfCBNQl9JQ09OSU5GT1JNQVRJT04pOwp9CgpWT0lEIFBJUkxPR0FQSSBBZGRUb0lnbm9yZUxpc3QoKQp7CglXQ0hBUiBzekl0ZW1UZXh0W01BWF9QQVRIXSA9IHswfTsKCUxWSVRFTSBsdkl0ZW0gPSB7MH07CglMVklURU0gbHZEZXNlbGVjdEl0ZW0gPSB7MH07CglsdkRlc2VsZWN0SXRlbS5zdGF0ZU1hc2sgPSBMVklGX1NUQVRFOwoJbHZEZXNlbGVjdEl0ZW0uc3RhdGUgPSAwOwoJSU5UIGlTZWxlY3RlZDsKCglpZiAoU2VuZERsZ0l0ZW1NZXNzYWdlKElEQ19MSVNUMiwgTFZNX0dFVE5FWFRJVEVNLCAtMSwgTFZOSV9TRUxFQ1RFRCkgPCAwKQoJCXJldHVybjsKCglXYWl0Rm9yU2luZ2xlT2JqZWN0KE11dGV4LCBJTkZJTklURSk7CglkbwoJewoJCWlTZWxlY3RlZCA9IFNlbmREbGdJdGVtTWVzc2FnZShJRENfTElTVDIsIExWTV9HRVRORVhUSVRFTSwgLTEsIExWTklfU0VMRUNURUQpOwoJCQoJCWlmIChpU2VsZWN0ZWQgPj0gMCkKCQl7CgkJCWxpc3Q8Q0FMTF9JVEVNICo+OjppdGVyYXRvciBpdDsKCQkJU2VuZERsZ0l0ZW1NZXNzYWdlKElEQ19MSVNUMiwgTFZNX1NFVElURU1TVEFURSwgaVNlbGVjdGVkLCAKCQkJCXJlaW50ZXJwcmV0X2Nhc3Q8TFBBUkFNPigmbHZEZXNlbGVjdEl0ZW0pKTsKCQkJU2VuZERsZ0l0ZW1NZXNzYWdlKElEQ19MSVNUMiwgTFZNX0RFTEVURUlURU0sIGlTZWxlY3RlZCwgMCk7CgoJCQlmb3IgKGl0ID0gbHZJdGVtcy5iZWdpbigpOyBpdCAhPSBsdkl0ZW1zLmVuZCgpICYmIAoJCQkJKCppdCktPmluZGV4ICE9IGlTZWxlY3RlZDsgaXQrKyk7CgoJCQlpZiAoaXQgIT0gbHZJdGVtcy5lbmQoKSkKCQkJCXBMb2dnZXItPklnbm9yZSgoKml0KS0+bHB2QWRkcmVzcyk7CgkJCWVsc2UKCQkJCWNvbnRpbnVlOwoKCQkJZm9yIGVhY2goQ0FMTF9JVEVNICpDYWxsIGluIGx2SXRlbXMpCgkJCQlpZiAoQ2FsbC0+aW5kZXggPiBpU2VsZWN0ZWQpCgkJCQkJQ2FsbC0+aW5kZXgtLTsKCgkJCXdzcHJpbnRmKHN6SXRlbVRleHQsIEwiMHglLjhYIiwgKCppdCktPmxwdkFkZHJlc3MpOwoJCQlsdkl0ZW0ucHN6VGV4dCA9IHN6SXRlbVRleHQ7CgkJCWx2SXRlbS5tYXNrID0gTFZJRl9URVhUOwoJCQlsdkl0ZW0uaUl0ZW0gPSAweDdGRkZGRkZGOwoJCQlTZW5kRGxnSXRlbU1lc3NhZ2UoSURDX0xJU1QxLCBMVk1fSU5TRVJUSVRFTSwgMCwgcmVpbnRlcnByZXRfY2FzdDxMUEFSQU0+KCZsdkl0ZW0pKTsKCgkJCWRlbGV0ZSAqaXQ7CgkJCWx2SXRlbXMuZXJhc2UoaXQpOwoJCX0KCX0KCXdoaWxlIChpU2VsZWN0ZWQgPj0gMCk7CglSZWxlYXNlTXV0ZXgoTXV0ZXgpOwp9CgpWT0lEIFBJUkxPR0FQSSBSZW1vdmVGcm9tSWdub3JlTGlzdCgpCnsKCUlOVCBpU2VsZWN0ZWQ7CglEV09SRCBkd0FkZHJlc3MgPSAwOwoJV0NIQVIgc3pJdGVtVGV4dFtNQVhfUEFUSF0gPSB7MH07CglMVklURU0gbHZJdGVtID0gezB9OwoKCVdhaXRGb3JTaW5nbGVPYmplY3QoTXV0ZXgsIElORklOSVRFKTsKCWRvCgl7CgkJaVNlbGVjdGVkID0gU2VuZERsZ0l0ZW1NZXNzYWdlKElEQ19MSVNUMSwgTFZNX0dFVE5FWFRJVEVNLCAtMSwgTFZOSV9TRUxFQ1RFRCk7CgkJCgkJaWYgKGlTZWxlY3RlZCA+PSAwKQoJCXsKCQkJbHZJdGVtLnBzelRleHQgPSBzekl0ZW1UZXh0OwoJCQlsdkl0ZW0uY2NoVGV4dE1heCA9IE1BWF9QQVRIOwoJCQlTZW5kRGxnSXRlbU1lc3NhZ2UoSURDX0xJU1QxLCBMVk1fR0VUSVRFTVRFWFQsIAoJCQkJaVNlbGVjdGVkLCByZWludGVycHJldF9jYXN0PExQQVJBTT4oJmx2SXRlbSkpOwoJCQlkd0FkZHJlc3MgPSB3c2hleHRvaShzekl0ZW1UZXh0KzIpOwoJCQlTZW5kRGxnSXRlbU1lc3NhZ2UoSURDX0xJU1QxLCBMVk1fREVMRVRFSVRFTSwgaVNlbGVjdGVkLCAwKTsKCQkJcExvZ2dlci0+VW5JZ25vcmUocmVpbnRlcnByZXRfY2FzdDxMUFZPSUQ+KGR3QWRkcmVzcykpOwoJCX0KCX0KCXdoaWxlIChpU2VsZWN0ZWQgPj0gMCk7CglSZWxlYXNlTXV0ZXgoTXV0ZXgpOwp9CgpWT0lEIFBJUkxPR0FQSSBDbGVhckxvZ0xpc3QoKQp7CglXYWl0Rm9yU2luZ2xlT2JqZWN0KE11dGV4LCBJTkZJTklURSk7CglTZW5kRGxnSXRlbU1lc3NhZ2UoSURDX0xJU1QyLCBMVk1fREVMRVRFQUxMSVRFTVMsIDAsIDApOwoJbHZJdGVtcy5jbGVhcigpOwoJcExvZ2dlci0+UmVzZXRDYWxsQ291bnQoKTsKCVJlbGVhc2VNdXRleChNdXRleCk7Cn0KClZPSUQgUElSTE9HQVBJIERpc3BhdGNoQ2FsbChMUFZPSUQgbHB2UmV0dXJuQWRkcmVzcykKewoJV0NIQVIgc3pJdGVtVGV4dFtNQVhfUEFUSF0gPSB7MH07CglMVklURU0gbHZJdGVtID0gezB9OwoJbGlzdDxDQUxMX0lURU0gKj46Oml0ZXJhdG9yIGl0OwoKCVdhaXRGb3JTaW5nbGVPYmplY3QoTXV0ZXgsIElORklOSVRFKTsKCXdzcHJpbnRmKHN6SXRlbVRleHQsIEwiMHglLjhYICglZCkiLCBscHZSZXR1cm5BZGRyZXNzLCBwTG9nZ2VyLT5HZXRDYWxsQ291bnQobHB2UmV0dXJuQWRkcmVzcykpOwoJbHZJdGVtLnBzelRleHQgPSBzekl0ZW1UZXh0OwoJbHZJdGVtLm1hc2sgPSBMVklGX1RFWFQ7Cglsdkl0ZW0uaUl0ZW0gPSAweDdGRkZGRkZGOwoJCglmb3IgKGl0ID0gbHZJdGVtcy5iZWdpbigpOyBpdCAhPSBsdkl0ZW1zLmVuZCgpICYmICgqaXQpLT5scHZBZGRyZXNzICE9IGxwdlJldHVybkFkZHJlc3M7IGl0KyspOwoKCWlmIChpdCAhPSBsdkl0ZW1zLmVuZCgpKQoJCVNlbmREbGdJdGVtTWVzc2FnZShJRENfTElTVDIsIExWTV9TRVRJVEVNVEVYVCwgKCppdCktPmluZGV4LCByZWludGVycHJldF9jYXN0PExQQVJBTT4oJmx2SXRlbSkpOwoJZWxzZQoJewoJCUlOVCBJbmRleDsKCQlDQUxMX0lURU0gKnBDYWxsSXRlbSA9IG5ldyBDQUxMX0lURU07CgkJcENhbGxJdGVtLT5scHZBZGRyZXNzID0gbHB2UmV0dXJuQWRkcmVzczsKCQkKCQlJbmRleCA9IFNlbmREbGdJdGVtTWVzc2FnZShJRENfTElTVDIsIExWTV9JTlNFUlRJVEVNLCAwLCByZWludGVycHJldF9jYXN0PExQQVJBTT4oJmx2SXRlbSkpOwoJCXBDYWxsSXRlbS0+aW5kZXggPSBJbmRleDsKCQlsdkl0ZW1zLnB1c2hfYmFjayhwQ2FsbEl0ZW0pOwoJfQoJUmVsZWFzZU11dGV4KE11dGV4KTsKCVNsZWVwKDEpOwp9CgpCT09MIENBTExCQUNLIERsZ1Byb2MoSFdORCBoV25kLCBVSU5UIE1lc3NhZ2UsIFdQQVJBTSB3UGFyYW0sIExQQVJBTSBsUGFyYW0pCnsKCXN3aXRjaChNZXNzYWdlKQoJewojcHJhZ21hIHJlZ2lvbiBTWVNURU1fTUVTU0FHRVMKCWNhc2UgV01fSU5JVERJQUxPRzoKCQlPbkNyZWF0ZShoV25kKTsKCQlyZXR1cm4gVFJVRTsKCgljYXNlIFdNX0NMT1NFOgoJCU9uQ2xvc2UoKTsKCQlyZXR1cm4gVFJVRTsKCgljYXNlIFdNX0RFU1RST1k6CgkJUG9zdFF1aXRNZXNzYWdlKDApOwoJCXJldHVybiBUUlVFOwojcHJhZ21hIGVuZHJlZ2lvbgoKI3ByYWdtYSByZWdpb24gQ09OVFJPTFMKCWNhc2UgV01fQ09NTUFORDoKCQlzd2l0Y2goTE9XT1JEKHdQYXJhbSkpCgkJewojcHJhZ21hIHJlZ2lvbiBCVVRUT05TCgkJY2FzZSBJRENfQlVUVE9OMToKCQkJQWRkVG9JZ25vcmVMaXN0KCk7CgkJCXJldHVybiBUUlVFOwoKCQljYXNlIElEQ19CVVRUT04yOgoJCQlSZW1vdmVGcm9tSWdub3JlTGlzdCgpOwoJCQlyZXR1cm4gVFJVRTsKCgkJY2FzZSBJRENfQlVUVE9OMzoKCQkJQ2xlYXJMb2dMaXN0KCk7CgkJCXJldHVybiBUUlVFOwojcHJhZ21hIGVuZHJlZ2lvbgoKI3ByYWdtYSByZWdpb24gTUVOVQoJCWNhc2UgSURfRklMRV9FWElUOgoJCQlTZW5kTWVzc2FnZShXTV9DTE9TRSwgMCwgMCk7CgkJCXJldHVybiBUUlVFOwoKCQljYXNlIElEX0FCT1VUX0VYSVQ6CgkJCUFib3V0KCk7CgkJCXJldHVybiBUUlVFOwojcHJhZ21hIGVuZHJlZ2lvbgoKI3ByYWdtYSByZWdpb24gQ0hFQ0tCT1hFUwoJCWNhc2UgSURDX0NIRUNLMToKCQkJaWYoSVNfQ0hFQ0tFRChJRENfQ0hFQ0sxKSkKCQkJCUNQdEluUmVjdExvZzo6SG9vaygpOwoJCQllbHNlCgkJCQlDUHRJblJlY3RMb2c6OlVuSG9vaygpOwoJCQlyZXR1cm4gVFJVRTsKCgkJY2FzZSBJRENfQ0hFQ0syOgoJCQlwTG9nZ2VyLT5Ub2dnbGVJZ25vcmluZyhJU19DSEVDS0VEKElEQ19DSEVDSzIpKTsKCQkJcmV0dXJuIFRSVUU7CgoJCWNhc2UgSURDX0NIRUNLMzoKCQkJcExvZ2dlci0+VG9nZ2xlRXh0ZXJuQ2FsbHMoIUlTX0NIRUNLRUQoSURDX0NIRUNLMykpOwoJCQlyZXR1cm4gVFJVRTsKI3ByYWdtYSBlbmRyZWdpb24KCQl9CgkJYnJlYWs7CiNwcmFnbWEgZW5kcmVnaW9uCgl9CgoJcmV0dXJuIEZBTFNFOwp9CgpWT0lEIFBJUkxPR0FQSSBDcmVhdGVHVUkoSElOU1RBTkNFIGhJbnN0YW5jZSkKewoJTXV0ZXggPSBDcmVhdGVNdXRleChOVUxMLCBGQUxTRSwgTlVMTCk7Cgk6OmhJbnN0YW5jZSA9IGhJbnN0YW5jZTsKCWhEaWFsb2cgPSBDcmVhdGVEaWFsb2coaEluc3RhbmNlLCBNQUtFSU5UUkVTT1VSQ0UoSUREX0RJQUxPRzEpLCBOVUxMLCBEbGdQcm9jKTsKCVNlbmREbGdJdGVtTWVzc2FnZShJRENfTElTVDEsIExWTV9TRVRDT0xVTU5XSURUSCwgMCwgMTAwKTsKCVNlbmREbGdJdGVtTWVzc2FnZShJRENfTElTVDIsIExWTV9TRVRDT0xVTU5XSURUSCwgMCwgMjQwKTsKfQoKVk9JRCBQSVJMT0dBUEkgRGVzdHJveUdVSSgpCnsKCWZvciBlYWNoIChDQUxMX0lURU0gKnBJdGVtIGluIGx2SXRlbXMpCgkJZGVsZXRlIHBJdGVtOwoKCWx2SXRlbXMuY2xlYXIoKTsKCUNsb3NlSGFuZGxlKE11dGV4KTsKCURlc3Ryb3lXaW5kb3coaERpYWxvZyk7Cn0KClZPSUQgUElSTE9HQVBJIE1lc3NhZ2VMb29wKCkKewogICAgTVNHIG1zZzsKCUlOVCBzdGF0dXM7CgogICAgd2hpbGUgKChzdGF0dXMgPSBHZXRNZXNzYWdlKCZtc2csIDAsIDAsIDApKSAhPSAwKQogICAgewogICAgICAgIGlmIChzdGF0dXMgPT0gLTEpCiAgICAgICAgICAgIHJldHVybjsKICAgICAgICBpZiAoIUlzRGlhbG9nTWVzc2FnZShoRGlhbG9nLCAmbXNnKSkKICAgICAgICB7CiAgICAgICAgICAgIFRyYW5zbGF0ZU1lc3NhZ2UoJm1zZyk7CiAgICAgICAgICAgIERpc3BhdGNoTWVzc2FnZSgmbXNnKTsKICAgICAgICB9CiAgICB9Cn0K