1725281874
2024-09-02 08:20:46
2024年8月28日
要約
私は東芝のHDDのファームウェアアップデータをリバースしてLinux上でアップデートできるようにしました。次のコマンド すべき 仕事、 自己責任でご利用ください:
$ wget https://www.canvio.jp/en/support/download/hdd/ot_ihdd/fw/ISFw.dat
$ grep -C2 MODELNAME ISFw.dat
# ^
# |___ identify the right filename here
$ wget https://www.canvio.jp/en/support/download/hdd/ot_ihdd/fw/sk060202.ftd
# hdparm --fwdownload-mode3 sk060202.ftd /dev/sdX
コンテクスト
NAS用に東芝HDWG480 HDDを購入しました。 hdparm -I /dev/XXX 次のような出力が得られます。
ATA device, with non-removable media
Model Number: TOSHIBA HDWG480
Serial Number: 3430A00RFR0H
Firmware Revision: 0601
Transport: Serial, ATA8-AST, SATA 1.0a, SATA II Extensions, SATA Rev 2.5, SATA Rev 2.6, SATA Rev 3.0
Standards:
Used: unknown (minor revision code 0x006d)
Supported: 10 9 8 7 6 5
Likely used: 10
[...]
いつものように、ファームウェアのアップデートがあるかどうかを確認したいと思いました。東芝の 専用ウェブページ リストバージョン 0602 私のモデルで利用可能なものとして。
残念ながら、予想通り、Linux ユーザー向けにファームウェア更新プロセスは提供されておらず、Windows 向けには「内部ストレージ ファームウェア更新ユーティリティ」のみが提供されています。
アップデートファイルも提供されません。
目標
したがって、私たちの目標は次のとおりです。
- アップデータが更新ファイルをどこから取得するかを理解する
- フラッシュプロセス自体をリバースエンジニアリングしてLinux上で再実装する
Windows Updater のリバース
はじめに
実行中 インストーラ Wineは完璧に動作し、以下のファイルが Program Files (x86):
18312 ISFw.exe: PE32 executable (native) Intel 80386, for MS Windows, 4 sections
2434952 TosISFw.exe: PE32 executable (GUI) Intel 80386, for MS Windows, 5 sections
2172296 TosISFwSvc.exe: PE32 executable (GUI) Intel 80386, for MS Windows, 5 sections
2362248 TosISFwTray.exe: PE32 executable (GUI) Intel 80386, for MS Windows, 5 sections
ざっと見たところ (ファイル名、インポート)、各プログラムの目標は次のようになります。
ISFW.exeドライバー(DriverEntryエクスポート) は、更新プログラムを効果的にフラッシュする役割を担っていると考えられます。TosISFw.exeGUIはTosISFwSvc.exeはユーザーランドサービスです(サービス関連のインポートが示すように)TosISFwTray.exeおそらくトレイアイコンを処理する
更新ファイルの検索
明らかな動きは、インストールされたさまざまなバイナリのURLをgrepすることです。残念ながら、デジタル署名に関連するURL以外は何も見つかりません。しかし、 HttpOpenRequestは、Windows プログラムがファイルをダウンロードするためによく使用する API であり、次の 2 つの結果を返します。 TosISFw.exe そして TosISFwSvc.exe。
見てみましょう TosISFwSvc.exe これは小さいので、xref をチェックして URL を見つけられるかどうか確認してみましょう。
呼び出しは関数 0x00401040 にあり、次のようになります。
v15 = HttpOpenRequestW(v14, L"GET", &v36[(_DWORD)lpBuffer], 0, (LPCWSTR)szReferrer, 0, 0x84000000, 0);
この関数は明らかに「ダウンロード」ヘルパーであり、API呼び出しからそれがわかる。名前を変更しよう。 dlfiledlfile への Xref は 2 つだけです。
if ( !RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
L"SYSTEM\CurrentControlSet\Services\TosISFwSvc",
0,
0x20019u,
&phkResult)
&& readregstring((LPBYTE)&String, &phkResult, L"FwURL")
&& lstrlenW(&String) )
{
sub_401000();
LOBYTE(v47) = 2;
if ( dlfile(&String, (int)v38) )
[...]
sub_4052E0(&lpValueName, L"%s%d", L"URL", phkResult);
v25 = 0;
if ( !RegOpenKeyExW(
HKEY_LOCAL_MACHINE,
L"SYSTEM\CurrentControlSet\Services\TosISFwSvc",
0,
0x20019u,
&v25)
&& readregstring((LPBYTE)&String, &v25, lpValueName)
&& lstrlenW(&String)
&& dlfile(&String, (int)v36) )
最初のものが答えです。URL はレジストリに保存されます。これは実際には InstallShield セットアップによって書き込まれます。
価値は http://www.canvio.jp/en/support/download/hdd/ot_ihdd/fw/ISFw.dat
更新ファイルの解析
このファイルは ini ファイルなので、読み取って解析するのは簡単です。
[VERS]
VERSION="20240513"
[Firmware]
0000=qa060378.ftd
0000model="TOSHIBA HDWG21E"
0000rev="0603"
0000rev0000="0601"
0000native=0
0000option=0
0001=qa060378.ftd
0001model="TOSHIBA HDWG21C"
0001rev="0603"
0001rev0000="0601"
0001native=0
0001option=0
[...]
0008=sk060202.ftd
0008model="TOSHIBA HDWG480 "
0008rev="0602"
0008rev0000="0601"
0008native=0
0008option=0
[...]
; 905CBD24
私の場合、ドライブ番号は8です。興味深いのは、最後のチェックサムです。これは、ファイルのCRC32から最後の10バイトを除いたもので、 slice そして crc32 私のハッキング用スイスアーミーナイフの道具 rsbkb:
$ slice -- ISFw.dat 0 -10 | crc32
905cbd24
さて、当然ですが、関連するファイルをダウンロードしてみましょう。
$ wget https://www.canvio.jp/en/support/download/hdd/ot_ihdd/fw/sk060202.ftd
Resolving www.canvio.jp (www.canvio.jp)... 23.72.248.205, 23.72.248.202
Connecting to www.canvio.jp (www.canvio.jp)|23.72.248.205|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1171456 (1.1M)
[...]
楽しみのために確認してみましょう cpu_rec_rs バイナリ内の任意のコードを識別できます。
$ ~/tools/cpu_rec_rs/cpu_rec_rs sk060202.ftd
Loading corpus from "/home/trou/tools/cpu_rec_rs/cpu_rec_corpus/*.corpus"
-------------------------------------------------
File | Range | Detected Architecture
-------------------------------------------------
sk060202.ftd | Whole file | ARMhf
-------------------------------------------------
したがって、ファームウェアはおそらく ARM SoC 上で実行されています (実際に実行されています)。
更新プロセスを理解する
さて、実際にアップデートを実行するために、ファイルはどのようにドライブに送信されるのでしょうか? 4つのバイナリがあり、 ISFW.exe 実はドライバーです。
の DriverEntry 関数は実際には非常に単純です:
NTSTATUS __stdcall DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
int v2; // eax
readregistry();
v2 = flashfirmware();
sub_1001812(v2 % 100 == 0, 1, v2);
return NtTerminateProcess((HANDLE)0xFFFFFFFF, 0);
}
すでに名前を変更しました readregistry そして flashfirmware 関数は簡単に識別できます。
char readregistry()
{
[...]
RtlInitUnicodeString(&DestinationString, L"\REGISTRY\MACHINE\SYSTEM\CurrentControlSet\Services\TosISFwSvc");
[...]
if ( NtOpenKey(&KeyHandle, 0x20019u, &ObjectAttributes) >= 0 )
{
RtlInitUnicodeString(&ValueName, L"FW_Serial");
if ( NtQueryValueKey(KeyHandle, &ValueName, KeyValuePartialInformation, KeyValueInformation, 0x800u, &ResultLength) >= 0 )
{
memcpy(&fwserial, &KeyValueInformation[3], KeyValueInformation[2]);
[...]
RtlInitUnicodeString(&ValueName, L"FW_CurRev");
memcpy(&fw_cur, &KeyValueInformation[3], KeyValueInformation[2]);
[...]
RtlInitUnicodeString(&ValueName, L"FW_NewRev");
memcpy(fw_new, &KeyValueInformation[3], KeyValueInformation[2]);
[...]
RtlInitUnicodeString(&ValueName, L"FW_Model");
memcpy(fw_model, &KeyValueInformation[3], KeyValueInformation[2]);
[...]
RtlInitUnicodeString(&ValueName, L"FW_FWFile");
wmemcpy(path, L"\??\", 4);
memcpy(&path + 4, &KeyValueInformation[3], KeyValueInformation[2]);
[...]
}
レジストリ値( TosISFwSvc.exe) が読み取られ、グローバル変数にコピーされ、レジストリ値の名前に応じて名前が変更されました。
これが始まりです flashfirmware:
int flashfirmware()
{
[...]
Handle = 0;
fwdata = 0;
fwsize = 0;
memset(&drivedata, 0, sizeof(drivedata));
printf(L"%s Firmware: %s -> %sn", fw_model, &fw_cur, fw_new);
printf(L"DO NOT TURN OFF THE PC WHILE ANY FIRMWARE UPDATE IS RUNNING.n");
printf(
L"Your device may become unusable if you do this and Toshiba is not n"
"responsible for any damage, including any necessary replacement of n"
"the unit, caused by your doing so.n");
HeapHandle = RtlCreateHeap(2u, 0, 0, 0, 0, 0);
if ( HeapHandle )
{
status = readfile(&path, &fwdata, &fwsize);
if ( !(status % 100) )
{
Handle = verifydisk(&fwserial, &fw_cur, fw_model, &drivedata);
[...]
verifydisk 非常に重要ですが、比較的単純です (すべてがすでに名前変更されています)。
HANDLE __stdcall verifydisk(PCWSTR serial, PCWSTR cur, WCHAR *model, IDENTIFY_DEVICE_DATA *devdata)
{
HANDLE hdl; // edi
UNICODE_STRING cur_; // [esp+10h] [ebp-104h] BYREF
struct _UNICODE_STRING serial_; // [esp+18h] [ebp-FCh] BYREF
UNICODE_STRING model_from_drive_u; // [esp+20h] [ebp-F4h] BYREF
UNICODE_STRING serial_from_drive_u; // [esp+28h] [ebp-ECh] BYREF
UNICODE_STRING model_; // [esp+30h] [ebp-E4h] BYREF
UNICODE_STRING fwrev_from_drive_u; // [esp+38h] [ebp-DCh] BYREF
DWORD *drivenumber; // [esp+40h] [ebp-D4h]
HANDLE hdl_; // [esp+44h] [ebp-D0h]
char v14; // [esp+4Bh] [ebp-C9h] BYREF
WCHAR model_from_drive[50]; // [esp+4Ch] [ebp-C8h] BYREF
WCHAR serial_from_drive[30]; // [esp+B0h] [ebp-64h] BYREF
WCHAR fwrev_from_drive[18]; // [esp+ECh] [ebp-28h] BYREF
[...]
for ( drivenumber = 0; (unsigned int)drivenumber 0x20; drivenumber = (DWORD *)((char *)drivenumber + 1) )
{
[...]
hdl = opendrive((char)drivenumber);
if ( !hdl )
break;
if ( !getdevprop(hdl, &bustype) || bustype == BusTypeUsb ) {
NtClose(hdl);
} else {
if ( get_IDENTIFY_DEVICE_DATA(hdl_, devdata, 0x200u) ) {
get_drive_serial(devdata, serial_from_drive, 30, 1);
get_drive_fw_rev(devdata, fwrev_from_drive, 18, 1);
get_drive_model(devdata, model_from_drive, 50, 1);
RtlInitUnicodeString(&serial_from_drive_u, serial_from_drive);
RtlInitUnicodeString(&fwrev_from_drive_u, fwrev_from_drive);
RtlInitUnicodeString(&model_from_drive_u, model_from_drive);
if ( RtlEqualUnicodeString(&serial_, &serial_from_drive_u, 0) )
{
if ( RtlEqualUnicodeString(&cur_, &fwrev_from_drive_u, 0)
&& RtlEqualUnicodeString(&model_, &model_from_drive_u, 0) )
{
return hdl_;
}
}
}
NtClose(hdl_);
}
}
return 0;
}
ドライブとのやり取り
ドライブタイプの確認
詳しく見ていきましょう opendrive そして getdevprop 機能:
HANDLE __stdcall opendrive(char Args)
{
[...]
HANDLE FileHandle; // [esp+30h] [ebp-88h] BYREF
WCHAR SourceString[64]; // [esp+34h] [ebp-84h] BYREF
DestinationString.Length = 0;
*(_DWORD *)&DestinationString.MaximumLength = 0;
HIWORD(DestinationString.Buffer) = 0;
memset(SourceString, 0, sizeof(SourceString));
FileHandle = 0;
wsprintf(SourceString, 64, (wchar_t *)L"\??\PhysicalDrive%u", Args);
RtlInitUnicodeString(&DestinationString, SourceString);
[...]
NtOpenFile(&FileHandle, 0x100003u, &ObjectAttributes, &IoStatusBlock, 3u, 0x20u);
return FileHandle;
}
char __stdcall getdevprop(HANDLE hdl, char *bustype)
{
char tmp; // al
struct _IO_STATUS_BLOCK IoStatusBlock; // [esp+Ch] [ebp-1018h] BYREF
char *bustype_; // [esp+14h] [ebp-1010h]
HANDLE FileHandle; // [esp+18h] [ebp-100Ch]
char retvalue; // [esp+1Fh] [ebp-1005h]
STORAGE_DEVICE_DESCRIPTOR InputBuffer; // [esp+20h] [ebp-1004h] BYREF
FileHandle = hdl;
bustype_ = bustype;
IoStatusBlock.Status = 0;
IoStatusBlock.Information = 0;
retvalue = 0;
memset(&InputBuffer, 0, 0x1000u);
if ( NtDeviceIoControlFile( hdl, 0, 0, 0, &IoStatusBlock,
IOCTL_STORAGE_QUERY_PROPERTY,
&InputBuffer, 0x1000u, &InputBuffer, 0x1000u) 0 ) {
tmp = 0;
} else {
tmp = InputBuffer.BusType;
retvalue = 1;
}
if ( bustype_ )
*bustype_ = tmp;
return retvalue;
}
opendrive 指定されたハンドルを返します PhysicalDrive、その後、 getdevpropの NtDeviceIoControlFileIDAの「標準列挙型」を使用して、 0x2D1400 読みやすい定義は次のとおりです。 IOCTL_STORAGE_QUERY_PROPERTY。
として InputBuffer 呼び出し前に0に設定されている場合、返されるデータは STORAGE_DEVICE_DESCRIPTOR 構造は、 verifydisk ドライブがUSB経由で接続されているかどうかを確認するには(BusTypeUsb) であり、その場合は撤退します。
ドライブモデルの確認
get_IDENTIFY_DEVICE_DATA その後、 verifydisk:
char __stdcall get_IDENTIFY_DEVICE_DATA(HANDLE hdl, void *buff, size_t Size)
{
struct _IO_STATUS_BLOCK IoStatusBlock; // [esp+Ch] [ebp-3Ch] BYREF
HANDLE FileHandle; // [esp+14h] [ebp-34h]
char v6; // [esp+1Bh] [ebp-2Dh]
ATA_PASS_THROUGH_DIRECT InputBuffer; // [esp+1Ch] [ebp-2Ch] BYREF
FileHandle = hdl;
IoStatusBlock.Status = 0;
v6 = 0;
IoStatusBlock.Information = 0;
memset(buff, 0, Size);
memset(&InputBuffer, 0, sizeof(InputBuffer));
InputBuffer.Length = 0x28;
InputBuffer.AtaFlags = ATA_FLAGS_DRDY_REQUIRED|ATA_FLAGS_DATA_IN|ATA_FLAGS_NO_MULTIPLE;
InputBuffer.DataTransferLength = Size;
InputBuffer.TimeOutValue = 10;
InputBuffer.DataBuffer = buff;
InputBuffer.CurrentTaskFile[reg_Command] = 0xEC;
if ( NtDeviceIoControlFile(hdl, 0, 0, 0, &IoStatusBlock,
IOCTL_ATA_PASS_THROUGH_DIRECT,
&InputBuffer, 0x28u, &InputBuffer, 0x28u) >= 0
&& (InputBuffer.CurrentTaskFile[reg_Status] & 9) == 0 )
{
return 1;
}
return v6;
}
NtDeviceIoControlFile 現在は IOCTL_ATA_PASS_THROUGH_DIRECTは、名前が示すように、ドライブに生のATAコマンドを送信します。実際にこの要求を理解するのは、 ATA_PASS_THROUGH_DIRECT 構造体はデータバッファと「レジスタ」の両方を CurrentTaskFile 分野。
CurrentTaskFile は、入力と出力の両方で 8 つのレジスタをインデックスするために使用される配列です。ドキュメントを使用して、IDA で使用する 2 つの列挙型を作成できます。
enum ATA_INPUT_REGISTERS : __int32
{
reg_Features = 0x0,
reg_Sector_Count_in = 0x1,
reg_Sector_Number_in = 0x2,
reg_Cylinder_Low_in = 0x3,
reg_Cylinder_High_in = 0x4,
reg_Device_Head_in = 0x5,
reg_Command = 0x6,
reg_Reserved = 0x7,
};
enum ATA_OUTPUT_REGISTERS : __int32
{
reg_Error = 0x0,
reg_Sector_Count_out = 0x1,
reg_Sector_Number_out = 0x2,
reg_Cylinder_Low_out = 0x3,
reg_Cylinder_High_out = 0x4,
reg_Device_Head_out = 0x5,
reg_Status = 0x6,
reg_Reserved_out = 0x7,
};
ここでのコマンドは 0xECATA/ATAPIコマンドセット仕様は、 ここは、 IDENTIFY DEVICE – ECh, PIO Data-In コマンドは 多く データの量は膨大です。ありがたいことに、マイクロソフトは IDENTIFY_DEVICE_DATA すべてを備えた構造。
次のコードは、返されたデータのシリアル、モデル、ファームウェア バージョンをレジストリに保存されているものと比較することで、「正しい」ドライブがあることを確認します。
int __stdcall get_drive_serial(IDENTIFY_DEVICE_DATA *drivedata, wchar_t *dest, int destlen, char stripflag)
{
return (int)getdrive_data_string( drivedata, dest, destlen,
offsetof(IDENTIFY_DEVICE_DATA, SerialNumber), 20,
stripflag);
}
[...]
get_drive_serial(devdata, serial_from_drive, 30, 1);
[...]
RtlInitUnicodeString(&serial_from_drive_u, serial_from_drive);
[...]
if ( RtlEqualUnicodeString(&serial_, &serial_from_drive_u, 0) )
{
if ( RtlEqualUnicodeString(&cur_, &fwrev_from_drive_u, 0)
&& RtlEqualUnicodeString(&model_, &model_from_drive_u, 0) )
実際にファームウェアファイルを送信する
ドライバーがディスクが実際にフラッシュ可能であることを識別して検証すると、実際の更新が続行されます。
[...]
MaxBlocksPerDownloadMicrocodeMode03 = drivedata.MaxBlocksPerDownloadMicrocodeMode03;
if ( !drivedata.MaxBlocksPerDownloadMicrocodeMode03 || drivedata.MaxBlocksPerDownloadMicrocodeMode03 == 0xFFFF ) {
MaxBlocksPerDownloadMicrocodeMode03 = 128;
} else if ( drivedata.MaxBlocksPerDownloadMicrocodeMode03 >= 0x80u ) {
MaxBlocksPerDownloadMicrocodeMode03 = 128;
}
if ( MaxBlocksPerDownloadMicrocodeMode03 >= drivedata.MinBlocksPerDownloadMicrocodeMode03
&& MaxBlocksPerDownloadMicrocodeMode03 ) {
fwblocks = fwsize >> 9;
fwblocks2 = fwsize >> 9;
v1 = 60;
do {
printprogress();
wait((LARGE_INTEGER)500LL);
--v1;
} while ( v1 );
for ( fwsize = 0; (int)fwsize 30; ++fwsize ) {
currentblock = 0;
status = 6000;
if ( fwblocks ) {
fwdata1 = fwdata;
MaxBytesPerDL = MaxBlocksPerDownloadMicrocodeMode03 9;
while ( 1 ) {
printprogress();
blocks_to_flash = fwblocks2 - currentblock;
if ( MaxBlocksPerDownloadMicrocodeMode03 fwblocks2 - currentblock )
blocks_to_flash = MaxBlocksPerDownloadMicrocodeMode03;
if ( !ATA_CMD_DOWNLOAD_MICRO(Handle, currentblock, blocks_to_flash, fwdata1) )
break;
currentblock += MaxBlocksPerDownloadMicrocodeMode03;
fwdata1 += MaxBytesPerDL;
if ( currentblock >= fwblocks2 )
goto LABEL_25;
}
status = 6009;
LABEL_25:
fwblocks = fwblocks2;
}
if ( !(status % 100) )
break;
v5 = 2;
do {
printprogress();
wait((LARGE_INTEGER)500LL);
--v5;
} while ( v5 );
}
if ( !(status % 100) )
{
if ( get_IDENTIFY_DEVICE_DATA(Handle, &drivedata, 0x200u) ) {
get_drive_fw_rev(&drivedata, newfwrev, 18, 1);
if ( wcsncmp(fw_new, newfwrev, wcslen(fw_new)) )
status = 6011;
} else {
status = 6010;
}
}
} else {
LABEL_35:
status = 6006;
}
[...]
if ( status % 100 )
printf(L"Update Failed. n");
else
printf(L"Update Succeeded. n");
ご覧のとおり、アップデータはドライブ情報データから興味深いフィールドを検証します。 MaxBlocksPerDownloadMicrocodeMode03これが何を意味するのか確認してみましょう。
ATAファームウェア更新コマンドの送信
ドキュメント
ATA コマンド セットからの次の抜粋は、フィールドの意味を説明しています。
A.11.5.3.4 DM MAXIMUM TRANSFER SIZE field
If:
a) the value of the DM MAXIMUM TRANSFER SIZE field (see table A.30) is greater than zero;
b) the value of the DM MAXIMUM TRANSFER SIZE field is less than FFFFh;
c) the DOWNLOAD MICROCODE SUPPORTED bit (see A.11.5.2.20) is set to one or the DOWNLOAD MICROCODE DMA SUPPORTED bit (see A.11.5.2.6) is set to one; and
d) the DM OFFSETS DEFERRED SUPPORTED bit (see A.11.5.3.1) is set to one, or the DM OFFSETS IMMEDIATE
SUPPORTED bit (see A.11.5.3.3) is set to one, then the DM MAXIMUM TRANSFER SIZE field indicates the maximum number of 512-byte data blocks permitted by a DOWNLOAD MICROCODE command (see 7.7) or a DOWNLOAD MICROCODE DMA command (see 7.8) that specifies a subcommand of:
a) Download with offsets and save microcode for immediate and future use (i.e., 03h); or
b) Download with offsets and save microcode for future use (i.e., 0Eh).
Otherwise, no maximum is indicated (i.e., there is no maximum number of 512-byte data blocks).
The IDENTIFY DEVICE data contains a copy of the DM MAXIMUM TRANSFER SIZE field (see IDENTIFY DEVICE
data word 235 in table 45).
もちろん、これを確認したい DOWNLOAD MICROCODE 指示:
The DOWNLOAD MICROCODE command allows the host to alter the device’s microcode. The data transferred
using the DOWNLOAD MICROCODE command and the DOWNLOAD MICROCODE DMA command is vendor
specific.
[...]
Downloading and activating microcode involves the following steps:
1) download: the host transfers updated microcode data to the device in one or more DOWNLOAD
MICROCODE commands or DOWNLOAD MICROCODE DMA commands;
2) save: after receiving the complete updated microcode data, if specified by the download microcode
mode, then the device shall save the updated microcode data to nonvolatile storage; and
3) activate: the device begins using the saved or deferred microcode data for the first time after an event
specified by the download microcode mode and the saved or deferred microcode data becomes the
active microcode data.
The BLOCK COUNT field specifies the number of 512-byte data blocks that shall be transferred. The BLOCK COUNT
field is specified in the COUNT field and the LBA field (see table 37).
DOWNLOAD サブコマンドは実際に更新動作を定義します。
実際のコード
char ATA_CMD_DOWNLOAD_MICRO(HANDLE FileHandle, __int16 currentblock, int blocks_to_flash, void *fwdata)
{
struct _IO_STATUS_BLOCK IoStatusBlock; // [esp+Ch] [ebp-38h] BYREF
char v6; // [esp+17h] [ebp-2Dh]
ATA_PASS_THROUGH_DIRECT InputBuffer; // [esp+18h] [ebp-2Ch] BYREF
IoStatusBlock.Status = 0;
IoStatusBlock.Information = 0;
memset(&InputBuffer, 0, sizeof(InputBuffer));
InputBuffer.Length = 0x28;
InputBuffer.AtaFlags = ATA_FLAGS_DRDY_REQUIRED|ATA_FLAGS_DATA_OUT|ATA_FLAGS_NO_MULTIPLE;
*(_WORD *)&InputBuffer.CurrentTaskFile[reg_Sector_Count_in] = blocks_to_flash;// BLOCK COUNT
*(_WORD *)&InputBuffer.CurrentTaskFile[reg_Cylinder_Low_in] = currentblock;// BUFFER OFFSET
v6 = 0;
InputBuffer.DataTransferLength = blocks_to_flash 9;
InputBuffer.TimeOutValue = 70;
InputBuffer.DataBuffer = fwdata;
InputBuffer.CurrentTaskFile[reg_Features] = 3;// mode 3
InputBuffer.CurrentTaskFile[reg_Device_Head_in] = 0xE0;// OBSOLETE7|N/A|OBSOLETE5
InputBuffer.CurrentTaskFile[reg_Command] = IDE_COMMAND_DOWNLOAD_MICROCODE;
if ( NtDeviceIoControlFile(FileHandle, 0, 0, 0, &IoStatusBlock,
IOCTL_ATA_PASS_THROUGH_DIRECT,
&InputBuffer, 0x28u, &InputBuffer, 0x28u) >= 0
&& (InputBuffer.CurrentTaskFile[6] & 9) == 0 )// status
{
return 1;
}
return v6;
}
ご覧の通り、 ATA_CMD_DOWNLOAD_MICRO 仕様に従っているだけです。唯一奇妙な点は Device レジスタは基本的に廃止されていますが、 0xE0念のため確認してみた hdparm ソースコードでコマンドに設定された値を確認すると、実際に次のように設定されています。 0xE0なので、おそらくレガシーな残骸でしょう:
enum {
ATA_USING_LBA = (1 6),
ATA_STAT_DRQ = (1 3),
ATA_STAT_ERR = (1 0),
};
[...]
r->lob.dev = 0xa0 | ATA_USING_LBA;
結論
つまり、基本的にアップデータは次のことを行います。
- 更新リストをダウンロードする
- ドライブが一致するかどうかを確認し、レジストリ値を設定します
- ドライバーが引き継ぎ、
- ドライブがUSB経由で接続されていないかどうかを確認します
- レジストリで指定された実際のドライブであることを確認するには、
IDENTIFY DEVICE指示 - ループしてファームウェアアップデートを128個の512バイトのチャンクで送信します。
DOWNLOAD MICROCODE指示 - ドライブが更新されたことを確認する
IDENTIFY DEVICE指示
実際にアップデートする
YOLO、メインの NAS ドライブで試してみました:
# hdparm -I /dev/sdb | grep Firmware
Firmware Revision: 0601
# hdparm --fwdownload-mode3 sk060202.ftd --yes-i-know-what-i-am-doing --please-destroy-my-drive /dev/sdb
/dev/sdb:
fwdownload: xfer_mode=3 min=1 max=4224 size=512
...............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
. Done.
# hdparm -I /dev/sdb | grep Firmware
Firmware Revision: 0602
o/
#Linux #上で #Toshiba #NAS #HDD #ファームウェアをアップグレードする #Bof別のブログ
