VAIO BIOSのサイレント更新パラメータ発見記 + ちょっとした回想

はじめに

この記事は中国語からの翻訳です。不自然な点がありましたらご容赦ください。

原記事(中国語)

実行前の確認: AC接続 + バッテリー残量あり BitLocker off もし文鎮化しても……自己責任を

最近、ノートPCを大量に再インストールする案件があり、要は初期化作業です。大半は無人応答+謎スクリプトで片付いたのですが、
BIOS 更新は WSUS 経由で配信されない(少なくとも私の手元に来た時点では一度も降ってこなかった)ので、
ある日突然 L / D の電源投入で勝手に BIOS が更新されるのは避けたい、という話になりました。

そこで BIOS 更新にサイレントインストールのパラメータがないか調べました。直感的にはあるはずと思ったので、この記事を書きました。

……とはいえ本文が薄いので、追加 セクションも用意しました。笑ってやってください(

コマンド

まず VAIO の公式サイトから対象機種の最新 BIOS をダウンロードします。
このチュートリアルで使っているのはこれです:
https://solutions.vaio.com/6403

ショートカットを作成して /VPM_NonInteractive のサイレントパラメータを追加:

SP000847.exe /VPM_NonInteractive

または cmd から:

start "" /wait "SP000847.exe" /VPM_NonInteractive

更新が必要な場合、2分ほど待つと自動で再起動して BIOS の自動更新が始まります。

実行中に出るのは UAC のダイアログだけで、他はすべてサイレントです。
EXE を直接起動すると 2〜3 回ポップアップをクリックして Start する必要があるので、
システムイメージ化後の自動実行にはこちらの方が便利です。
一番簡単なのはスタートアップに入れる方法ですが、UAC の確認は手動になるので、
OOBE でデスクトップに入ったタイミングで実行するなども考えられます(詳細は未検証)。

本文

このサイレントパラメータの見つけ方を少し説明します。
ネット上にはこのパラメータの情報が全くなく、VAIO は日本専売なので、
サポート情報も基本的にクローズド(という推測)です。

最初はパッケージを分解して BIOS を直に更新できないかと考えましたが、
7-zip / winrar は反応なし、binwalk を使っても何も出てきませんでした:

❯ binwalk SP000847.exe 

                         /Users/h/Downloads/SP000847.exe
--------------------------------------------------------------------------------
DECIMAL                            HEXADECIMAL                        DESCRIPTION
--------------------------------------------------------------------------------
2845781                            0x2B6C55                           Windows 
                                                                      PE 
                                                                      binary, 
                                                                      machine 
                                                                      type: 
                                                                      Intel 
                                                                      x86-64
--------------------------------------------------------------------------------

Analyzed 1 file for 85 file signatures (187 magic patterns) in 102.0 milliseconds

そこで諦めて Ghidra に投げ込みました。
Windows PE の起動パラメータは基本的に / から始まるので、
すぐにそれっぽい文字列が見つかりました:
strings -> filter /

見つかったのは /VPM_NonInteractive だけで、名前的にもサイレント実行(NonInteractive)っぽいですよね。
試しに実行してみたら、数分後に PC が再起動して BIOS 更新が始まり、UAC 以外は何も操作不要でした。

reverse code snippet:


/* WARNING: Function: __security_check_cookie replaced with injection: security_check_cookie */

bool FUN_1400059d0(longlong param_1,longlong *param_2)

{
  int *piVar1;
  wchar_t *pwVar2;
  wchar_t *pwVar3;
  wchar_t wVar4;
  uint uVar5;
  longlong *plVar6;
  longlong lVar7;
  size_t sVar8;
  size_t sVar9;
  ulonglong uVar10;
  wchar_t *pwVar11;
  uint uVar12;
  int iVar13;
  ulonglong uVar14;
  bool bVar15;
  undefined1 auStack_308 [32];
  undefined4 local_2e8;
  undefined8 local_2e0;
  wchar_t *local_2d8;
  longlong local_2d0;
  longlong local_2c8;
  longlong local_2c0;
  undefined1 local_2b8 [64];
  undefined8 local_278;
  ulonglong local_48;
  ulonglong local_38;
  
  local_38 = DAT_14027f930 ^ (ulonglong)auStack_308;
  uVar10 = 0;
  local_2c8 = param_1;
  plVar6 = (longlong *)FUN_140022698();
  if (plVar6 == (longlong *)0x0) {
                    /* WARNING: Subroutine does not return */
    FUN_140004cc0(0x80004005);
  }
  lVar7 = (**(code **)(*plVar6 + 0x18))(plVar6);
  pwVar11 = (wchar_t *)(lVar7 + 0x18);
  uVar12 = 0xffffffff;
  uVar14 = uVar10;
  local_2d8 = pwVar11;
  do {
    lVar7 = *param_2;
    pwVar2 = (wchar_t *)(lVar7 + uVar14 * 2);
    pwVar3 = (wchar_t *)(lVar7 + (longlong)*(int *)(lVar7 + -0x10) * 2);
    if (pwVar2 < pwVar3) {
      sVar8 = wcsspn(pwVar2,L" ");
      if (pwVar3 <= pwVar2 + (int)sVar8) goto LAB_140005aa4;
      sVar9 = wcscspn(pwVar2 + (int)sVar8,L" ");
      iVar13 = (int)sVar8 + (int)uVar14;
      FUN_140006480(param_2,&local_2d0,iVar13,sVar9 & 0xffffffff);
      uVar5 = iVar13 + 1 + (int)sVar9;
    }
    else {
LAB_140005aa4:
      if ((*(longlong **)(*param_2 + -0x18) == (longlong *)0x0) ||
         (plVar6 = (longlong *)(**(code **)(**(longlong **)(*param_2 + -0x18) + 0x20))(),
         plVar6 == (longlong *)0x0)) {
        plVar6 = (longlong *)FUN_140022698();
        plVar6 = (longlong *)(**(code **)(*plVar6 + 0x20))(plVar6);
        if (plVar6 == (longlong *)0x0) {
                    /* WARNING: Subroutine does not return */
          FUN_140004cc0(0x80004005);
        }
      }
      local_2d0 = (**(code **)(*plVar6 + 0x18))();
      local_2d0 = local_2d0 + 0x18;
      uVar5 = uVar12;
    }
    lVar7 = local_2d0;
    uVar14 = (ulonglong)uVar5;
    pwVar2 = pwVar11 + -0xc;
    if ((wchar_t *)(local_2d0 + -0x18) != pwVar2) {
      if ((*(int *)(pwVar11 + -4) < 0) || (*(longlong *)(local_2d0 + -0x18) != *(longlong *)pwVar2))
      {
        FUN_140004ce0(&local_2d8,local_2d0,*(undefined4 *)(local_2d0 + -0x10));
        pwVar11 = local_2d8;
      }
      else {
        lVar7 = FUN_140004630();
        LOCK();
        pwVar11 = pwVar11 + -4;
        iVar13 = *(int *)pwVar11;
        *(int *)pwVar11 = *(int *)pwVar11 + -1;
        UNLOCK();
        if (iVar13 < 2) {
          (**(code **)(**(longlong **)pwVar2 + 8))(*(longlong **)pwVar2,pwVar2);
        }
        local_2d8 = (wchar_t *)(lVar7 + 0x18);
        pwVar11 = local_2d8;
        lVar7 = local_2d0;
      }
    }
    wVar4 = *pwVar11;
    LOCK();
    piVar1 = (int *)(lVar7 + -8);
    iVar13 = *piVar1;
    *piVar1 = *piVar1 + -1;
    UNLOCK();
    if (iVar13 < 2) {
      (**(code **)(**(longlong **)(lVar7 + -0x18) + 8))();
    }
    if (wVar4 == L'\0') {
      bVar15 = false;
      pwVar11 = local_2d8;
      goto LAB_140005cb8;
    }
    FUN_140005f80(&local_2d8);
    pwVar11 = local_2d8;
    iVar13 = FID_conflict:_wcsicoll(local_2d8,L"/VPM_NonInteractive");
    if (iVar13 == 0) {
      if ((-1 < *(int *)(*param_2 + -0x10)) &&
         (lVar7 = FUN_1401bb6b4(*param_2,L"/VPM_NonInteractive"), lVar7 != 0)) {
        uVar12 = (uint)(lVar7 - *param_2 >> 1);
      }
      FUN_140006370(param_2,uVar12,0x13);
      FUN_1400155a0(local_2b8);
      local_2c0 = FUN_1401b7e10(0xb8,&PTR_14023bdc0);
      if (local_2c0 != 0) {
        uVar10 = FUN_140007aa0(local_2c0,local_278);
      }
      local_48 = uVar10;
      if (uVar10 != 0) {
        local_2e0 = 0;
        local_2e8 = 4;
        lVar7 = FUN_1400292d4(FUN_140012910,uVar10,0,0);
        *(longlong *)(uVar10 + 0x60) = lVar7;
        if (lVar7 != 0) {
          *(undefined4 *)(lVar7 + 0x50) = 0;
          ResumeThread(*(HANDLE *)(*(longlong *)(uVar10 + 0x60) + 0x58));
        }
      }
      lVar7 = local_2c8;
      iVar13 = *(int *)(local_2c8 + 0x1b4);
      while (iVar13 == 0) {
        Sleep(2000);
        iVar13 = *(int *)(lVar7 + 0x1b4);
      }
      iVar13 = *(int *)(lVar7 + 0x1b0);
      FUN_1400056a0(local_2b8);
      bVar15 = iVar13 == 0;
LAB_140005cb8:
      LOCK();
      pwVar2 = pwVar11 + -4;
      iVar13 = *(int *)pwVar2;
      *(int *)pwVar2 = *(int *)pwVar2 + -1;
      UNLOCK();
      if (iVar13 < 2) {
        (**(code **)(**(longlong **)(pwVar11 + -0xc) + 8))();
      }
      return bVar15;
    }
    if ((int)uVar5 < 0) {
                    /* WARNING: Subroutine does not return */
      FUN_140004cc0(0x80070057);
    }
  } while( true );
}

追加

実は今回の案件、最後は「私が現場を離れて、全台システム再インストール」で決着しました。
同僚や納期に影響を与えたことは申し訳なく思っています。

簡単な背景説明:
相手企業が数ページ分の紙手順を用意していて、全て手作業でインストールするよう求められました。そこで手順をまとめてバッチを作り、ちょっとサボりつつ、同僚の負担も減らそうとした というわけです。

第二フェーズで、いくつかのソフトのインストールがうまくいかず、少し調べたところソフト側の問題で、放置して自動更新を待てば解決できると判断しました。
しかし顧客は私の意見や説明を聞かず、私も当時は KY気味に何度も念押しし、メーカーの KB まで見せました。
注意の度合い、つまり KY の度合いは自分でもコントロールが難しく、結果的に他の面でも反抗的だと思われたのか、
手順違反だと受け取られて不安を与えてしまったようです。

顧客が問題のソフトを再インストールして、2回連続で失敗したタイミングで私がまた一言言ったところ、
しばらくして「今すぐ帰れ」と言われました。マニュアル通りにやっていないのが理由でした。


その後の展開はさらにドラマチックでした。
同僚の一人が、こちらがバッチで作業していたことをバレてしまい、私の罪が上乗せ。
自然な流れで、この案件遅延の主犯になりました。

でも追い出されたのは良かった面もあって、矛先が私に向いたことで、
現場に残った同僚には少し優しくなるだろうと思いました。


結局は私の落ち度です。最初から文書通りにやっていれば問題は起きなかったでしょう。
技術的なこだわりで勝手に軌道を外れてしまったのが原因です。

今振り返ると、顧客が数ページ分の紙手順を書かせる時点で、
楽な案件ではないことは明らかでした。
ただ会社としては初めての種類の案件で、業界最安レベルの価格で受けてしまった。
私はそもそもこういう顧客とのコミュニケーションが得意ではありません。
確認すべきことが多く、細かい確認をしているだけで一日が終わってしまいます。

ただ相手は発注元(クライアント)です。
曖昧な点は同僚に頼んでメールで確認してもらいました。

それに正直、数ページ分の手順書には実質的な指針がほとんどありませんでした。
手順を大量に書いているのに、最も基本的な OS イメージ(ISO)すら指定がないのです。
極端な話、Windows 21H2 を入れても手順上は問題ないことになります。


今回の件は深い教訓になりました。
こういう「プロセスドリブン」「イベント主導」の会社では、
細かく硬直化した手順ができあがると、効率は優先事項ではなくなります。
そしてそういう環境は、効率を優先する私には合わないのだと思います。

だからこそ、こんな単純な PC 初期化作業が外注に出されるのでしょう。
多くの会社では、こういう作業は内部で簡単に消化できます。
無人展開(Microsoft のドキュメントを少し読めば作れる unattend.xml やグループポリシー)を使えば、
一人で数十台を同時に処理するのも難しくありません。
台数がもっと多ければ、PXE のネットワークインストールも使えるでしょう。

しかしこの現場では、明らかに展開手順を理解していない人が手順書を作り、
技術者がそれを一行ずつ実行させられる構図でした。
私にとっては、かなり違和感のある体験でした。

追い出されて初めて気づいたのは、この仕組みではオペレーター側に裁量権がないということです。
もっと簡単に言えば「黙ってやれ、失敗したら責任を取れ」という構図です。
本質的には技術ロジックと手順要求の衝突であり、
技術的に意味の薄い手順を一つずつ守らされると、
専門家としての判断を否定されたような感覚になります。言い換えれば侮辱です。

結局のところ、私の技術が今回の案件では活きず、
双方にとって不幸でした。相手が求めているのは、
「少しだけPCに詳しい従業員」であって、私が本気になる必要はなかったのだと思います。

なので結論としては、
技術要素がほぼゼロで、完全にイベントドリブンな案件は、
できるだけ受けないのが最適解だと感じました。


一番ありえなかったのは、
「私が特殊なウイルスを仕込んだ」と言いがかりをつけられたことです。
そのウイルスはPCを壊すのではなく、OSを入れるためのUSBだけを壊すらしい。
しかも既に何本も壊れた、と。

少しでもPCを知っていれば、どれだけ荒唐無稽な話か分かるはずです。
落ち込んでいたはずが、思わず笑ってしまいました。
技術者同士で「USBだけ壊すウイルス」なんて、さすがにファンタジーすぎませんか。

もっとマシな理由を出すか、理由なしで「スクリプトを見せて」と言ってくれれば、普通に渡したのに。
しかもこの口実で、こちらの初期化デプロイ用スクリプトまで持っていかれました。
2026年にもなって、こんなレベルの陰謀論じみた言いがかりを受けるとは思いませんでした。
責任を押し付けながら、ちゃっかりスクリプトだけ持っていく。

* 私も遠隔でUSBを壊せる技術を学びたい。私の認識だと、物理破壊できる中国教室の黒板ギロチンくらいです:
seewo usb killer


それでも最後は少し冷静になれました。
顧客の Google Maps 評価はかなり微妙で、今回の体験とも一致します。
こういう「プロセス至上主義」の書類で権威を維持しないと、
この手の会社は生き残れないのだと思います。

最後に、私の中で 夢グループ の評価はさらに上がりました。
石田社長はとても優しい人だったんだなと。

まとめ

プログラマなら感覚的に分かると思いますが、
デバッグやデプロイを便利にするために隠しパラメータを埋め込むのは割と一般的な手法です。
ただし、こうしたパラメータはドキュメントに書かれていないことが多く、
逆に言えば逆アセンブルなどで掘り出せることも少なくありません。
(もちろんこの記事ではそこまでやっていなくて、単純に文字列検索しただけですが)。

ふと思い出しました。ブログの第一回記事(Hello World 除く)はこちらでした:
在 macOS 10.15 以上 刷入 IPCC 文件 / On macOS 10.15 or later enable carrier-testing
ブログの起点は「プログラムの隠しロジックを剥がす」だったなあと、高校時代を懐かしく思いました。





以上。






制作・著作
━━━━━
ⒽⓊⒼⒼⓎ