Wednesday, December 31, 2008

Change the default zoom mode in Google Chromium

As a WebKit based browser, Google Chrome use the text zoom mode as default zoom mode. It means, if you choose the Zoom -> Larger in the page context menu, or press the Ctrl + '+' accelerator, all the text font size in the page will be increased by 20%. On the other hand, if you do the same thing in FireFox, the whole page will be zoomed instead the text font size.

In my opinion, I feel the page zoom mode more comfortable, although I don't think any of those mode is better than the other. At least in my 24' LCD with 1080p, I need the page zoom width enough to fill the right space.

So, I just modified Chrome source code, add two alternative accelerators for page zoom mode; I also add a command line switch to set the default zoom mode when starting.

The changes are straight, because WebKit had provided the build-in support for page zoom mode, and Chrome also had an internal switch for zoom mode.

Following steps are the changes base on latest SVN revision.

  1. Add two new command definition in the \app\chrome_dll_resource.h file, which define the alternative zoom command itself.

    #define IDC_ZOOM_VIEW_PLUS 38004
    #define IDC_ZOOM_VIEW_MINUS 38005

    And add new accelerator like Ctrl + Alt + '+'/'-' in the IDR_MAINFRAME table (app\chrom_dll.rc)

    VK_ADD,              IDC_ZOOM_VIEW_PLUS, VIRTKEY, CONTROL, ALT
    VK_SUBTRACT,         IDC_ZOOM_VIEW_MINUS, VIRTKEY, CONTROL, ALT



  2. Handle the commands in the Browser::ExecuteCommand function (\browser\browser.cc), and add a parameter for ZoomIn/ZoomOut functions which is the zoom mode

    // Zoom
    case IDC_ZOOM_PLUS: ZoomIn(true); break;
    case IDC_ZOOM_NORMAL: ZoomReset(); break;
    case IDC_ZOOM_MINUS: ZoomOut(true); break;
    case IDC_ZOOM_VIEW_PLUS: ZoomIn(false); break;
    case IDC_ZOOM_VIEW_MINUS: ZoomOut(false); break;
  3. Add two new zoom mode in PageZoom (\common\page_zoom.h), which will be used for page zoom; and refactor ZoomIn/ZoomOut functions (\browser\browser.cc) for new modes.

    enum Function {
    VIEW_SMALLER = -2,
    SMALLER = -1,
    STANDARD = 0,
    LARGER = 1,
    VIEW_LARGER = 2, };

    void Browser::ZoomIn(bool isTextOnly) {
    UserMetrics::RecordAction(L"ZoomPlus", profile_);
    GetSelectedTabContents()->AsWebContents()->render_view_host()->Zoom( isTextOnly ? PageZoom::LARGER : PageZoom::VIEW_LARGER);}

    void Browser::ZoomOut(bool isTextOnly) {
    UserMetrics::RecordAction(L"ZoomMinus", profile_);
    GetSelectedTabContents()->AsWebContents()->render_view_host()->Zoom( isTextOnly ? PageZoom::SMALLER : PageZoom::VIEW_SMALLER);}

    Then, the RenderViewHost::Zoom function will wrap the zoom mode as a ViewMsg_Zoom message, and send it to the render process.
  4. Modify the render handler, which redirect ViewMsg_Zoom message to OnZoom function (\renderer\render_view.cc)

    IPC_MESSAGE_HANDLER(ViewMsg_Zoom, OnZoom)

    The OnZoom function has a hard code kZoomIsTextOnly constant. So, we just change its value base on the command line parameter; and set two kind of zoom mode base on its default setting.

    void RenderView::OnZoom(int function) {
    static const bool zoomIsTextOnly = CommandLine().HasSwitch(switches::kZoomIsTextOnly);

    switch (function) {
    case PageZoom::VIEW_SMALLER:
    webview()->ZoomOut(!zoomIsTextOnly); break;
    case PageZoom::SMALLER: webview()->ZoomOut(zoomIsTextOnly); break;
    case PageZoom::STANDARD: webview()->ResetZoom(); break;
    case PageZoom::LARGER: webview()->ZoomIn(zoomIsTextOnly); break;
    case PageZoom::VIEW_LARGER: webview()->ZoomIn(!zoomIsTextOnly); break;
    default: NOTREACHED(); }}
  5. Besides the command workflow, there are some bookkeeping works.

    We need update the command status base on the current stage in the Browser::UpdateCommandsForTabState function (browser\browser.cc)

    controller_.UpdateCommandEnabled(IDC_ZOOM_VIEW_PLUS, is_web_contents);
    controller_.UpdateCommandEnabled(IDC_ZOOM_VIEW_MINUS, is_web_contents);
  6. To change the default zoom mode, I added a new switch for command line in \common\chrome_switches.h/.cc

    // Turns on text only mode zoom supportconst wchar_t
    kZoomIsTextOnly[] = L"zoom-is-text-only";

    and add it into the switch_names array in RenderProcessHost::Init function (browser\render_process_host.cc). So, the browser process will propagate the switch to the child render process.

    static const wchar_t* const switch_names[] = {
    //...
    switches::kZoomIsTextOnly, };

Besides, I found a similar feature request has been submited to the chrome issues tracking. So, I will add some comment as patch for this issue. Wish Google could merge it into next offical release :)


Wednesday, October 15, 2008

Use 3rd party font render engine in Google Chromium

    As you know, there are some 3rd party font engines, such as GDI++, which could hook Windows font related APIs, and render font more smoother. 
    In my opinion, that engine seems better than 
ClearType or other build-in Windows font engine. 

    For Chromium, it use multi-processes architecture, a standalone renderer process will render the page in sandbox. It means that we should know which process need be hooked for font smoother. 




    So I made some slight changes in Chromium to support GDI++ 

    1. a new command line parameter "--use-gdipp=", which give a setting file for GDI++ 

    I add a new key name kUseGdiPP (chrome_switches.h|.cc), which could be used to parse command line parameters.

const wchar_t kUseGdiPP[] = L"use-gdipp";

    And also add it in switch_names array (render_process_host.cc), that control the main browser process propagate which switches to renderer child process.

    2. the initialize and finalize code in RendererMain function, which load and free the font engine 

    After the renderer process started, it call RendererMain function (renderer_main.cc) to initilize process. So I add the font engine related code just after HandleRendererErrorTestParameters calling. Because that function could help us debugging the children process.

  bool use_gdipp = parsed_command_line.HasSwitch(switches::kUseGdiPP);

  if (use_gdipp)
  {
    std::wstring setting_file_name = 
      parsed_command_line.GetSwitchValue(switches::kUseGdiPP);

    if (!LoadFontEngine(setting_file_name.c_str()))
    {
      LOG(WARNING) << "Fail to load GDI++ font engine.";

      use_gdipp = false;
    }
    else
    {
      DLOG(INFO) << "Loaded GDI++ font engine.";
    }
  }

    We can use "--renderer-startup-dialog" parameter to popup a message box before any other initialize code. And we could attach debugger to renderer process for troubeshooting. Besides, this parameter must be used with "--no-sandbox" parameter. Please check Debugging Chromium for more detail.

     On the other hand, we also free the font engine before uninitialize COM with CoUninitialize function.

  if (use_gdipp)
  {
    FreeFontEngine();
  }

    3. a customized GDI++ library which will be linked as static library in chrome.dll library 

    To compile the GDI++ library, you should download the latest FreeType version GDI++, with FreeType and Detours.
    And create a static library project in chrome solution, which should add all the source files in GDI++\src folder except run.cpp.

    Than, you should do some dirty works to make it compilable, remove some function definition and includes.
    Two funtions LoadFontEngine and FreeFontEngine should be added in hook.cpp as wrapper, and a new header file should be created to declare those functions.


bool LoadFontEngine(LPCTSTR lpszFile)
{
  hook_initinternal();

  CCriticalSectionLock::Init();

  if (!g_TLInfo.ProcessInit()) 
  {
    return false;
  }
  
  CGdippSettings* pSettings = CGdippSettings::CreateInstance();
    
  if (!pSettings || !pSettings->LoadAppSettings(lpszFile)) 
  {
    CGdippSettings::DestroyInstance();
    
    return false;
  }
  
  if (!FontLInit()) 
  {
    return false;
  }
  
  g_pFTEngine = new FreeTypeFontEngine;

  if (!g_pFTEngine) 
  {
    return false;
  }

  if (!InterlockedExchange(&g_bHookEnabled, TRUE))
  {
    hook_init();
  }  

  return true;
}

void FreeFontEngine()
{
  if (InterlockedExchange(&g_bHookEnabled, FALSE)) 
  {
    hook_term();
  }

  if (g_pFTEngine) delete g_pFTEngine;
  
  FontLFree();

  CGdippSettings::DestroyInstance();

  g_TLInfo.ProcessTerm();

  CCriticalSectionLock::Term();
}

After that, you could get a local build with GDI++, it depend on freetype6.dll, zlib1.dll and detoured.dll as cost. And you could customize your setting file for GDI++, and run your local chrome with it. For more detail, you could read its offical document (Japanese), guess it by name or google it. :)

Wednesday, April 2, 2008

Virtualization Detection


As you know, virtualization is changing traditional IT infrastructure.
VMware ESX server, the market leading hypervisor, has been accepted by customers of all sizes, including all of the Fortune 100; Microsoft Windows 2008 also integrate Hyper-V as a core server role; several open source projects also focus on it, such as Xen (Intel), KVM (Linux) and Virtual Box (Sun) etc.

There are a lot of research papers and online discussion about Virtualization Detection

The 1st option is using vender specific public interface or backdoor, which could provide rich information about host OS and virtualization.

Microsoft public a Hypervisor Functional Specification for its Hyper-V implementation, which includes definition about not only feature and interface discovery but also Hyper-Call based management interfaces.
We could directly use one simple CPUID instruction to detect the existent of Hyper-V.




bool CHyperV::Exists(void)
{
int cpuid[4];


__cpuid(cpuid, 1);

return cpuid[2] & (1 <<>Further more, vender ID, neutral interface name, version and supported features could be fetched through similar method.

VMware also has similar private interface through a magic I/O port, which includes not only implementation information but also runtime information, such as processor speed, mouse cursor position and clipboard etc.
We could use its magic I/O port to detect the existent of VMware.





bool CVMware::Exists(void)
{
bool exists = true;

__try
{

__asm
{
push edx
push ecx
push ebx

mov eax, 'VMXh'
mov ebx, 0 // any value but not the MAGIC VALUE
mov ecx, 10 // get VMWare version

mov edx, 'VX' // port number

in eax, dx // read port

// on return EAX returns the VERSION
cmp ebx, 'VMXh' // is it a reply from VMWare?


setz [exists] // set return value

pop ebx
pop ecx
pop edx
}
}

__except(EXCEPTION_EXECUTE_HANDLER)
{
exists = false;
}
return exists;
}
For more completely commands list, you could check the following URL





VMware Backdoor I/O Port
http://chitchat.at.infoseek.co.jp/vmware/backdoor.html
VMware also provide an open source project, Open Virtual Machine Tools, which includes more detail usage of its backdoor.

Besides, Microsoft Virtual PC/Server, bochs, qemu has similar interface, you could check the following URLs for more detail.




How to detect Virtual PC or VMware from your program
http://www.codegurus.be/codegurus/Programming/virtualpc&vmware_en.htm

Attacks on Virtual Machine Emulators
http://www.symantec.com/avcenter/reference/Virtual_Machine_Threats.pdf

The 2nd option is checking VM implementation mechanism, which could detect some unknown VM vender in theory.

The mostly famous method is checking IDT register from Joanna’s Red Pill.







Red Pill... or how to detect VMM using (almost) one CPU instruction
http://invisiblethings.org/papers/redpill.html
The heart of this code is actually the SIDT instruction (encoded
as 0F010D[addr]), which stores the contents of the interrupt descriptor table register (IDTR) in the destination operand, which is actually a memory location. What is special and interesting about SIDT instruction is that, it can be executed in non privileged mode (ring3) but it returns the contents of the sensitive register, used internally by operating system.




Because there is only one IDTR register, but there are at least two OS running concurrently (i.e. the host and the guest OS), VMM needs to relocate the guest's IDTR in a safe place, so that it will not conflict with a host's one. Unfortunately, VMM cannot know if (and when) the process running in guest OS executes SIDT instruction, since it is not privileged (and it doesn't generate exception). Thus the process gets the relocated address of IDT table. It was observed that on VMWare, the relocated address of IDT is at address 0xffXXXXXX, whereas on Virtual PC it is 0xe8XXXXXX. This was tested on VMWare Workstation 4 and Virtual PC 2004, both running on Windows XP host OS.

This method only use one SIDT instruction to detect the existent of VM, because a lot of x86 software VM implementation choose to change IDT for guest OS.





bool CUnknown::CheckIDT(void)
{
unsigned char idt[6];

_asm
{
sidt idt

}
return idt[5] > 0xd0;
}
Unfortunately, this method could be spoofed by VM implementation





SubVirt: Implementing malware with virtual machines
http://www.eecs.umich.edu/~pmchen/papers/king06.pdf
The redpill [39] virtual-machine detection technique detects the
presence of a VMM by using the sidt instruction. The sidt instruction reads the address of the processor’s interrupt descriptor table. This address is different for an operating system running directly above hardware than for an operating system running above a VMM. VMMs emulate the sidt instruction when it is called from kernel-mode, but for performance reasons this instruction is not virtualized when called from user-mode. Thus, user-mode code can execute the
sidt instruction to detect the presence of a VMM. To defeat this detection technique, we use virtual-machine introspection to emulate the sidt instruction when it is called by the redpill application.

And it also doesn’t work on some hardware accelerated VM implementation and multi-core CPU system.





Introducing Blue Pill
http://theinvisiblethings.blogspot.com/2006/06/introducing-blue-pill.html
SubVirt was implemented on x86 hardware, which doesn't allow to achieve 100% virtualization, because there are number of sensitive instructions, which are not privileged, like the famous SIDT/SGDT/SLDT. This allows for trivial detection of the virtual mode - see e.g. my little Red Pill program. This however, doesn't apply to Blue Pill, as it relies on AMD SVM
technology.


Detecting the Presence of Virtual Machines
Using the Local Data Table
http://www.offensivecomputing.net/files/active/0/vm.pdf
The SIDT mechanism as implemented by Tobias Klein [1] and
separately by Joanna Rutkowska [2] is a method for detecting the presence of a virtual machine environment. While the test is by no means thorough, it is an effective test for the presence of an emulated CPU environment on a single-processor machine. There are various problems with the implementation, however. If a multi-core CPU is used, the interrupt descriptor table can change significantly when the process is run on different cores. Furthermore if two or more physical processors are present the same implementation issues apply.
To improve the issues of SIDT mechanism, a lot of attempts focus on similar register, such as GDT, LDT or TS register.




Detecting the Presence of Virtual Machines Using the Local Data Table
http://www.offensivecomputing.net/files/active/0/vm.pdf

Methods for Virtual Machine Detection
http://www.s21sec.com/descargas/vmware-eng.pdf

bool CUnknown::CheckGDT(void)
{
unsigned char gdt[6];
_asm
{
sgdt gdt
}
return gdt[5] > 0xd0;
}

bool CUnknown::CheckLDT(void)
{
unsigned char ldt[2];
_asm
{
sldt ldt
}
return ldt[0] != 0 && ldt[1] != 0;
}

bool CUnknown::CheckTR(void)
{
unsigned char tr[2];

_asm
{
str tr

}
return tr[0] == 0 && tr[1] == 0x40;
}
The major problem of these kinds of detection methods is the researcher hard to test compatibility on every VM vender.

After I made a simple test, it seems we hard to trust any of them, but we could made a hyper detection method base on more test results.



The 3rd option is using VM specific defect, which mostly tight coupling with VM version.

For example, eEye researcher published a detection method base on wrong far execution transfers behaviors.




Another VMware Detection
http://eeyeresearch.typepad.com/blog/2006/09/another_vmware_.html
So, the real world is not perfect, we hard to use one hammer to solve all the problems. We should carefully define our purpose and scope of virtualization detection, and choose a hyper detection solution for major focus market.