ccastle
@ccastle

Are you sure you want to delete the question?

Leaving a resolved question undeleted may help others!

書き込んで読み込んだ同一のバイナリファイルから作成したハッシュ値が一致しない

環境

使用しているOS: Windows11
使っているコンパイラ: gcc(version8.3.0)

解決したいこと

ゲームのセーブ機能にハッシュ値を使ってデータが改竄されていないかを調べる機能を取り入れようと考えているのですが、セーブする際に(バイナリファイルに書き込む時に)書き込む値からハッシュ値を作って、その作ったバイナリファイルを読み込んでハッシュ値を作ると値が一致しないと出ます。

該当するソースコード(中略有り)

#include <Windows.h>
#include <stdio.h>
#include <openssl\sha.h>
#include <time.h>
#include <math.h>
typedef struct
{
    int lv_limit;
    int lv;
    char name[110];
    char yourname[105];
    int curhp;
    int maxhp;
    int curmp;
    int maxmp;
    int atk;
    int n_atk;
    int def;
    int mana;
    int espro;
    int avo_pro;
    int cr_pro;
    int ne_exp;
    int no_exp;
    int no_mon;
    double tru_no_exp;
    int m_abnom_f[5][2];
    int m_abnom[6];
    int ab_d;
    int ab_resist[6];
    int w_at[6];
    int m_ski_num;
    int m_abre_f[2][2];
    int equip[5];
    int wea_dam_cou[2];
    int wea_dam[4][2];
    int have_item[283][2];
    int in_dungeon;
    int buy_his[500];
    int buy_lim[500];
    int buy_lv[500];
    int location[3];
    int not_move;
    int befo_room;
    int befo_warp;
    int en;
    int sp_count;
    int sp_comma;
    int sp_eq_get;
    int alive_boss[3];
    int knowloca;
    int lv_shop;
    int dk_turn;
    int dk;
    int dk_per;
    int absorb[4];
    int viol;
    unsigned char has_d[SHA256_DIGEST_LENGTH];
} A_STATUS;
#define INT_SHA_UP(num)                                            \
    do                                                             \
    {                                                              \
        memset(buf, '\0', sizeof(buf));                            \
        snprintf(buf, sizeof(buf), "%d", num);                     \
        SHA256_Update(&ctx256, (unsigned char *)buf, sizeof(buf)); \
    } while (0)
#define DOU_SHA_UP(dec)                                            \
    do                                                             \
    {                                                              \
        memset(buf, '\0', sizeof(buf));                            \
        snprintf(buf, sizeof(buf), "%.lf", dec);                   \
        SHA256_Update(&ctx256, (unsigned char *)buf, sizeof(buf)); \
    } while (0)
#define CHA_SHA_UP(chr)                                            \
    do                                                             \
    {                                                              \
        memset(buf, '\0', sizeof(buf));                            \
        memmove(buf, chr, strlen(chr));                            \
        SHA256_Update(&ctx256, (unsigned char *)buf, sizeof(buf)); \
    } while (0)
#define OPEN_R(filenum, fp)                                                                \
    do                                                                                     \
    {                                                                                      \
        sprintf_s(file_number, sizeof(file_number), "savefiles\\savedata%d.bin", filenum); \
        fp = fopen(file_number, "rb");                                                     \
    } while (0)
#define OPEN_W(filenum, fp)                                                                \
    do                                                                                     \
    {                                                                                      \
        mkdir("savefiles");                                                                \
        sprintf_s(file_number, sizeof(file_number), "savefiles\\savedata%d.bin", filenum); \
        fp = fopen(file_number, "wb");                                                     \
    } while (0)
static A_STATUS save_d[100] = {{0}};
static char file_number[27];
int hash_comp(unsigned char *hash_a, unsigned char *hash_b, int a_size, int b_size)
{
    int comp = 1;
    if (b_size == a_size)
    {
        int i = 0;
        while ((i < b_size) && (hash_b[i] == hash_a[i]))
        {
            i += 1;
        }
        if (i == b_size)
        {
            comp = 0;
        }
    }
    return comp;
}
int main(void)
{
    unsigned int now = (unsigned int)time(0);
    srand(now);
    rand();
    rand();
    rand();
    rand();
    rand();
    /*(保存するメンバの初期化)*/

    unsigned char ucHash[SHA256_DIGEST_LENGTH];
    mkdir("hash_save");
    FILE *fp = NULL;
    int save_data = 1 + rand() % 100;
    /*保存するファイル番号の選択*/

    OPEN_W(save_data, fp);
    char buf[10000];
    SHA256_CTX ctx256;
    SHA256_Init(&ctx256);/*初期化*/
    memset(buf, '\0', sizeof(buf));
    snprintf(buf, sizeof(buf), "%d", save_d[0].lv_limit);
    SHA256_Update(&ctx256, (unsigned char *)buf, sizeof(buf));
    CHA_SHA_UP(save_d[0].name);
    INT_SHA_UP(save_d[0].curhp);
    DOU_SHA_UP(save_d[0].tru_no_exp);
    /*中略*/
    /*保存するメンバの値をSHA256_UPDATE()へと渡した*/

    SHA256_Final(save_d[0].has_d, &ctx256);/*ハッシュ値を保存するメンバへ渡す*/
    printf("\n");
    fwrite(save_d + 1 - 1, sizeof(save_d), sizeof(save_d[0]) / sizeof(A_STATUS), fp);/*バイナリファイル書き込み*/
    fclose(fp); /*ファイルポインタ閉じる*/
    FILE *fp2 = NULL;
    memset(save_d[0].name, '\0', sizeof(save_d[0].name));
    memset(save_d[0].yourname, '\0', sizeof(save_d[0].yourname));
    OPEN_R(save_data, fp);/*読み込みモードでバイナリファイルを開く*/
    fread(save_d + 1 - 1, sizeof(save_d), sizeof(save_d[0]) / sizeof(A_STATUS), fp2);/*バイナリファイル読み込み*/
    fclose(fp2); /*ファイルポインタ閉じる*/
    SHA256_Init(&ctx256);
    memset(buf, '\0', sizeof(buf));
    snprintf(buf, sizeof(buf), "%d", save_d[0].lv_limit);
    SHA256_Update(&ctx256, (unsigned char *)buf, sizeof(buf));
    CHA_SHA_UP(save_d[0].name);
    INT_SHA_UP(save_d[0].curhp);
    DOU_SHA_UP(save_d[0].tru_no_exp);
    /*順番は正しいので中略*/
    /*読み込んだ値をSHA256_UPDATE()へ渡した*/
    SHA256_Final(ucHash, &ctx256);/*ハッシュ血をucHashへと渡す*/
    printf("\n");
    if (hash_comp(ucHash, save_d[0].has_d, sizeof(ucHash) / sizeof(unsigned char), sizeof(save_d[0].has_d) / sizeof(unsigned char)) == 0)/*保存する際に作ったハッシュ値とその保存した値などを読み込んで作ったハッシュ値が同一である*/
        printf("ucHash==ucHash.\n");
    else/*同一でない(こうなる)*/
        printf("ucHash!=ucHash.\n");
    return EXIT_SUCCESS;
}

結果

ucHash!=ucHash.

自分で試したこと

より単純な構造体の配列でやってみた時は上手くいきました。(その時は読み込みと書き込みをするバイナリファイルに数字は付けなかった)その構造体は以下になります。

struct data
{
    char name[30];
    int star;
    int atk;
    int sample[500];
    int item[183][2];
    unsigned char ucHash2[SHA256_DIGEST_LENGTH];
};
struct data CHARACTER[2] = {{"冒険者a", 55, 68730, {0}, {{0, 0}, {0, 0}}}, {"魔女b", 32, 12980, {0}, {{2, 25}, {4, 10}}}};

上記のコードではないですが、その前には日本語の部分や配列をSHA256_UPDATEに渡さずに行うなどしましたが変化はありませんでした。後はSHA256_CTX ctx2などと2つ目の宣言を行えば最初は上手くいきましたがゲームを進めてセーブするにつれて判定が同一にならなくなっていきました。

0

1Answer

失礼、上のソースコードはミスがあったので直したところ、同一と確認できました。ただ、あれは短くするあまり、失敗したコードから再現性が下がった結果のようなので、補足として削除する部分を少なくして再現性を上げたものを載せておきます。これは勿論のこと、同一確認が出来ません。以下のアドレスにあります。
https://github.com/sasatadamoto/hash_test

0Like

Comments

  1. @ccastle

    Questioner

    SHA256_UPDATEの引数となる文字列の配列内を全て0にしてからその関数を呼び出すことと、バイナリファイルに書き込む構造体のメンバの一部で一定の条件でしか初期化されない変数があったのでどのような条件でもまずは初期化することで全て上手くいきました。

Your answer might help someone💌