[1/2]解决Windows 8.1检查更新时发生的0x8024A008错误

DNTTAH => Do not try this at home.

故事还是从之前咱升级了操作系统开始的,因为一直没有收到Windows Update的通知所以咱觉得有点奇怪了。照道理来说每月至少都会有那么几个更新的,毕竟之前也说了,Windows 超复杂。

于是和上一次一样,拿出Process Monitor监测,但是这次什么区别都没看出来

问了Google,看到有几个人和我一样有这个问题,其中有一条是微软官方的回复:

Thank You for sharing the logs. I was able to reproduce the issue in my virtual environment. To understand more about this behavior, I engaged the Product Group and I have been informed that this behavior is By Design. WU uses the OOBEComplete() Windows API call to determine whether OOBE is in progress or not, and if so, it will not perform automatic or UI update searches. HRESULT code 0x8024a008 is the WU error code WU_E_AU_OOBE_IN_PROGRESS. WU automatic and UI updates won’t run while Setup reports that OOBE is still in progress. This is to prevent automatic updates from causing a system reboot during OOBE, which is – needless to say – a Very Bad Thing. This problem has always existed. Unfortunately, when the computer is in Sysprep audit mode, Setup will report to WU that OOBE is in progress even though it might not actually be so. This is the reason that updates from WU UIs are blocked in audit mode. On the plus side, this means that OEMs can better ensure that only the updates they want on their machines get installed in the factory floor image, even if they enable automatic updating in the image.

简而言之:Windows Update被设计为在OOBE模式下(也就是刚装完系统后出现的那个叫你设置用户的界面),是不会检查、安装更新的,以防止在OOBE过程中发生重启。

看来出于某些原因,操作系统认为我还没设置完,但实际上我都用了几个月了。

不过上面的回复里也有一些有用的信息,比如:

WU uses the OOBEComplete() Windows API call to determine whether OOBE is in progress or not.

这也就给(暴力)解决问题提供了线索,那就是在Windows的某个系统DLL里有个名为OOBEComplete的导出函数。

那简单了,拿出CFF Explorer,加载wuaueng.dll,打开导入表,一个一个找过去,最后看到这条
p0

也就是说,这个API是在kernel32.dll里声明的。于是再用CFF Explorer加载kernel32.dll,RVA是0x000B6280,再通过分段表内的信息计算出函数开头在文件里的偏移量是0x000B5680,打开、定位、反汇编,发现其实这个函数不长

MOV  RAX,            RSP         ;48 8BC4
PUSH RBX                         ;53
SUB  RSP,            0x30        ;48 83EC 30
AND  DWORD[RAX+8],   0           ;8360 08 00
MOV  RBX,            RCX         ;48 8BD9
TEST RCX,            RCX         ;48 85C9
JZ   $+0x18D38                   ;0F84 388D0100
MOV  RDX,            [RIP+0x569] ;48 8B15 69050000
AND  QWORD[RAX-0x18],0           ;48 8360 E8 00
LEA  R9,             [RAX+8]     ;4C 8D48 08
LEA  R8,             [RIP+0x29]  ;4C 8D05 29000000
LEA  RCX,            [RAX+0x10]  ;48 8D48 10
CALL $+0x6727F                   ;FF15 7F720600
TEST EAX,            EAX         ;85C0
JS   $+0x18D1C                   ;0F88 1C8D0100
MOV  EAX,            [RSP+0x40]  ;8B4424 40
MOV  [RBX],          EAX         ;8903
MOV  EAX,            1           ;B8 01000000
ADD  RSP,            0x30        ;48 83C4 30
POP  RBX                         ;5B
RET                              ;C3

随便找了个程序,把调试器挂进去,JS和JZ两个的都是普通的错误处理过程,CALL的是ntdll.dll!RtlQueryWnfStateData

按照x64的调用规范,第一到第四参数在RCX、RDX、R8、R9四个寄存器里传递,返回值从RAX里返回(更详细的不说了),那么这个函数大概可以写成这样一串东西:(其实我不太会汇编)

BOOL OOBEComplete(BOOL *par1) {
  BOOL h1;
  int h2;

  h1 = FALSE;
  if(par1 == NULL) {
    //RtlSetLastWin32Error等等
    return FALSE;
  }
  if(RtlQueryWnfStateData(&h2, WNF_DEP_OOBE_COMPLETE, OOBECompleteWnfQueryCallback, &h1, 0) < 0) {
    //RtlSetLastWin32Error等等
    return FALSE;
  }
  *par1 = h1;
  return TRUE;
}

反正大概就这么一回事嘛,那么为了图省事,我们就把这东西改一改,打个补丁好了。

我期望的函数是这样的:

BOOL OOBEComplete(BOOL *par1) {
  if(par1 == NULL) {
    RtlSetLastWin32Error(0x00000057);
    return 0;
  }
  *par = TRUE;
  return TRUE;
}

那么接下来就该干编译器干的事情了。我打算给32位的程序也打上这个补丁

于是汇编代码的草稿:(64位)

TEST RCX,   RCX                  ;48 85C9
JZ   $+7    ;line 7              ;74 07
XOR  EAX,   EAX                  ;31C0
INC  EAX                         ;FFC0
MOV  [RCX], EAX                  ;8901
RET                              ;C3
MOV  CL,    0x57                 ;B1 57
CALL ntdll.RtlSetLastWin32Error  ;FF15 ????????
XOR  EAX,   EAX                  ;31C0
RET                              ;C3

汇编代码的草稿:(32位)

MOV  EDI,     EDI                ;8B FF
MOV  ECX,     [ESP+8]            ;8B4CE4 08
TEST ECX,     ECX                ;85C9
JZ   $+8      ;line 9            ;74 08
XOR  EAX,     EAX                ;31C0
INC  EAX                         ;40
MOV  [ECX],   EAX                ;8901
RETN 4                           ;C2 0400
MOV  CL,      0x57               ;B1 57
MOV  [ESP+8], ECX                ;894CE4 08
CALL ntdll.RtlSetLastWin32Error  ;E8 ????????
XOR  EAX,     EAX                ;31C0
RET                              ;C3

之后就是怎么应用补丁的问题了,放到下一个part里好了。

发表评论