いやー、かなりの期間(約9年半)さぼってました。
何となく仕事の関係で、調べた事を残しておきたくて書いてます。

さて何の件かというと、パスワードのクラックにかかる時間です。
仕事の関係でパスワードクラックってどれくらい時間がかかるものなのか調べていたら
IPAさんが掲載していた2008年のレポート(https://www.ipa.go.jp/security/txt/2008/10outline.html)が見つかりました。
でも、このレポートは10年前のもので、H/Wの性能は高くなっているはずだと他を当たってみたのですが、
出てくるのは、かなり高性能なマシンでのレポートだったことから現実的じゃなかったので、
最終的に自分でプログラムを作成する事にしました。
取り敢えず手元の環境で作ろうと調べていて、NTLMハッシュだと、HDDからツールでPWファイルを引っ張ってきて
そのデータと比較してブルートフォースアタックでクラックできそうなので、その方法を再現するように
C#を使ってプログラミングしました。

時間計測の方法としては、NTLMハッシュ(MD4のハッシュ)のクラックを想定し、IPAさんの結果と比べられるように、
同種の文字種と桁数から発生する可能性のあるハッシュ値ブルートフォースで全件チェックするロジックです。

実際にしたことは、例えば4桁の英小文字であれば、"aaaa"から"aaab"、"aaac"から"zzzz"まで
の456,976種のハッシュ値を生成し、ランダムに発生させた5桁MD4のハッシュ値(基本的には一致
しないはずのハッシュ値)と比較するのにかかった時間を計測しました。

環境は、
 CPU:Intel® Core™ i7-3770 @ 3.4GHz (4Core, 8Thread) / Mem: 12GByte
 使用OS:Windows 7 Professional SP1 64bit
と言った感じです。

結果は、

文字種4桁6桁8桁10桁
英小文字:26種1秒未満約40秒約8時間約227日
英小文字+数字:36種1秒未満約5分約4.5日約16年
英大小文字+数字:62種約2秒約2時間約315日約3,317年
英大小文字+数字+記号:93種約10秒約22時間約22年約18.6万年
(文字色がオレンジの時間は、2日間実行した時間と進んだチェック回数から想定した時間)
と言った感じです。

なんだかぱっとしなかったので、再度ネットを検索して回ることに、
そこで見つかったのが、"GPUを使ったハックツールだと速い"というのがあったので、早速試してみる事に・・・。
今回は、hashcatの64bit版(http://hashcat.net/からダウンロード)を使いました。
(さすがにGPUのプログラミングまでは手が出なかった。)
とは言っても、はじめの環境には使えるGPUを積んだカードがなかったため、
 GPU: NVIDIA® GeForce® GTX750 (CUDA Core 512)
を中古(\8,000-)で購入し、前述の環境へセッティング

結果は、

文字種6桁8桁9桁10桁
英小文字:26種数秒約50秒約20分約9時間
英小文字+数字:36種数秒約11分約6.5時間約10日
英大小文字+数字:62種約18秒約14時間約11日約6年
英大小文字+数字+記号:95種約3分約18日約5年約430年
(文字色がオレンジの時間は、2日間実行した時間と進んだチェック回数から想定した時間)
と言った感じです。

GTX750は最新のグラボではないけど、一般的に言われている8桁の英文字(大小)と数字のパスワードだと、
約14時間で解析できてしまうというのはいかがなものかと・・・。

今回のクラックではNTLMのMD4ハッシュだったので、ノートPCなどパスワードが入っているドライブを
盗まれなければ大丈夫かもしれないけど、今後はもう少し長めのパスワードにしないと危ないって事が分かった。

久々の更新

今回はプログラムではなくって、Wiresharkを使っていて覚えておきたいことを書くことにします。

仕事の関係で、Wiresharkのキャプチャデータをもらったのですが、それがなんと1M毎に分割された6000個以上もあるファイル群。
この中から必要なデータを探して処理をするなんて至難の業なので、マージが出来ないかと検索していたら・・・、ちゃんとあるじゃないですか。
さすがかゆ良いところに手が届く設計がされていること。

mergecap.exeコマンドを使うことで、複数のキャプチャファイルをマージできると書いてある。

コマンドラインで、
mergecap.exe -w 出力先ファィル名 マージ元ファイル ・・・
として実行すると、マージ元ファイルを全てマージした結果が出力先ファィル名に書出されました。

この先他にもありそうなので、今回の作業で必要な処理をコマンドラインで実行できないかと模索することにします。

FStringクラスの使用方法で

昨日公開したFStringクラスですが、説明文中で使用したサンプルのプログラムで冗長な部分があったので修正します。

/*
 *  指定したインターフェースに設定されたアドレスを取得する
 *  param   : インターフェースの番号(int)
 *
 *  return  : address_list
 */
address_list AddrInfo::setAddressList(int ifIndex) {
    address_list result;
    String ipaddress;
    String netmask;
    String bcastaddress;

    long retCode = NO_ERROR;
    DWORD dwSize = 0;
    GetIpAddrTable(NULL, &dwSize, 0);
    PMIB_IPADDRTABLE pIPAddrTable = (MIB_IPADDRTABLE *)malloc(dwSize);
    memset(pIPAddrTable, 0, dwSize);
    if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == NO_ERROR) {
        for (unsigned int num = 0; num < pIPAddrTable->dwNumEntries; num++) {
            if (ifIndex == pIPAddrTable->table[num].dwIndex) {
ここ==>         ipaddress = String((_TCHAR *)FString(inet_ntoa(*(struct in_addr *)&pIPAddrTable->table[num].dwAddr)));
ここ==>         netmask = String((_TCHAR *)FString(inet_ntoa(*(struct in_addr *)&pIPAddrTable->table[num].dwMask)));
                bcastaddress = getBcastAddr(pIPAddrTable->table[num].dwAddr, pIPAddrTable->table[num].dwMask, pIPAddrTable->table[num].dwBCastAddr);
                AddrInfo info = AddrInfo(ipaddress,
                                            netmask,
                                            bcastaddress,
                                            pIPAddrTable->table[num].wType);
                result.push_back(info);
            }
        }
    }
    free(pIPAddrTable);

    return result;
}

_bstr_tクラスをそのまま置換えちゃったので、

    ipaddress = String((_TCHAR *)_bstr_t(inet_ntoa(*(struct in_addr *)&pIPAddrTable->table[num].dwAddr)));
                                  ||
                                  V
    ipaddress = String((_TCHAR *)FString(inet_ntoa(*(struct in_addr *)&pIPAddrTable->table[num].dwAddr)));

と何やら複雑なキャストがされていますが、後から色々とチェックしていて、

    ipaddress = (String)FString(inet_ntoa(*(struct in_addr *)&pIPAddrTable->table[num].dwAddr));

で良いことが分かりました。


_bstr_tクラスでは、"string"や"wstring"にキャストするオペレータがなく、キャストできるのが"char*"か"wchar_t*"になるために、String化する場合には、一旦_bstr_tを"char*"か"wchar_t*"にキャストしてStringを構成させる方法だったのですが、FStringでは直接"string"や"wstring"にキャストするオペレータを用意したので、(_TCHAR *)へのキャストとStringの作成が不要だったのでした。
実際には、ipaddressがStringクラスなのでキャストも不要ですが、一応明示的に付けてあります。

MicroSoftの_bstr_tクラスをシミュレート

最後に日記を書いたあとの作業が、社外秘の情報が多くて日記に書けない内容だったので、ずーっと空いてしまいました。
今回は文字コード変換のロジックをまかなうクラスを作ってみました。
以前作ったプログラムで、UNICODE文字セットでも、MBCS文字セットでも動作するプログラミングをするために、文字セットに依存しそうなロジック中では、"_bstr_t"というMSのクラスを利用していました。
例えば、

/*
 *  指定したインターフェースに設定されたアドレスを取得する
 *  param   : インターフェースの番号(int)
 *
 *  return  : address_list
 */
address_list AddrInfo::setAddressList(int ifIndex) {
    address_list result;
    String ipaddress;
    String netmask;
    String bcastaddress;

    long retCode = NO_ERROR;
    DWORD dwSize = 0;
    GetIpAddrTable(NULL, &dwSize, 0);
    PMIB_IPADDRTABLE pIPAddrTable = (MIB_IPADDRTABLE *)malloc(dwSize);
    memset(pIPAddrTable, 0, dwSize);
    if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == NO_ERROR) {
        for (unsigned int num = 0; num < pIPAddrTable->dwNumEntries; num++) {
            if (ifIndex == pIPAddrTable->table[num].dwIndex) {
ここ==>         ipaddress = String((_TCHAR *)_bstr_t(inet_ntoa(*(struct in_addr *)&pIPAddrTable->table[num].dwAddr)));
ここ==>         netmask = String((_TCHAR *)_bstr_t(inet_ntoa(*(struct in_addr *)&pIPAddrTable->table[num].dwMask)));
                bcastaddress = getBcastAddr(pIPAddrTable->table[num].dwAddr, pIPAddrTable->table[num].dwMask, pIPAddrTable->table[num].dwBCastAddr);
                AddrInfo info = AddrInfo(ipaddress,
                                            netmask,
                                            bcastaddress,
                                            pIPAddrTable->table[num].wType);
                result.push_back(info);
            }
        }
    }
    free(pIPAddrTable);

    return result;
}

ただ"_bstr_t"クラスはMSのクラスで、Linuxへはこのまま移行できないので、この"_bstr_t"クラスをシミュレートする感じのクラスを作って見ました。
100%シミュレートするクラスを作るのは面倒だったので、自分の使ってるロジック中で利用される機能を中心に構築する事にしました。
クラスの名前は"FString"(ちょっとべた過ぎるかも・・・)にしています。


ソースプログラムは、
FString.h

#include <locale.h>
#include <tchar.h>

#include <string>
using std::string;
using std::wstring;

#ifdef  _UNICODE
    typedef std::wstring                String;
#else
    typedef std::string                 String;
#endif  //  _UNICODE

#define _LOCALE_JAPANESE    _T("Japanese_Japan.932")

class FString {
public:
    FString();
    FString(string str);
    FString(wstring wstr);
    FString(const char *str);
    FString(const wchar_t *wstr);
    ~FString();

    FString& operator=(const char *from);
    FString& operator=(const wchar_t *from);
    FString& operator+=(FString& from);
    FString& operator+=(const char *from);
    FString& operator+=(const wchar_t *from);

    operator const char*();
    operator char*();
    operator string();
    operator const wchar_t*();
    operator wchar_t*();
    operator wstring();

    string          getString();
    wstring         getWString();
    const char      *getCharBuff();
    const wchar_t   *getWCharBuff();

    static      void        setLocale(String loc);
    static      wstring     strToWstr(string str);
    static      string      wstrToStr(wstring wstr);

private:
    wstring     wchar_string;
    string      char_string;
};  //  class FString

それと、FStringのクラスボディは、
FString.cpp

#include "FString.h"

static String       own_loc = _LOCALE_JAPANESE;

//  Constructor
FString::FString() {
}

//  Constructor
//  param   : 初期化文字列(string)
FString::FString(string str) {
    wchar_string = strToWstr(str);
    char_string.clear();
}

//  Constructor
//  param   : 初期化文字列(wstring)
FString::FString(wstring wstr) {
    wchar_string = wstr;
    char_string.clear();
}

//  Constructor
//  param   : 初期化文字列(char*)
FString::FString(const char *str) {
    wchar_string = strToWstr(string(str));
    char_string.clear();
}

//  Constructor
//  param   : 初期化文字列(wchar_t*)
FString::FString(const wchar_t *wstr) {
    wchar_string = wstring(wstr);
    char_string.clear();
}

//  Destructor
FString::~FString() {
    wchar_string.clear();
    char_string.clear();
}

/*
 *  "=(代入)"オペレータの処理
 */
FString& FString::operator=(const char *from) {
    wchar_string = strToWstr(string(from));
    char_string.clear();
    return *this;
}

/*
 *  "=(代入)"オペレータの処理
 */
FString& FString::operator=(const wchar_t *from) {
    wchar_string = wstring(from);
    char_string.clear();
    return *this;
}

/*
 *  "+=(追加)"オペレータの処理
 */
FString& FString::operator+=(FString& from) {
    wchar_string.append(from.getWString());
    char_string.clear();
    return *this;
}

/*
 *  "+=(追加)"オペレータの処理
 */
FString& FString::operator+=(const char *from) {
    wstring ap = strToWstr(string(from));
    wchar_string.append(ap);
    char_string.clear();
    return *this;
}

/*
 *  "+=(追加)"オペレータの処理
 */
FString& FString::operator+=(const wchar_t *from) {
    wchar_string.append(from);
    char_string.clear();
    return *this;
}

/*
 *  "const char*(リダイレクト)"オペレータの処理
 */
FString::operator const char*() {
    return getCharBuff();
}

/*
 *  "char*(リダイレクト)"オペレータの処理
 */
FString::operator char*() {
    return (char *)getCharBuff();
}

/*
 *  "string(リダイレクト)"オペレータの処理
 */
FString::operator string() {
    getString();
    return char_string;
}

/*
 *  "const wchar_t*(リダイレクト)"オペレータの処理
 */
FString::operator const wchar_t*() {
    return getWCharBuff();
}

/*
 *  "wchar_t*(リダイレクト)"オペレータの処理
 */
FString::operator wchar_t*() {
    return (wchar_t *)getWCharBuff();
}

/*
 *  "wstring(リダイレクト)"オペレータの処理
 */
FString::operator wstring() {
    return wchar_string;
}

/*
 *  登録文字列を取得する
 *
 *  return  : string
 */
string FString::getString() {
    if (char_string.empty() && !wchar_string.empty()) {
        char_string = wstrToStr(wchar_string);
    }
    return char_string;
}

/*
 *  登録文字列を取得する
 *
 *  return  : char*
 */
const char *FString::getCharBuff() {
    getString();
    return char_string.c_str();
}

/*
 *  登録文字列を取得する
 *
 *  return  : wstring
 */
wstring FString::getWString() {
    return wchar_string;
}

/*
 *  登録文字列を取得する
 *
 *  return  : wchar_t*
 */
const wchar_t *FString::getWCharBuff() {
    return wchar_string.c_str();
}

/*
 *  Localeを設定する
 *
 *  param   : Locale(String)
 */
void FString::setLocale(String loc) {
    own_loc = loc;
}

/*
 *  stringをwstringに変換する
 *
 *  param   : オリジナル文字列(string)
 *
 *  return  : wstring
 */
wstring FString::strToWstr(string str) {
    wstring result;
    if (!str.empty()) {
        _TCHAR *org_loc = _tcsdup(_tsetlocale(LC_ALL, NULL));
        _tsetlocale(LC_ALL, own_loc.c_str());
        size_t from_len = str.length();
        char *from_value = (char *)calloc(from_len+1, sizeof(char));
        memcpy(from_value, str.c_str(), from_len);
        size_t to_len = mbstowcs(NULL, from_value, from_len);
        wchar_t *buffer = (wchar_t *)calloc(to_len+1, sizeof(wchar_t));
        mbstowcs(buffer, str.c_str(), from_len);
        free(from_value);
        result = wstring(buffer);
        free(buffer);
        _tsetlocale(LC_ALL, org_loc);
        free(org_loc);
    }
    return result;
}

/*
 *  wstringをstringに変換する
 *
 *  param   : オリジナル文字列(wstring)
 *
 *  return  : string
 */
string FString::wstrToStr(wstring wstr) {
    string result;
    if (!wstr.empty()) {
        _TCHAR *org_loc = _tcsdup(_tsetlocale(LC_ALL, NULL));
        _tsetlocale(LC_ALL, own_loc.c_str());
        size_t from_len = wstr.length();
        wchar_t *from_value = (wchar_t *)calloc(from_len+1, sizeof(wchar_t));
        memcpy(from_value, wstr.c_str(), from_len*sizeof(wchar_t));
        size_t to_len = wcstombs(NULL, from_value, from_len);
        char *buffer = (char *)calloc(to_len+1, sizeof(char));
        /*
         *  本当なら"wcstombs(buffer, from_value, from_len);"で事が足りるはずなんですが、
         *  なぜか、ロケールに"japanese"を指定して第一パラメータに値を指定すると途中まで
         *  しか変換されないケースがあるので、あえて一文字づつ変換させています
         */
        size_t pos = 0;
        for (size_t i = 0; i < from_len; i++) {
            int len = wctomb(&buffer[pos], from_value[i]);
            if (len < 0)
                break;
            pos += len;
        }
        free(from_value);
        result = string(buffer);
        free(buffer);
        _tsetlocale(LC_ALL, org_loc);
        free(org_loc);
    }
    return result;
}

テストで、

int _tmain(int argc, _TCHAR* argv[])
{
    wcout.imbue(locale("japanese"));
    BString::setLocale(_LOCALE_JAPANESE);
    BString mbs = BString("漢字のデータ");
    const wchar_t *mbs_buff = (const wchar_t*)mbs;
    wstring wcs_ref = mbs;
    mbs += L"−追加";
    wstring wcs_val = mbs.getWString();
    wcout << L"WString : " << wcs_val << endl;
    BString wcs = BString(wcs_val);
    string mbs_val = wcs.getString();
    cout << "String : " << mbs_val << endl;
    return 0;
}

のロジックが動作する事を確認しました。


最初に提示した以前のロジックとの変更点としては、

/*
 *  指定したインターフェースに設定されたアドレスを取得する
 *  param   : インターフェースの番号(int)
 *
 *  return  : address_list
 */
address_list AddrInfo::setAddressList(int ifIndex) {
    address_list result;
    String ipaddress;
    String netmask;
    String bcastaddress;

    long retCode = NO_ERROR;
    DWORD dwSize = 0;
    GetIpAddrTable(NULL, &dwSize, 0);
    PMIB_IPADDRTABLE pIPAddrTable = (MIB_IPADDRTABLE *)malloc(dwSize);
    memset(pIPAddrTable, 0, dwSize);
    if (GetIpAddrTable(pIPAddrTable, &dwSize, 0) == NO_ERROR) {
        for (unsigned int num = 0; num < pIPAddrTable->dwNumEntries; num++) {
            if (ifIndex == pIPAddrTable->table[num].dwIndex) {
ここ==>         ipaddress = String((_TCHAR *)FString(inet_ntoa(*(struct in_addr *)&pIPAddrTable->table[num].dwAddr)));
ここ==>         netmask = String((_TCHAR *)FString(inet_ntoa(*(struct in_addr *)&pIPAddrTable->table[num].dwMask)));
                bcastaddress = getBcastAddr(pIPAddrTable->table[num].dwAddr, pIPAddrTable->table[num].dwMask, pIPAddrTable->table[num].dwBCastAddr);
                AddrInfo info = AddrInfo(ipaddress,
                                            netmask,
                                            bcastaddress,
                                            pIPAddrTable->table[num].wType);
                result.push_back(info);
            }
        }
    }
    free(pIPAddrTable);

    return result;
}

になります。
テストしましたが、何とか動作しています。


余談で、FString.cppのロジック中にある、"string FString::wstrToStr(wstring wstr)"メソッドのロジック中にもコメントしたんですが、localeを"japanese"にすると、

    size_t to_len = wcstombs(NULL, from_value, from_len);

のロジックは問題なく動作して、from_valueが"漢字のデータ"を設定した時のto_lenの値は12になるのですが、

    char *buffer = (char *)calloc(to_len+1, sizeof(char));
    size_t to_len = wcstombs(buffer, from_value, from_len);

のロジックでは、from_valueが"漢字のデータ"を設定した時のto_lenの値が6になり、bufferには"漢字の"までしか変換されませんでした。
色々と試してみたのですが、原因が特定できなかったので、

    char *buffer = (char *)calloc(to_len+1, sizeof(char));
    size_t pos = 0;
    for (size_t i = 0; i < from_len; i++) {
        int len = wctomb(&buffer[pos], from_value[i]);
        if (len < 0)
            break;
        pos += len;
    }

として回避しています。

LinuxでDiskの情報を取得する

久しぶりに日記の更新です。


前回Windows系の情報収集等のプログラミングをやって来ていたんですが、先週はLinux系の情報収集に翻弄されていました。
その第一弾はDisk情報の取得で、WindowsだとWMIを使って比較的簡単に取得できたんですが、Linuxの方は取得するのに苦労してしまいました。


次のプログラムは、/proc/partitions情報からLinuxが認識している区画情報を取得して、その区画情報を元にDisk情報の入っているsuper_blockを探し出して取得する手順を撮って取っています。
Diskの情報としては、ext2以外にext3、FAT、NTFSなどがありますが、今回はext2(JDB,ext3ext2として)の情報を取得することだけを念頭においてプログラミングしています。

#include <uuid/uuid.h>
#include <linux/ext2_fs.h>
#include <fcntl.h>
#define _SUPERBLOCK_BUFF_SIZE   65536

char uuid_buf[64];
char line[1024];
char devid[128];
char devname[128];
unsigned char *io_buf = (unsigned char *)malloc(_SUPERBLOCK_BUFF_SIZE);
int ma, mi;

FILE *proc = fopen("/proc/partitions", "r");
unsigned long long sz;
if (proc != 0) {
    while (fgets(line, sizeof(line), proc)) {
        if (sscanf(line, " %d %d %llu %127[^\n ]", &ma, &mi, &sz, devid) == 4) {
            if (sz <= 1)
                continue;
            if (!isdigit(devid[strlen(devid)-1]))
                continue;
            sprintf(devname, "/dev/%s", devid);
            int fd = open(devname, O_RDONLY | O_NONBLOCK);
            if (fd) {
                unsigned loc = lseek(fd, 0, SEEK_SET);
                if (loc >= 0) {
                    read(fd, io_buf, _SUPERBLOCK_BUFF_SIZE);
                    size_t off = 0;
                    while (off+sizeof(ext2_super_block) < _SUPERBLOCK_BUFF_SIZE) {
                        ext2_super_block *sb = (ext2_super_block*)&io_buf[off];
                        if (sb->s_magic == EXT2_SUPER_MAGIC) {
                            memset(uuid_buf, 0, 64);
                            uuid_unparse(sb->s_uuid, uuid_buf);
                            printf("Device %s : LABEL=%s : %s", devname, sb->s_volume_name, uuid_buf);
                            if (strncmp(devid, "md", 2) == 0) {
                                printf(" : DriveRaidDisk.\n");
                            }
                            else {
                                printf(" : DriveLocalDisk.\n");
                            }
                            break;
                        }
                        off += 1024;
                    }
                }
                close(fd);
            }
        }
    }
    fclose(proc);
}

変換するロケールをjapanese以外にも出来るかな

昨日作ったJavaの文字化けを修正するプログラムですが、一方的にJapaneseしか扱っていなかったので、新しいLocaleをパラメータで渡すように変更しました。
また、クラスの名前もJapaneseだったのをCharSetに、メソッド名もFixCharsetをchangeLocaleに変更してみました。


先ずはJavaのクラスの方で
CharSet.java

package charset;

public class CharSet {
    public CharSet() {
    }
    public static native String changeLocale(String Original, String newLocale);
}

changeLocaleメソッドの第2パラメータに新しいロケールを指定できるようにしました。


次にCharSetクラスに対応したJNIプログラムの作成で、
これもCharSet.cppに名前を変更して、

#include <jni.h>
#include "charset_CharSet.h"

#include <locale>
using std::locale;

JNIEXPORT jstring JNICALL Java_charset_CharSet_changeLocale(JNIEnv *env, jclass cls, jstring original, jstring new_loc) {
    /*
     *  Java文字列を一旦MBCS文字列に変換する
     */
    wchar_t *words = (wchar_t *)env->GetStringChars(original, NULL);
    size_t mbs_len = wcstombs(NULL, words, wcslen(words));
    char *mbs_value = (char *)calloc(mbs_len+1, sizeof(char));
    mbs_len = wcstombs(mbs_value, words, wcslen(words));
    env->ReleaseStringChars(original, (const jchar *)words);
    /*
     *  Localeを取得する
     */
    words = (wchar_t *)env->GetStringChars(new_loc, NULL);
    size_t loc_len = wcstombs(NULL, words, wcslen(words));
    char *loc_value = (char *)calloc(loc_len+1, sizeof(char));
    loc_len = wcstombs(loc_value, words, wcslen(words));
    env->ReleaseStringChars(new_loc, (const jchar *)words);
    /*
     *  MBCS文字列をLocal=new_locで再度Unicode文字列に変換し、結果をJava文字列に設定する
     */
    locale new_locale(loc_value);
    locale old_locale = locale::global(new_locale); 
    size_t wcs_len = mbstowcs(NULL, (const char *)mbs_value, mbs_len);
    wchar_t *wcs_value = (wchar_t *)calloc(wcs_len, sizeof(wchar_t));
    wcs_len = mbstowcs(wcs_value, (const char *)mbs_value, mbs_len);
    jstring wcs_string = env->NewString((const jchar *)((wchar_t *)wcs_value), (jsize)wcs_len);
    locale::global(old_locale);
    free(mbs_value);
    free(loc_value);
    return wcs_string;
}

としました。
変更点としては単純に追加された第2パラメータで設定された情報をlocale new_localeに渡すように変更しました。


ただこのプログラム自体は、japanese以外のロケールは試していませんので、あしからず。

NetworkInterface.getDisplayName()で発生する文字化けをチェック

Javajava.net.NetworkInterfaceにある、getDisplayName()メソッドで文字化けするために、IPHLPAPIを使って新たにクラスを作るということを以前していましたが、何故文字化けするのか調査しようと思って、簡単なテストプログラムを作ってみました。
Javajava.net.NetworkInterfaceのロジックとしては、jdk1.6のソースコードからjdk6-src\j2se\src\windows\native\java\net\NetworkInterface.cを参考にして作っています。
テスト用のJavaプログラムとして、
NetworkInfo.java

package charset;

import java.util.Enumeration;

public class NetworkInfo {
    private int             index = 0;
    private String          description_jdk = null;
    private String          description_cvt = null;
    private String          description_loc = null;
    private String          description_bstr = null;

    static {
        System.loadLibrary("CharSet");
        init();
    }

    public NetworkInfo() {
    }
    
    public int getIndex() {
        return index;
    }
    
    public String getDescriptionJDK() {
        return description_jdk;
    }
    
    public String getDescriptionCVT() {
        return description_cvt;
    }
    
    public String getDescriptionLOC() {
        return description_loc;
    }
    
    public String getDescriptionBSTR() {
        return description_bstr;
    }

    public static void main(String[] args) {
        try {
            Enumeration<java.net.NetworkInterface> ifs = 
                         java.net.NetworkInterface.getNetworkInterfaces();
            while (ifs.hasMoreElements()) {
                java.net.NetworkInterface intf = ifs.nextElement();
                System.out.println("Interface : ");
                System.out.println("  Name : "+intf.getName());
                System.out.println("  Description : "+intf.getDisplayName());
            }
        } catch (Exception e) {}
    
    
        NetworkInfo[] nis = NetworkInfo.getInterfaces();
        for (int i = 0; i < nis.length; i++) {
            NetworkInfo ni = nis[i];
            System.out.println("Nic : "+ni.getIndex());
            System.out.println("  DescriptionJDK : "+ni.getDescriptionJDK());
            System.out.println("  DescriptionCVT : "+ni.getDescriptionCVT());
            System.out.println("  DescriptionLOC : "+ni.getDescriptionLOC());
            System.out.println("  DescriptionBSTR : "+ni.getDescriptionBSTR());
        }
    }

    private static native void init();
    public static native NetworkInfo[] getInterfaces();
}

JNIを呼び出すことになるので、いつものようにコンパイルした後にjavahでヘッダファイルを作りました。
いつもだとIncludeや変数情報などはプログラムファイルと別にヘッダファイルを用意するんですが、今回はテスト用に単純なアプリケーションなので、ヘッダ情報等もまとめてC++のJNIプログラム1ファイルで作っちゃいました。

#include <windows.h>
#include <tchar.h>
#include <comdef.h>

#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi.lib")

#include <jni.h>
#include "charset_NetworkInfo.h"

#include <locale>
using std::locale;

jclass      ni_class;               /* NetworkInfo */
jmethodID   ni_ctor;                /* NetworkInfo() */
jfieldID    ni_indexID;             /* NetworkInfo.index */
jfieldID    ni_description_jdkID;   /* NetworkInfo.description_jdk */
jfieldID    ni_description_cvtID;   /* NetworkInfo.description_cvt */
jfieldID    ni_description_locID;   /* NetworkInfo.description_loc */
jfieldID    ni_description_bstrID;  /* NetworkInfo.description_bstr */

JNIEXPORT void JNICALL Java_charset_NetworkInfo_init(JNIEnv *env, jclass cls) {
    ni_class = (jclass)env->NewGlobalRef(cls);
    ni_indexID = env->GetFieldID(ni_class, "index", "I");
    ni_description_jdkID = env->GetFieldID(ni_class, "description_jdk", "Ljava/lang/String;");
    ni_description_cvtID = env->GetFieldID(ni_class, "description_cvt", "Ljava/lang/String;");
    ni_description_locID = env->GetFieldID(ni_class, "description_loc", "Ljava/lang/String;");
    ni_description_bstrID = env->GetFieldID(ni_class, "description_bstr", "Ljava/lang/String;");
    ni_ctor = env->GetMethodID(ni_class, "<init>", "()V");
}

JNIEXPORT jobjectArray JNICALL Java_charset_NetworkInfo_getInterfaces(JNIEnv *env, jclass cls) {
    jobjectArray ni_list = NULL;
    DWORD dwSize = 0;

    GetIfTable(NULL, &dwSize, true);
    PMIB_IFTABLE pIfTable = (MIB_IFTABLE *)calloc(dwSize, 1);
    if (pIfTable != NULL) {
        if (GetIfTable(pIfTable, &dwSize, true) == NO_ERROR) {
            jsize ni_count = (jsize)pIfTable->dwNumEntries;
            if (ni_count > 0) {
                ni_list = env->NewObjectArray(ni_count, ni_class, NULL);
                for (unsigned long i = 0; i < pIfTable->dwNumEntries; i++) {
                    MIB_IFROW pInterface = pIfTable->table[i];
                    jobject netifObj = env->NewObject(ni_class, ni_ctor);
                    env->SetIntField(netifObj, ni_indexID, pInterface.dwIndex);

                    /*
                     * XPでの表示をテストするために、
                     *  jdk6-src\j2se\src\windows\native\java\net\NetworkInterface.c
                     * の582行目付近にある以下のロジックを参照して、Unicodeモードでない方の処理を流用しました。
                     *
                     *  if (ifs->dNameIsUnicode) {
                     *      displayName = (*env)->NewString(env, (PWCHAR)ifs->displayName, wcslen ((PWCHAR)ifs->displayName));
                     *  } else {
                     *      displayName = (*env)->NewStringUTF(env, ifs->displayName);
                     *  }
                     */
                    jstring jdk_string = env->NewStringUTF((const char *)pInterface.bDescr);
                    env->SetObjectField(netifObj, ni_description_jdkID, jdk_string);

                    /*
                     * JavaにUnicodeの文字列を設定するところで、MBCS文字列からUnicodeへの変換処理をcの関数
                     * mbstowcsを利用して変換してみました。
                     */
                    size_t wcs_len = mbstowcs(NULL, (const char *)pInterface.bDescr, strlen((const char *)pInterface.bDescr));
                    wchar_t *cvt_value = (wchar_t *)calloc(wcs_len, sizeof(wchar_t));
                    wcs_len = mbstowcs(cvt_value, (const char *)pInterface.bDescr, strlen((const char *)pInterface.bDescr));
                    jstring cvt_string = env->NewString((const jchar *)((wchar_t *)cvt_value), (jsize)wcs_len);
                    env->SetObjectField(netifObj, ni_description_cvtID, cvt_string);

                    /*
                     * JavaにUnicodeの文字列を設定するところで、MBCS文字列からUnicodeへの変換処理をcの関数
                     * mbstowcsを利用して変換してみました。
                     * 変換時にプログラムのロケールを一時的に"japanese"にしてみました。
                     */
                    locale jpn("japanese");
                    locale old_locale = locale::global(jpn); 
                    wcs_len = mbstowcs(NULL, (const char *)pInterface.bDescr, strlen((const char *)pInterface.bDescr));
                    wchar_t *loc_value = (wchar_t *)calloc(wcs_len, sizeof(wchar_t));
                    wcs_len = mbstowcs(loc_value, (const char *)pInterface.bDescr, strlen((const char *)pInterface.bDescr));
                    jstring loc_string = env->NewString((const jchar *)((wchar_t *)loc_value), (jsize)wcs_len);
                    locale::global(old_locale);
                    env->SetObjectField(netifObj, ni_description_locID, loc_string);

                    /*
                     * 変換処理は_bstr_tに任せてみました。
                     */
                    _bstr_t bstr_value = _bstr_t((char *)pInterface.bDescr);
                    jstring bstr_string = env->NewString((const jchar *)((wchar_t *)bstr_value), (jsize)bstr_value.length());
                    env->SetObjectField(netifObj, ni_description_bstrID, bstr_string);

                    env->SetObjectArrayElement(ni_list, i, netifObj);
                }
            }
        }
        free(pIfTable);
    }

    return ni_list;
}

Javaのプログラムを実行した結果(一部抜粋)は、

・・・
Interface : 
  Name : eth1
  Description : TAP-Win32 Adapter V9 - ?p?P?b?g ?X?P?W
・・・
Nic : 3
  DescriptionJDK : TAP-Win32 Adapter V9 - ?p?P?b?g ?X?P?W
  DescriptionCVT : TAP-Win32 Adapter V9 - ?p?P?b?g ?X?P?W???[?? ?~?j?|?[?g
  DescriptionLOC : TAP-Win32 Adapter V9 - パケット スケジューラ ミニポート
  DescriptionBSTR : TAP-Win32 Adapter V9 - パケット スケジューラ ミニポート
・・・

となり、内部的な文字コードの変換における問題って感じですね。
内部コードのロケールの問題らしいので、かなり強引ですが文字コードを変換しちゃうメソッドを作ってしまいました。
クラス名はJapaneseとかなり即物的な名前にしました。・・・名前を考えるのが面倒だったので。
Japanese.java

package charset;

public class Japanese {
    public Japanese() {
    }
    public static native String FixCharset(String Original);
}

今までと同様に、これをコンパイルしてヘッダファイルを作ってから、
Japanese.cpp

#include <jni.h>
#include "charset_Japanese.h"

#include <locale>
using std::locale;

JNIEXPORT jstring JNICALL Java_charset_Japanese_FixCharset(JNIEnv *env, jclass cls, jstring original) {
    /*
     *  Java文字列を一旦MBCS文字列に変換する
     */
    wchar_t *words = (wchar_t *)env->GetStringChars(original, NULL);
    size_t mbs_len = wcstombs(NULL, words, wcslen(words));
    char *mbs_value = (char *)calloc(mbs_len+1, sizeof(char));
    mbs_len = wcstombs(mbs_value, words, wcslen(words));
    env->ReleaseStringChars(original, (const jchar *)words);
    /*
     *  MBCS文字列をLocal=japaneseで再度Unicode文字列に変換し、結果をJava文字列に設定する
     */
    locale jpn("japanese");
    locale old_locale = locale::global(jpn); 
    size_t wcs_len = mbstowcs(NULL, (const char *)mbs_value, mbs_len);
    wchar_t *wcs_value = (wchar_t *)calloc(wcs_len, sizeof(wchar_t));
    wcs_len = mbstowcs(wcs_value, (const char *)mbs_value, mbs_len);
    jstring wcs_string = env->NewString((const jchar *)((wchar_t *)wcs_value), (jsize)wcs_len);
    locale::global(old_locale);
    free(mbs_value);
    return wcs_string;
}

を作ってテストしてみました。
テストプログラムを

        NetworkInfo[] nis = NetworkInfo.getInterfaces();
        for (int i = 0; i < nis.length; i++) {
            NetworkInfo ni = nis[i];
            System.out.println("Nic : "+ni.getIndex());
            System.out.println("  DescriptionJDK : "+ni.getDescriptionJDK());
            System.out.println("  DescriptionCVT : "+ni.getDescriptionCVT());
            System.out.println("  DescriptionLOC : "+ni.getDescriptionLOC());
            System.out.println("  DescriptionBSTR : "+ni.getDescriptionBSTR());
            System.out.println("  Converted : "+Japanese.FixCharset(ni.getDescriptionJDK()));
        }

に変更して実行した結果が、

Nic : 3
  DescriptionJDK : TAP-Win32 Adapter V9 - ?p?P?b?g ?X?P?W
  DescriptionCVT : TAP-Win32 Adapter V9 - ?p?P?b?g ?X?P?W???[?? ?~?j?|?[?g
  DescriptionLOC : TAP-Win32 Adapter V9 - パケット スケジューラ ミニポート
  DescriptionBSTR : TAP-Win32 Adapter V9 - パケット スケジューラ ミニポート
  Converted : TAP-Win32 Adapter V9 - パケット スケジ

になりました。
"パケット スケジ"で切れているのは、Java保有している元々の文字列が"?p?P?b?g ?X?P?W"で切れている関係です。

env->NewStringUTF((const char *)pInterface.bDescr);

の内部処理で切れちゃったみたいですね。


あくまでも想像ですが、pInterface.bDescrの値として

offset  0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F    0123456789ABCDEF
     0  54 41 50 2d 57 69 6e 33  32 20 41 64 61 70 74 65   TAP-Win32 Adapte
    16  72 20 56 39 20 2d 20 83  70 83 50 83 62 83 67 20   r V9 - .p.P.b.g
    32  83 58 83 50 83 57 83 85  81 5b 83 89 20 83 7e 83   .X.P.W...[.. .~.
    48  6a 83 7c 81 5b 83 67 00                            j.|.[.g.

が設定されているので、NewStringUTF関数の中の処理で、offsetが38byteから0x83,0x85と0x80以上の値が連続したために内部の変換処理でエラー等の不具合が発生したのかもしれません。
これに関しては、Javaソースコードを追いかける気にならなかったので、あくまでも想像ベースですけど。