読者です 読者をやめる 読者になる 読者になる

kkAyatakaのメモ帳。

誰かの役に立つかもしれない備忘録。

OpenSSLを使った暗号化

C++

OpenSSLを使ってAESで暗号化。

暗号化

#include <openssl/evp.h>

EVP_CIPHER_CTX ctx = {};
EVP_CIPHER_CTX_init(&ctx);

// 暗号化の設定で、EVP_aes_128_ecb等いろいろ
const unsigned char iv[16] = {};
EVP_EncryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);

// ブロックサイズで割り切れる部分の処理
int outl = 0;
EVP_EncryptUpdate(&ctx, encrypted, &outl, data, data_size);

// 最後のブロックの処理で、PKCSパディングされる。
int pad = 0;
EVP_EncryptFinal_ex(&ctx, encrypted + outl, &pad);
  
EVP_CIPHER_CTX_cleanup(&ctx);

AESはブロック暗号で、16バイトづつ処理される。

16の倍数分はEVP_EncryptUpdateで処理されて、パディングを含んだ最後の部分はEVP_EncryptFinal_exで処理される。

OpenSSLはPKCSでパディングする。PKCSのパディング処理は足りないバイトの値で足りない分を埋めるんだけど、足りないバイトが0(データサイズが16の倍数)の場合は、1ブロックまるまるパディングが追加されるので、暗号化データのサイズには注意が必要になる。

復号化

EVP_CIPHER_CTX ctx = {};
EVP_CIPHER_CTX_init(&ctx);

// 暗号化の設定
const unsigned char iv[16] = {};
EVP_DecryptInit_ex(&ctx, EVP_aes_128_cbc(), NULL, key, iv);

// ブロックサイズで割り切れる部分の処理
int outl = 0;
EVP_DecryptUpdate(&ctx, decrypted, &outl, data, data_size);

// 最後のブロックの処理で、PKCSパディングのサイズを返す
// パディングのデータは処理されないので、後処理は自分で行う
int last = 0;
EVP_DecryptFinal_ex(&ctx, decrypted + outl, &last);
memset(decrypted + outl + last, 0, decrypted_size - outl - last); // remove padding
  
EVP_CIPHER_CTX_cleanup(&ctx);

復号もEVP_DecryptUpdateEVP_DecryptFinal_exに分かれていて、EVP_DecryptFinal_exはパディングの値を返す。ただ、サイズを返すだけでデータ自体は処理されないので適宜処理してやる。

参考

Win32 APIを使ってOSを再起動する

Windows

ちょっと必要になって調べてた。呼び出す関数はExitWindowsExなんだけど特権の調整が必要で単純に呼び出すだけでは成功しない。

ExitWindowsExから調べていって、LUID_AND_ATTRIBUTESのところで...

Its meaning is dependent on the definition and use of the LUID

LUID_AND_ATTRIBUTES

といわれて、SE_SHUTDOWN_NAMEで調べたらサンプルが出てきたというオチ。以下はサンプルベースでは無くて、それまでに作っていたコード。

#include <Windows.h>

int main() {
  HANDLE token = NULL;
  OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token);
  if (token) {
    LUID luid = {};
    LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &luid);

    LUID_AND_ATTRIBUTES attr = { luid, SE_PRIVILEGE_ENABLED };

    TOKEN_PRIVILEGES p = {};
    p.PrivilegeCount = 1;
    p.Privileges[0] = attr;

    AdjustTokenPrivileges(token, FALSE, &p, 0, 0, 0);

    CloseHandle(token);
  }

  // 第2引数は値が豊富な上に、推奨される組み合わせがある
  ExitWindowsEx(EWX_REBOOT, SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_NETWORK_CONNECTIVITY);
}

ExitWindowsExの第2引数はパターンが豊富なのできちっとリファレンスを参照したほうが良い。

ExitWindowsExにはなじみが無かったんだけど、手札として持っておくと何かあったときに使えそうな感じ。

結局SE_SHUTDOWN_NAMESE_PRIVILEGE_ENABLEDを結びつける文書って見つからなかったんだけど、他の値を使うときはどうなるのか...

参考

C#のラムダをWin32呼び出しで使う

Windows

あたり前っちゃーあたり前なんですが、Win32を呼び出す際の選択肢として、なぜかラムダを使うという選択肢が思いつかなかった。

delegate Boolean EnumWindowProc(IntPtr hwnd, IntPtr lp);

[DllImport("User32.dll")]
private static extern Boolean EnumWindows(EnumWindowProc handler, IntPtr lparam);

public void Enum()
{
  int count = 0;
  EnumWindows(
    (hwnd, lp) =>
    {
      ++count;
      return true;
    }, IntPtr.Zero);

    Console.WriteLine(count);
}

処理内容が小さい場合はもちろん、呼び出し処理を完全にラップして抽象化する場合にも使える。コールバック関数がそこだけ必要ってケースも多いので利便性高い。

C++のラムダがCのコールバックにつかえる

C++ Windows

ためしにやったら動いた。さすがC++

#include <Windows.h>
#include <iostream>

int main() {
  HWINSTA sta = GetProcessWindowStation();

  EnumDesktops(
    sta,
    [](LPTSTR desktop, LPARAM lp)->BOOL {
      std::cout << desktop << std::endl;
      return TRUE;
    },
    0);
}

Win32は列挙系でコールバックを使うことが多いので、シンプルに書けてすごく助かる。

#include <Windows.h>
#include <iostream>

int main() {
  EnumDisplayMonitors(
    NULL,
    NULL,
    [](HMONITOR mon, HDC dc, LPRECT rc, LPARAM lp)->BOOL {
      std::cout << rc->left << ", " << rc->top << ", " <<
        rc->right << ", " << rc->bottom << std::endl;
      return TRUE;
    },
    0
    );
}

WPFでHotKeyBox

Windows

ホットキー用のコントロールを自前で作る際のポイント。TextBoxのオーバーライドとかは必要なくて、UserControlから十分作れそうな感じ。

ポイントはPreviewKeyDownで処理すること。KeyDownだとCtrl+Aで全選択されたり、特定のキーを拾えなかったりする。

this.textBox.PreviewKeyDown += textBox_PreviewKeyDown;

private void textBox_PreviewKeyDown(object sender, KeyEventArgs e)
{
  e.Handled = true;
  
  var mod = e.KeyboardDevice.Modifiers;
  mod |= e.KeyboardDevice.IsKeyDown(Key.LWin) ? ModifierKeys.Windows : ModifierKeys.None;
  mod |= e.KeyboardDevice.IsKeyDown(Key.RWin) ? ModifierKeys.Windows : ModifierKeys.None;

  // 使えるキーは絞ったほうが良いかも 
  var key = IsValidKey(e.Key) ? e.Key : Key.None;

  // ...本来はこのあたりで、登録可能かチェックする
  
  // キーの押下状態からテキストを作る
  textBox.Text = GetHotKeyText(mod, key);
}

WPFだとイベントオブジェクトにKeyboardDeviceがあるのでキー状態を見るのに使える。Windows Formsだと代替するものが無いので、Win32GetKeyboardStateを使ってしまうのが楽だった。

標準だとWinキーが取れないので、キーボードの状態を調べて修飾キーに加えてやる。XPの時ほど空いてないけど、個人的にこれが無いと始まらない感じ。

このやり方でそれなりにうまく動くところまでは確認できているけど、厳密にチェックしていくと穴はあるかも。ただ、ある程度はフォローできそうだと思っているし、機能的にも十分かなーとは思ってます。

WPFでWindow MessageのHook

Windows

Windows Forms同じくオーバーライドかなぁと思っていたら全然違いました。

using System.Windows.Interop;

var wnd = new Window();
wnd.Show();

// Show()の後で無いと取れません。
var handle = new WindowInteropHelper(wnd).Handle;
var src = HwndSource.FromHwnd(handle);
src.AddHook(MessageHook);

private IntPtr MessageHook(IntPtr hwnd, int msg, IntPtr wp, IntPtr lp, ref bool handle)
{
  return IntPtr.Zero;
}

特にコンストラクタ時点でハンドルが取れません。Window Messageのハンドリング用にウィンドウを作ったりする場合は(Hot Keyとかね)コンストラクタ内でShow()を呼び出しておけばよい感じ。

Messageハンドリング専用の場合、いろいろと設定して、表示されないように細工が必要でしょう。

private class MsgWnd : Window
{
  public MsgWnd()
  {
    this.Top = -100000;
    this.Left = -100000;
    this.Width = 0;
    this.Height = 0;
    this.WindowStyle = WindowStyle.None;
    this.AllowsTransparency = true;
    this.Background = Brushes.Transparent;

    this.ShowInTaskbar = false;
    this.ShowActivated = false;

    this.SourceInitialized += MsgWnd_SourceInitialized;
    this.Show();
  }

  public IntPtr Handle
  {
    get
    {
      if (handle == IntPtr.Zero)
      {
        handle = new WindowInteropHelper(this).Handle;
      }
      return handle;
    }
  }
  private IntPtr handle;
  
  private void MsgWnd_SourceInitialized(object sender, EventArgs e)
  {
    try
    {
      HwndSource src = HwndSource.FromHwnd(Handle);
      src.AddHook(MessageHook);
    }
    catch (Exception ex)
    {
      Console.WriteLine(ex.Message);
    }
  }

  private IntPtr MessageHook(IntPtr hwnd, int msg, IntPtr wp, IntPtr lp, ref bool handle)
  {
    return IntPtr.Zero;
  }
}

WPFでHot Key

Windows

WPFでHot Keyを取り扱う方法です。登録自体はWin32を用いますが、仮想キーの取り扱いやWindow Handleの取得方法なども課題になります。

順番に。登録・解除はWin32

using System.Runtime.InteropServices;

[DllImport("User32.dll")]
private static extern bool RegisterHotKey(IntPtr wnd, int id, int modifiers, int vk);

[DllImport("User32.dll")]
private static extern bool UnregisterHotKey(IntPtr wnd, int id);

キーコードは組み込みのEnumを用い、関数を使ってRegisterHotKeyに渡す仮想キーを得ます。逆変換の関数もあるので、Hot Keyのマネージメントをするクラスのみ仮想キーに依存するようにするのが良いでしょう。

using System.Windows.Input;

ModifierKeys mod;
Key key;

var vkey = KeyInterop.VirtualKeyFromKey(key);
key = KeyInterop.KeyFromVirtualKey(vkey);

Window HandleとWindow Messageの受け取りは次のとおり。Windows Formsと違ってちょっと面倒。

using System.Windows.Interop;

var wnd = new Window();
wnd.Show();

// Show()の後で無いと取れません。
var handle = new WindowInteropHelper(wnd).Handle;
var src = HwndSource.FromHwnd(handle);
src.AddHook(MessageHook);

private IntPtr MessageHook(IntPtr hwnd, int msg, IntPtr wp, IntPtr lp, ref bool handle)
{
  if (msg == 0x0312) // WM_HOTKEY
  {
    handle = true;

    int id = wp.ToInt32();
    int vkey = ((lp.ToInt32() >> 16) & 0x0000FFFF);
    int mod  = ((lp.ToInt32() >>  0) & 0x0000FFFF);
  }
  return IntPtr.Zero;
}

以上を組み合わせれば実現できます。組み上げ方はいろいろあるとは思いますが、私はdelegateを登録できるようにして、使用しました。

public Boolean Register(ModifierKeys mod, Key key, HotKeyEventHandler handler);