2017-03-24 11:11:17 -07:00
2017-03-24 11:11:17 -07:00

Here's a list of the Windows updates that I will be talking about in this paper:

Title Products Classification Last Updated Version Size
March, 2017 Preview of Monthly Quality Rollup for Windows 7 (KB4012218) Windows 7 Updates 3/16/2017 n/a 93.4 MB
March, 2017 Preview of Monthly Quality Rollup for Windows 7 for x64-based Systems (KB4012218) Windows 7 Updates 3/16/2017 n/a 153.9 MB
March, 2017 Preview of Monthly Quality Rollup for Windows Server 2008 R2 x64 Edition (KB4012218) Windows Server 2008 R2 Updates 3/16/2017 n/a 153.9 MB
March, 2017 Preview of Monthly Quality Rollup for Windows 8.1 (KB4012219) Windows 8.1 Updates 3/16/2017 n/a 121.2 MB
March, 2017 Preview of Monthly Quality Rollup for Windows 8.1 for x64-based Systems (KB4012219) Windows 8.1 Updates 3/16/2017 n/a 218.0 MB
March, 2017 Preview of Monthly Quality Rollup for Windows Server 2012 R2 (KB4012219) Windows Server 2012 R2 Updates 3/16/2017 n/a 218.0 MB

About

After reading this article on gHacks, I was inspired to look into these new rollup updates that Microsoft released on March 16. Among other things included in these updates, the changelog mentions the following:

Enabled detection of processor generation and hardware support when PC tries to scan or download updates through Windows Update.

Which is really just Microsoft's nice way of telling you to fuck yourself if you want to keep using Windows 7 or 8.1. There even have been people with AMD and Intel systems from 2015 who have reportedly been locked out of Windows Update because of this!

Bad Microsoft!

Well then, time figure out how to fix this garbage.

I started by downloading the .msu package for my system (in my case, it was windows6.1-kb4012218-x64_590943c04550a47c1ed02d3a040d325456f03663.msu)

I extracted it using the command line expand tool, like this:

md "windows6.1-kb4012218-x64"
expand -f:* ".\windows6.1-kb4012218-x64_590943c04550a47c1ed02d3a040d325456f03663.msu" ".\windows6.1-kb4012218-x64"
cd ".\windows6.1-kb4012218-x64"
md "Windows6.1-KB4012218-x64"
expand -f:* ".\Windows6.1-KB4012218-x64.cab" ".\Windows6.1-KB4012218-x64"

Great, now there's thousands of .exe and .dll files to sort through! Just kidding. Sort of. Maybe. 🤔

I ended up using PowerShell to sort through all the binaries, like so:

Get-ChildItem -Filter "wu*" -Exclude "*.mui" -Recurse | ForEach-Object { $_.FullName }

That's narrowed it down quite a bit! This is now what we're looking at:

  • wu.upgrade.ps.dll
  • wuapi.dll
  • wudriver.dll
  • wups.dll
  • wuapp.exe
  • wuwebv.dll
  • wuauclt.exe
  • wuaueng.dll
  • wups2.dll
  • wucltux.dll
  • wuapi.dll
  • wudriver.dll
  • wups.dll
  • wuapp.exe
  • wuwebv.dll

Next, I started comparing these binaries with the ones already on my system with BinDiff and Diaphora, starting with wuauclt.exe. After turning up empty with that (the two binaries were nearly identical), I decided to take a look at wuaueng.dll, which turned up quite a few relevant new functions:

EA Name Basicblock Instructions Edges
00000600001DCB9C CWUTelemetryDownloadCanceledEvent::FireAsimovEvent(void) 36 446 53
00000600001D8F98 CWUTelemetryDownloadCanceledEvent::`scalar deleting destructor'(uint) 3 15 3
00000600001D8FD0 CWUTelemetryDownloadEvent::CWUTelemetryDownloadEvent(void) 1 58 0
00000600001DAEDC CWUTelemetryDownloadEvent::Init(CReporter *,long,long,ushort const *,long,_GUID,_GUID,CReportingOptionalValues &,AsimovDataInAddition *) 6 50 8
00000600001DAFB8 CWUTelemetryDownloadEvent::InitializeMemebersFromOptionalData(tagOptionalData *) 27 91 40
00000600001D9100 CWUTelemetryDownloadEvent::~CWUTelemetryDownloadEvent(void) 2 60 1
00000600001DC2C4 CWUTelemetryDownloadFailedEvent::FireAsimovEvent(void) 36 446 53
00000600001DB114 CWUTelemetryDownloadStartedEvent::FireAsimovEvent(void) 36 446 53
00000600001DB9EC CWUTelemetryDownloadSucceededEvent::FireAsimovEvent(void) 36 446 53
00000600001D8C48 CWUTelemetryEventFactory::FireTelemetryEvent(CReporter *,long,long,ushort const *,long,_GUID,_GUID,CReportingOptionalValues &,AsimovDataInAddition *) 11 76 17
00000600001D8574 CWUTelemetryEventFactory::GetTelemetryEvent(CReporter *,long,long,ushort const *,long,_GUID,_GUID,CReportingOptionalValues &,AsimovDataInAddition *,CWUTelemetryEvent * *) 77 395 127
00000600001DEE7C CWUTelemetryInstallCanceledEvent::FireAsimovEvent(void) 34 409 50
00000600001D8DD4 CWUTelemetryInstallEvent::CWUTelemetryInstallEvent(void) 1 57 0
00000600001DD474 CWUTelemetryInstallEvent::Init(CReporter *,long,long,ushort const *,long,_GUID,_GUID,CReportingOptionalValues &,AsimovDataInAddition *) 6 50 8
00000600001DD550 CWUTelemetryInstallEvent::InitializeMemebersFromOptionalData(tagOptionalData *) 23 81 34
00000600001D8EFC CWUTelemetryInstallEvent::~CWUTelemetryInstallEvent(void) 2 66 1
00000600001DE67C CWUTelemetryInstallFailedEvent::FireAsimovEvent(void) 34 409 50
00000600001DF67C CWUTelemetryInstallRebootPendingEvent::FireAsimovEvent(void) 34 409 50
00000600001D8D9C CWUTelemetryInstallRebootPendingEvent::`scalar deleting destructor'(uint) 3 15 3
00000600001DD67C CWUTelemetryInstallStartedEvent::FireAsimovEvent(void) 34 409 50
00000600001DDE7C CWUTelemetryInstallSucceededEvent::FireAsimovEvent(void) 34 409 50
00000600001CAE68 CWUTelemetryScanFailedEvent::FireAsimovEvent(void) 31 416 46
00000600001CA100 CWUTelemetryScanRetryEvent::FireAsimovEvent(void) 9 108 13
00000600001CA588 CWUTelemetryScanSucceededEvent::FireAsimovEvent(void) 47 459 73
00000600001CB790 CWUTelemetryUnsupportedSystemClickSupportEvent::FireAsimovEvent(void) 5 22 7
00000600001CB9B0 CWUTelemetryUnsupportedSystemClickSupportEvent::`scalar deleting destructor'(uint) 3 17 3
00000600001CB7FC CWUTelemetryUnsupportedSystemDetectionEvent::FireAsimovEvent(void) 5 22 7
00000600001CB970 CWUTelemetryUnsupportedSystemDetectionEvent::`scalar deleting destructor'(uint) 3 17 3
00000600001CB724 CWUTelemetryUnsupportedSystemNotificationDismissEvent::FireAsimovEvent(void) 5 22 7
00000600001CB9F0 CWUTelemetryUnsupportedSystemNotificationDismissEvent::`scalar deleting destructor'(uint) 3 17 3
00000600001CB6B8 CWUTelemetryUnsupportedSystemNotificationShowEvent::FireAsimovEvent(void) 5 22 7
00000600001CBA30 CWUTelemetryUnsupportedSystemNotificationShowEvent::`scalar deleting destructor'(uint) 3 17 3
0000060000102F08 IsCPUSupported(void) 20 157 31
00000600000AF3C0 IsDeviceServiceable(void) 7 31 8
00000600000832CC TraceLoggingEnableForTelemetry(_TlgProvider_t const *) 16 86 23
0000060000083210 TraceLoggingSetInformation(_TlgProvider_t const *,_EVENT_INFO_CLASS,void *,ulong) 6 50 8

We have found culprits, IsDeviceServiceable(void) and IsCPUSupported(void)!

Solutions

IsCPUSupported(void) is only ever called by IsDeviceServiceable(void), which is called by five other functions. Luckily, there are a few potential ways to kill this CPU check.

  1. Patch wuaueng.dll and change dword_600002EE948 (see this line) which is at file offset 0x26C948, from 0x01 to 0x00, which makes IsDeviceServiceable(void) jump over its entire body and return 1 (supported CPU) immediately. This is my preferred method, because it is the least intrusive and doesn't require any runtime memory patching. These offsets are only for the Windows 7 x64 version, I will upload .xdelta files for all of the other versions eventually. The only downside of this method is you have to re-apply the patch whenever wuaueng.dll gets updated.
  2. nop all the instructions out highlighted here in IsDeviceServiceable(void), this will enable the usage of the ForceUnsupportedCPU of type REG_DWORD under the registry key HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Test\Scan (create it if it doesn't exist, if you want to use this method). Set this value to 0x00000001 to force unsupported CPUs, and back to 0x00000000 to change the behaviour back to default.
  3. I guess you could do runtime memory patching with Windows APIs too, but that's ugly.
Description
Get updates on Windows 7 and 8.1 with unsupported hardware.
Readme 1 MiB
Languages
C 99%
Batchfile 1%