Changes in Chrome 71 break Gmail.

kao

I've complained about Chrome automatic updates before. I actually stayed on outdated Chrome 45 for a long time because I really needed NPAPI support to perform certain tasks.Β 

But few months ago I decided to bite the bullet and "live a normal life". So, I enabled Google Chrome updates and crossed my fingers. It worked for some time. I got the awful "modern UI" and managed to turn it off. I got the automatic Chrome sign-in that nobody actually wants and Google retracted later. And I was able to turn it off too.

But now Chrome cannot open my Gmail account. WTF?

Can't sync to account.

When logging into Gmail it just pops up this message "Can't sync to account. Request cancelled."

Read More

Fix Backspace in Google Chrome

kao

I've written about my fight with Google Chrome updates and broken features in the past. This time let's talk about the brain-dead decision to disable Backspace.

This was their rationale for the change:

We have UseCounters showing that 0.04% of page views navigate back via the backspace button and 0.005% of page views are after a form interaction. The latter are often cases where the user loses data. Years of user complaints have been enough that we think it's the right choice to change this given the degree of pain users feel by losing their data and because every platform
has another keyboard combination that navigates back.

So, just because 50 persons out of each 1'000'000 are f*king idiots, all the others have to suffer? Makes no sense to me.

To prove my point, let's look at the simple Google search: "Google Chrome backspace". It gives 238'000+ results. First few results are: "Backspace to go Back - Chrome Web Store", "Go Back With Backspace - Chrome Web Store", "Back to Backspace - Chrome Web Store", "How to restore the backspace key as a keyboard shortcut to go back in ...", "So where's the Chrome flag to RE-ENABLE BACKSPACE going back a...".

Apparently, I'm not the only one who is hurt by this change.

Hidden BackspaceGoesBack feature

When the change was first introduced in Google Chrome, developers also created a hidden feature that you could set and make Backspace work as it used to. To use it, you just need to launch chrome.exe with a command-line like this:

"C:\Program Files (x86)\Google\Chrome\Application\chrome.exe" --enable-features=BackspaceGoesBack

But in commit 0fe1505a this feature was removed as well.

If you enter the commit number in Chromium Find Releases tool, you'll see that in went out in public in v61.0.3116.0. Another check in Chrome Channel Releases tool will tell you that as of this moment the change is already out for both Canary and Dev channels, and will hit Beta and Stable channels in next months:

So, let's fix this issue for good! And by "fixing it" I don't mean some stupid JavaScript-based Chrome extension (which doesn't work when JavaScript is disabled and in hundreds of other cases..), I mean a proper fix in the code.

Patching Google Chrome again

If you've read my previous post, you know the drill. Set the symbol path, load chrome.dll in IDA, get yourself some coffee and wait. Wait a lot. And after 20-30 minutes you'll be able to start working.

This is the commit that's causing our headaches: commit 0fe1505a and the corresponding place in disassembly of Chrome 58:

.text:0001819AB871      mov     ecx, edi
.text:0001819AB873      sub     ecx, 33000
.text:0001819AB879      jz      short locIDC_BACK
.text:0001819AB87B      sub     ecx, 1
.text:0001819AB87E      jz      short locIDC_FORWARD

...

.text:0001819AB8D9 locIDC_FORWARD:              ; CODE XREF: chrome::BrowserCommandController::ExecuteCommandWithDisposition(int,WindowOpenDisposition)+BEj
.text:0001819AB8D9      mov     rcx, [rbx+0F0h]
.text:0001819AB8E0      mov     rax, [rcx]
.text:0001819AB8E3      call    qword ptr [rax+110h]
.text:0001819AB8E9
.text:0001819AB8E9 doGoForward:                 ; CODE XREF: chrome::BrowserCommandController::ExecuteCommandWithDisposition(int,WindowOpenDisposition)+1C3j
.text:0001819AB8E9      mov     rcx, [r14+18h]  ; browser
.text:0001819AB8ED      mov     edx, r15d       ; disposition
.text:0001819AB8F0      call    ?GoForward@chrome@@YAXPEAVBrowser@@W4WindowOpenDisposition@@@Z ; chrome::GoForward(Browser *,WindowOpenDisposition)
.text:0001819AB8F5      jmp     loc_1819AC7C2
.text:0001819AB8FA ; ---------------------------------------------------------------------------
.text:0001819AB8FA
.text:0001819AB8FA locIDC_BACK:                 ; CODE XREF: chrome::BrowserCommandController::ExecuteCommandWithDisposition(int,WindowOpenDisposition)+B9j
.text:0001819AB8FA      mov     rcx, [rbx+0F0h]
.text:0001819AB901      mov     rax, [rcx]
.text:0001819AB904      call    qword ptr [rax+110h]
.text:0001819AB90A
.text:0001819AB90A doGoBack:                    ; CODE XREF: chrome::BrowserCommandController::ExecuteCommandWithDisposition(int,WindowOpenDisposition)+1DBj
.text:0001819AB90A      mov     rcx, [r14+18h]  ; browser
.text:0001819AB90E      mov     edx, r15d       ; disposition
.text:0001819AB911      call    ?GoBack@chrome@@YAXPEAVBrowser@@W4WindowOpenDisposition@@@Z ; chrome::GoBack(Browser *,WindowOpenDisposition)
.text:0001819AB916      jmp     loc_1819AC7C2

...

.text:0001819AB954      mov     ecx, edi
.text:0001819AB956      sub     ecx, 33007
.text:0001819AB95C      jz      loc_1819AB9FC
.text:0001819AB962      sub     ecx, 2
.text:0001819AB965      jz      short loc_1819AB9BC
.text:0001819AB967      sub     ecx, 1
.text:0001819AB96A      jz      short locIDC_BACKSPACE_BACK
.text:0001819AB96C      cmp     ecx, 1
.text:0001819AB96F      jnz     loc_1819AC729
.text:0001819AB975
.text:0001819AB975 locIDC_BACKSPACE_FORWARD:               ; feature
.text:0001819AB975      lea     rcx, ?kBackspaceGoesBackFeature@features@@3UFeature@base@@B
.text:0001819AB97C      call    ?IsEnabled@FeatureList@base@@SA_NAEBUFeature@2@@Z ; base::FeatureList::IsEnabled(base::Feature const &)
.text:0001819AB981      test    al, al
.text:0001819AB983      jnz     doGoForward
.text:0001819AB989      mov     dl, 1
.text:0001819AB98B      jmp     short loc_1819AB9A3
.text:0001819AB98D ; ---------------------------------------------------------------------------
.text:0001819AB98D
.text:0001819AB98D locIDC_BACKSPACE_BACK:       ; CODE XREF: chrome::BrowserCommandController::ExecuteCommandWithDisposition(int,WindowOpenDisposition)+1AAj
.text:0001819AB98D      lea     rcx, ?kBackspaceGoesBackFeature@features@@3UFeature@base@@B ; feature
.text:0001819AB994      call    ?IsEnabled@FeatureList@base@@SA_NAEBUFeature@2@@Z ; base::FeatureList::IsEnabled(base::Feature const &)
.text:0001819AB999      test    al, al
.text:0001819AB99B      jnz     doGoBack
.text:0001819AB9A1      xor     edx, edx
.text:0001819AB9A3
.text:0001819AB9A3 loc_1819AB9A3:               ; CODE XREF: chrome::BrowserCommandController::ExecuteCommandWithDisposition(int,WindowOpenDisposition)+1CBj
.text:0001819AB9A3      mov     rax, [r14+18h]
.text:0001819AB9A7      mov     rcx, [rax+0F0h]
.text:0001819AB9AE      mov     rax, [rcx]
.text:0001819AB9B1      call    qword ptr [rax+108h]   ; window()->MaybeShowNewBackShortcutBubble()
.text:0001819AB9B7      jmp     loc_1819AC7C2

What a mess!

Luckily for us, compiler decided to emit nice switch table in version v61.0.3153.2:

.text:0001806CE0C3 loc_1806CE0C3:                          ; CODE XREF: sub_1806CDF28+CFj
.text:0001806CE0C3                 lea     eax, [rbx-33000] ; jumptable 00000001806CE00B default case
.text:0001806CE0C9                 cmp     eax, 0Bh        ; switch 12 cases
.text:0001806CE0CC                 ja      loc_1806CEACC   ; jumptable 00000001806CE03B cases 2-11,13
.text:0001806CE0D2                 lea     rcx, off_1806CEC30
.text:0001806CE0D9                 movsxd  rax, dword ptr [rcx+rax*4]
.text:0001806CE0DD                 add     rax, rcx
.text:0001806CE0E0                 jmp     rax             ; switch jump

...

.text:0001806CEC30 off_1806CEC30:   
.text:0001806CEC30  B2 F4 FF FF    dd offset locIDC_BACK - 1806CEC30h
.text:0001806CEC34  5B FD FF FF    dd offset locIDC_FORWARD - 1806CEC30h ; jumptable 00000001806CE0E0 case 1
.text:0001806CEC38  91 FD FF FF    dd offset loc_1806CE9C1 - 1806CEC30h ; jumptable 00000001806CE0E0 case 2
.text:0001806CEC3C  B3 FD FF FF    dd offset loc_1806CE9E3 - 1806CEC30h ; jumptable 00000001806CE0E0 case 3
.text:0001806CEC40  D5 FD FF FF    dd offset loc_1806CEA05 - 1806CEC30h ; jumptable 00000001806CE0E0 case 4
.text:0001806CEC44  9C FE FF FF    dd offset loc_1806CEACC - 1806CEC30h ; jumptable 00000001806CE0E0 default case
.text:0001806CEC48  F5 FD FF FF    dd offset loc_1806CEA25 - 1806CEC30h ; jumptable 00000001806CE0E0 case 6
.text:0001806CEC4C  21 FE FF FF    dd offset loc_1806CEA51 - 1806CEC30h ; jumptable 00000001806CE0E0 case 7
.text:0001806CEC50  9C FE FF FF    dd offset loc_1806CEACC - 1806CEC30h ; jumptable 00000001806CE0E0 default case
.text:0001806CEC54  15 FE FF FF    dd offset loc_1806CEA45 - 1806CEC30h ; jumptable 00000001806CE0E0 case 9
.text:0001806CEC58  43 FE FF FF    dd offset locIDC_BACKSPACE_BACK - 1806CEC30h ; jumptable 00000001806CE0E0 case 10
.text:0001806CEC5C  65 FE FF FF    dd offset locIDC_BACKSPACE_FORWARD - 1806CEC30h ; jumptable 00000001806CE0E0 case 11

To make Backspace work as intended, we can simply overwrite 2 entries in jump table.

.text:0001806CEC30 off_1806CEC30:   
.text:0001806CEC30  B2 F4 FF FF    dd offset locIDC_BACK - 1806CEC30h
.text:0001806CEC34  5B FD FF FF    dd offset locIDC_FORWARD - 1806CEC30h ; jumptable 00000001806CE0E0 case 1
.text:0001806CEC38  91 FD FF FF    dd offset loc_1806CE9C1 - 1806CEC30h ; jumptable 00000001806CE0E0 case 2
.text:0001806CEC3C  B3 FD FF FF    dd offset loc_1806CE9E3 - 1806CEC30h ; jumptable 00000001806CE0E0 case 3
.text:0001806CEC40  D5 FD FF FF    dd offset loc_1806CEA05 - 1806CEC30h ; jumptable 00000001806CE0E0 case 4
.text:0001806CEC44  9C FE FF FF    dd offset loc_1806CEACC - 1806CEC30h ; jumptable 00000001806CE0E0 default case
.text:0001806CEC48  F5 FD FF FF    dd offset loc_1806CEA25 - 1806CEC30h ; jumptable 00000001806CE0E0 case 6
.text:0001806CEC4C  21 FE FF FF    dd offset loc_1806CEA51 - 1806CEC30h ; jumptable 00000001806CE0E0 case 7
.text:0001806CEC50  9C FE FF FF    dd offset loc_1806CEACC - 1806CEC30h ; jumptable 00000001806CE0E0 default case
.text:0001806CEC54  15 FE FF FF    dd offset loc_1806CEA45 - 1806CEC30h ; jumptable 00000001806CE0E0 case 9
.text:0001806CEC30  B2 F4 FF FF    dd offset locIDC_BACK - 1806CEC30h    ; jumptable 00000001806CE0E0 case 10
.text:0001806CEC34  5B FD FF FF    dd offset locIDC_FORWARD - 1806CEC30h ; jumptable 00000001806CE0E0 case 11

Mission accomplished! πŸ™‚

In the next part of this blog series, I'll show you how to make this patch more user friendly and a few ways how to automate the patching (so that you can receive automatic Google Chrome updates, if you wish).

Till next time!
kao.

Let’s say something good about Google Chrome

kao

In my previous post I criticized Google's decision to disable NPAPI plugin technology. I still think it was a bad decision. But today let's talk about a change that should be an improvement for virtually all users.

Chrome will begin pausing many Flash ads by default to improve performance for users. This change is scheduled to start rolling out on September 1, 2015.

Source: https://plus.google.com/+GoogleAds/posts/2PmwKinJ7nj

Say what? Is Google going against ads? 😎 Well, not really. HTML5 ads are apparently OK. But those obnoxious Flash-based ads will become click-to-play.

The setting in question is located in Settings->Advanced->Content Settings->Plugins:
chrome_plugin_settings
It has been present in Chrome for several months already. So, I'm guessing that Google will be only pushing out some configuration change, or change the default value for new installations. Who knows, as Google is not giving us any details at this point..

Google's ad detection algorithm might need some improvements and there might be some other side-effects but overall I think it's a great change! Good job Google, you made my day better! πŸ™‚

Dancing pigs – or how I won my fight with Google Chrome updates

kao

I think removing NPAPI support from Google Chrome was a really stupid decision from Google. Sure, Java and some other plugins were buggy and vulnerable. But there is a huge group of users that need to have NPAPI for perfectly legit reasons. Certain banks use NPAPI plugins for 2-factor authentication. Certain countries have made their digital government and signatures based on NPAPI plugins. And the list goes on.

I have my reasons too. If I have to run older version of Chrome for that, I will do so - and no amount of nagging will change my mind.

That’s a well known fact in security circles, named "dancing pigs":

If J. Random Websurfer clicks on a button that promises dancing pigs on his computer monitor, and instead gets a hortatory message describing the potential dangers of the applet β€” he's going to choose dancing pigs over computer security any day

Unfortunately pointy-haired managers at Google fail to understand this simple truth. Or they just don't give a crap.

Hello, I am AutoUpdate, I just broke your computer

Imagine my reaction one day when my NPAPI plugin suddenly stopped working. It just wouldn't load. It turned out that Google Chrome was silently updated by Google Update. It broke my plugin in the process and - officially - there is no way of going back.

What do you think I did next?

That's right - I disabled Google Update from services, patched GoogleUpdate.exe to terminate immediately and restored previous version of Google Chrome from the backup. Dancing pigs, remember?

Your Google Chrome is out-of-date

It worked well for few months. But this week, Chrome started nagging me again.
chrome_nag1
Quick Google search lead me to this answer: you need to disable Chrome updates using Google's administrative templates.

Let's ignore the fact that the described approach works only for XP (for Windows 7 you need to use ADMX templates which you need to copy manually to %systemroot%\PolicyDefinitions) and now there are like 4 places related to Google Chrome updates in the policies.

So, I set the policies and it seemed to work. For a day.

Your Google Chrome is still out-of-date

Imagine my joy the next day when I saw yet-another-nagscreen. Like this:
chrome_nag2

No, I don't need that update. Really!

I can close the nag, but 10 minutes later it will pop up again. And it looks like the only way to get rid of the nag is to patch chrome.dll. I really didn't want to do that but dumb decisions by Google managers are forcing my hand here.

Reversing Google Chrome

Since Chrome is more or less open-source, you can easily find the nagware message:

      
        Chrome could not update itself to the latest version, so you are missing out on awesome new features and security fixes. You need to update Chrome.
      

From here, we can find which dialog is responsible for the nag:

void OutdatedUpgradeBubbleView::Init() {
  ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
  accept_button_ = new views::LabelButton(
      this, l10n_util::GetStringUTF16(
          auto_update_enabled_ ? IDS_REINSTALL_APP : IDS_REENABLE_UPDATES));
  accept_button_->SetStyle(views::Button::STYLE_BUTTON);
  accept_button_->SetIsDefault(true);
  accept_button_->SetFontList(rb.GetFontList(ui::ResourceBundle::BoldFont));
  elevation_icon_setter_.reset(new ElevationIconSetter(
      accept_button_,
      base::Bind(&OutdatedUpgradeBubbleView::SizeToContents,
                 base::Unretained(this))));

  later_button_ = new views::LabelButton(
      this, l10n_util::GetStringUTF16(IDS_LATER));
  later_button_->SetStyle(views::Button::STYLE_BUTTON);

  views::Label* title_label = new views::Label(
      l10n_util::GetStringUTF16(IDS_UPGRADE_BUBBLE_TITLE));
  title_label->SetFontList(rb.GetFontList(ui::ResourceBundle::MediumFont));
  title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);

  views::Label* text_label = new views::Label(l10n_util::GetStringUTF16(
      auto_update_enabled_ ? IDS_UPGRADE_BUBBLE_TEXT
                           : IDS_UPGRADE_BUBBLE_REENABLE_TEXT));

From there we can find NOTIFICATION_OUTDATED_INSTALL which comes from UpgradeDetector. And finally we arrive at CheckForUpgrade() procedure:

void UpgradeDetectorImpl::CheckForUpgrade() {
  // Interrupt any (unlikely) unfinished execution of DetectUpgradeTask, or at
  // least prevent the callback from being executed, because we will potentially
  // call it from within DetectOutdatedInstall() or will post
  // DetectUpgradeTask again below anyway.
  weak_factory_.InvalidateWeakPtrs();

  // No need to look for upgrades if the install is outdated.
  if (DetectOutdatedInstall())
    return;

  // We use FILE as the thread to run the upgrade detection code on all
  // platforms. For Linux, this is because we don't want to block the UI thread
  // while launching a background process and reading its output; on the Mac and
  // on Windows checking for an upgrade requires reading a file.
  BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
                          base::Bind(&UpgradeDetectorImpl::DetectUpgradeTask,
                                     weak_factory_.GetWeakPtr()));
}

This is what I want to patch! But how?

You could load Chrome DLL in IDA and try to find the offending call on your own. But I'm willing to bet that it will take you hours, if not days. Well, PDB symbols to the rescue!

Symbols for Chrome are stored at https://chromium-browser-symsrv.commondatastorage.googleapis.com and you will need to add that path to your _NT_SYMBOL_PATH. Something like this:

set _NT_SYMBOL_PATH=SRV*F:\Symbols*https://msdl.microsoft.com/download/symbols;SRV*F:\Symbols*https://chromium-browser-symsrv.commondatastorage.googleapis.com

_NT_SYMBOL_PATH is a very complex beast, you can do all sorts of things with it. If you want a more detailed explanation how it works, I suggest that you read Symbols the Microsoft Way.

After that, you can load chrome.dll in IDA, wait until IDA downloads 850MB of symbols, and drink a coffee or two while IDA is analyzing the file. After that it's all walk in the park. This is the place:

.02CF3BDF: 55                             push         ebp
.02CF3BE0: 8BEC                           mov          ebp,esp
.02CF3BE2: 83EC20                         sub          esp,020 ;' '
.02CF3BE5: 8365FC00                       and          d,[ebp][-4],0
.02CF3BE9: 56                             push         esi
.02CF3BEA: 8BF1                           mov          esi,ecx
.02CF3BEC: 57                             push         edi
.02CF3BED: 8DBE10010000                   lea          edi,[esi][000000110]

And one retn instruction makes my day so much better..

Final words

Unfortunately for me, this world is changing. You are no more the sole owner of your devices, all the big corporations want to make all the decisions for you.

Luckily for me, it is still possible to achieve a lot using a disassembler and debugger. And reverse engineering for interoperability purposes is completely legal in EU. πŸ™‚

Have fun!

Fixing choppy sound in Chrome within RDP connection

kao

Some things and services are banned from work computers. Like your collection of MP3s. Or p2p-based television. Or access to Pandora. πŸ™‚ But everyone knows that music is a really great motivator! So, I decided to try a small trick - use RDP connection to my home PC and play my MP3s from home PC.

It turns out that playing MP3s in Winamp works great. However, playing Pandora radio or anything else in Chrome produced a very choppy sound and video framerate was around 3fps.. That's not great at all.

Quick Google search locates this 1.5-years old Chrome bug: Issue 310983: choppy sound playing videos within RDP session (not only Flash, also HTML5). As it happens quite often - it's reproduced by several people but nobody gives a flying fcuk about actually fixing it. So much for the open-source and quick fixes..

Lucky for me, there was a workaround suggested in the comments - install RDP 8.0 server and client.

Hmm, I haven't heard anything about RDP versoin 8.0. How is that possible?

Turns out, it comes by default on Windows 8.x but must be manually installed and explicitly enabled on Windows 7. It's one of those hidden treasures very few people know about!

So, on my home Win7 box I installed updates KB2574819, KB2592687 and restarted. Automatically received Security Update KB2965788 and got another restart. Made the necessary changes in group policy settings, and - you guessed right - yet another restart. Got locked out of my box because suddenly my username was not in "Remote Users" group, and I had to re-add it manually. Logged in and everything works as it should. Pandora sounds great, video is suddenly smooth and watchable and my work productivity goes... UP! πŸ™‚

Happy happy joy joy!

Further reading

List of new features in RDP v8.0
Technical blog explaining technologies behind RDP v8.0 magic

About e-governments

kao

Two days ago Google released Chrome 42. It's the answer to life, the universe and everything. And among other things, it disables all NPAPI plugins by default, finally putting that Java nightmare into it's grave. Good riddance!

But what about other NPAPI plugins? Like, you know, the ones used for electronic documents, digital signatures and other e-government thingies?

Well, here are 2 ways how government agencies approach the same problem:
e-governments compared

Welcome to the 21st century. If you're Estonian, that is..