fwimgのチェックルーチン
ここではfwimgが、イメージを読み込んでからどんな処理をしているか、ghidraでの静的解析結果を元に見ていきます。
基本的に関数名や変数名は自分で定義したものになります。
このコードを読むのには、イメージのヘッダが重要なので比較しながらご覧ください。
code:shell
# GPLビルド/通常イメージ
$ hexdump -C dsm232.img | head
00000000 67 39 88 70 77 42 92 35 6f 70 f0 5e 00 00 8a 01 |g9.pwB.5op.^....|
00000010 00 00 00 00 00 00 00 00 16 65 b4 36 05 02 08 00 |.........e.6....|
00000020 44 53 4d 32 33 32 5f 46 6c 61 73 68 46 53 00 00 |DSM232_FlashFS..|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
# GPLビルド/ファクトリイメージ
$ hexdump -C dsm232_fact.img
00000000 67 39 88 70 99 57 19 83 6f 70 f0 5e 00 00 8a 01 |g9.p.W..op.^....|
00000010 00 00 00 00 00 00 00 00 16 65 b4 36 05 02 0d 00 |.........e.6....|
00000020 44 53 4d 32 33 32 5f 46 6c 61 73 68 46 53 00 00 |DSM232_FlashFS..|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
# 公式ビルド/通常イメージ
$ hexdump -C ../../dumped/DSM232A1_FW1.02.31JP_20150408_GCG.img
00000000 67 39 88 70 2c 3e aa 59 7c 99 24 55 c8 02 8c 01 |g9.p,>.Y|.$U....|
00000010 00 00 00 00 00 00 00 00 37 ca 3f 9b 05 02 04 00 |........7.?.....|
00000020 44 53 4d 32 33 32 5f 4d 75 6c 74 69 5f 49 6d 61 |DSM232_Multi_Ima|
00000030 67 65 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |ge..............|
では、早速コードの解説をしていきます。
まずは、次のコードでアップデートの中に「DSM232」という文字が含まれていないか検索し、ない場合に戻り値を-1に設定しています。ここで、FP_FOR_IMAGE_DATAはイメージのファイルポインタです。
code:fwup_main.c
fseek(FP_FOR_IMAGE_DATA,0,0);
fread(buffer,1,0x40,FP_FOR_IMAGE_DATA);
header_chars = buffer;
result_pointer = strstr(unknown_chars,"CAMEO_MANUFACTORY");
if ((result_pointer == (char *)0x0) &&
(result_pointer = strstr(header_chars + 0x20,"DSM232"), result_pointer == (char *)0x0)) {
printf("!1 Invalid product file(%s)\n",header_chars + 0x20);
isOk = 0xffffffff;
}
LEDの点滅設定をしたあと、ヘッダの「0x1e」に含まれるデータを確認しています。ここが4の場合はイメージがファームウェア内臓のAESキーで暗号化されているようです。GPLビルドの場合は暗号化されていないので、if内についての詳細は省きます。(header_chars[0x1e] == '\x04'がelseの方で説明していきます)
code:fwup_main.c
else {
setLEDStatus("led 200\n");
if (header_chars0x1e == '\x04') {
memset(aiStack148,0,0x28);
fseek(FP_FOR_IMAGE_DATA,0x40,0);
local_10 = 1;
local_c = 0;
while ((local_10 != 0 && (local_c < 10))) {
local_10 = fread(aiStack148 + local_c,1,4,FP_FOR_IMAGE_DATA);
if (aiStack148local_c == 0) break;
local_c = local_c + 1;
}
if (((local_c == 0) || (local_10 == 0)) || ((9 < local_c && (local_70 != 0)))) {
puts("error ?");
}
local_18 = local_c + 1;
local_14 = (local_c + 0x11) * 4;
local_20 = param_1 + (local_c + 0x11) * -4;
local_1c = 0;
while (local_1c < local_18 + -1) {
local_24 = aiStack148local_1c;
if (local_20 < (int)local_24) {
return 0xfffffffe;
}
isOk = readNewSectionImage(local_14);
local_2c = installApp(isOk,local_14,local_24,1);
if (local_2c < 0) {
puts("upgrade fail!");
waitFinishOrSleepMsec(3000);
return 0xfffffffd;
}
sync();
sync();
sync();
local_24 = local_24 + 3 & 0xfffffffc;
local_14 = local_14 + local_24;
local_20 = local_20 - local_24;
local_1c = local_1c + 1;
}
puts("upgrade success!");
waitFinishOrSleepMsec(0x5dc);
if (param_2 == 0) {
return 0;
}
puts("reboot!");
system("reboot");
}
else {
isOk = readNewSectionImage(0);
local_2c = installApp(isOk,0,param_1,0);
if (local_2c < 0) {
puts("upgrade fail!");
waitFinishOrSleepMsec(3000);
return 0xfffffffc;
}
puts("upgrade success!");
local_a0 = param_2;
if (local_2c == 0) {
local_a0 = 0;
}
if (local_a0 == 0) {
return 0;
}
puts("reboot!");
system("reboot");
}
isOk = 0;
}
}
return isOk;
ここから、システムは連続して記載のあるヘッダファイルも含め見ていきます。ここからはdsm232.imgのみで説明します。
code:shell
00000000 67 39 88 70 77 42 92 35 6f 70 f0 5e 00 00 8a 01 |g9.pwB.5op.^....|
00000010 00 00 00 00 00 00 00 00 16 65 b4 36 05 02 08 00 |.........e.6....|
00000020 44 53 4d 32 33 32 5f 46 6c 61 73 68 46 53 00 00 |DSM232_FlashFS..|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 27 05 19 56 a6 e9 4b 13 5e f0 70 6f 01 88 f7 32 |'..V..K.^.po...2|
00000050 00 00 80 00 00 00 80 00 b2 bb d4 b1 05 02 02 00 |................|
00000060 32 34 30 5f 53 44 4b 5f 67 63 6c 75 73 74 65 72 |240_SDK_gcluster|
00000070 5f 21 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |_!..............|
installAppでの処理により、app.shがどうやら実行されています。
code:install_app.c
prepareFile("/mnt/jffs2","/mnt/jffs2/app.tar.gz",param_2,param_3);
puts("Installing App..");
system("cd /mnt/jffs2/;gunzip -f app.tar.gz;tar -xf app.tar -C /mnt/jffs2;rm -f app.tar");
sync();
iVar1 = lookFileType("/mnt/jffs2/app.sh",auStack328);
if (iVar1 == 0) {
system("chmod 755 /mnt/jffs2/app.sh;/mnt/jffs2/app.sh");
unlink("/mnt/jffs2/app.sh");
sync();
}
puts("Done.");
}
else {
iVar1 = FUN_000094f8(8,param_2,param_3,flag_judger,param_4);
if (iVar1 < 0) {
return 0xffffffff;
}
iVar1 = FUN_000094f8(7,param_2,param_3,iVar1,param_4);
if (iVar1 < 0) {
return 0xffffffff;
}
}
}
なので、AppImageの要素内に「app.tar.gz」を入れ、その中に「app.sh」があれば、それを実行することになるようです。
これで、任意のシェルスクリプトを実行できます。
実際に試したい方は、
https://github.com/gpioblink/build-dsm232-docker
から、「シェルスクリプト起動のためのイメージ作成」をやってみてください。