カーネル / VM Advent Calendar (2011年度) の参加記事です。
メインの内容はWindowsのDLLで遅延読み込みされるAPIを、Delay IATを書き換える方法でフックです。なんかぐぐっても見つからないので情報共有的な意味でも。
下記のような、Windowsのファイルマネージャーの検索バーみたいな半透明エディットボックスが欲しい!!

普通に、ウィンドウのガラス領域にエディットボックスをCreateWindowで作ってみたら、次のようになってしまって若干おかしい・・・・

いろいろぐぐってみたら、下記サイトに情報があった
http://weblogs.asp.net/kennykerr/archive/2007/01/23/controls-and-the-desktop-window-manager.aspx
情報のとおりにやってみたら、綺麗に描画できるようになった

しかし・・・

原因を調べると、次のようなことがわかった。

なので、良くあるWindowsのAPIフックを使い、再描画のタイミングを制御するためのコードを注入した。
自分は、http://ruffnex.oc.to/kenji/text/api_hook/ を参考にさせてもらった。

ただし、comctl32.dll からの DrawThemeBackground() APIの呼び出しは、dllの遅延インポート領域にかいてあるらしく、
直接フックはできないので、遅延インポート領域を初期化するのに使われるGetProcAddress() APIをフックして、そいつ経由で
コードを注入した

しかし、Windows Vista/7 はこの方法でいけたのだが、なぜかWindows8ではDrawThemeBackground() APIのフックに失敗する・・・・。

調べてみると、なんとDrawThemeBackground() APIを遅延インポートするのに、GetProcAddress() APIが使われていない・・・

誰だよ!勝手にアドレス書いたのは!!....orz

・・・ということで、こんどはきっちりメモリにはりついている、上図参照のqword ptr [__imp_DrawThemeBackground] を書き換えることにする。

なぜかネットにぜんぜん情報が無かったので、しょーがなくスタックオーバーフローの記事
http://stackoverflow.com/questions/3430718/thunk-table-in-import-address-tableとかそこにのってるリンク先とか色々見ながら自前で実装した
delay import address tableのベースアドレスをゲットできれば、あとは伝統的なIAT書き換えフックと要領は同じだった

これで実装してみたら、

C++11 Advent Calendarの参加記事です。
static_assertの利用例とdynamic_assert(仮)についてを記述します。
次のような仕様の"send_foo()"関数を作ることにする。
上記仕様をできるだけstatic_assertでチェックしてみる。
次のようにする
template <class T>
void send_foo(T t) {
static_assert(sizeof(t) <= (2 << (10 - 1)), "sizeof(t) is too big");
}
ただ、シフト演算よりもpow(2, 10)と書くほうがベターではある。
しかし、標準のpow関数の戻り値はstatic_assertには渡せないので、そうやりたい場合は
いまのところ標準にはそういうモノがなさそうなので自前で定義することになる。
power関数の仕様は以下
static_assert(power(0, 1) == 0, "0の1乗"); static_assert(power(0, 2) == 0, "0の2乗"); static_assert(power(1, 0) == 1, "1の0乗"); static_assert(power(1, 1) == 1, "1の1乗"); static_assert(power(1, 2) == 1, "1の2乗"); static_assert(power(2, 0) == 1, "2の0乗"); static_assert(power(2, 1) == 2, "2の1乗"); static_assert(power(2, 2) == 4, "2の2乗"); // 最大値付近のチェックは略
実装を書く
constexpr int power(int x, int y) {
return y <= 1 ? x : x * power(x, y - 1);
}
コンパイルしてもエラーが出ないことを確認して、元のsend_foo()関数を書き換える
template <class T>
void send_foo(T t) {
static_assert(sizeof(t) <= power(2, 10), "sizeof(t) is too big");
}
結果はこちら

実際にはpower関数は車輪なので、Sprout C++ Libraryなどを使わせてもらう。
type_traitsヘッダ内に、型を引数にとって、型の特性を教えてくれる機能いっぱいある。
これはstatic_assertでも使用することができる。
「Plain Old Dataかどうかを調べる」機能を呼び出すにはis_podを使う。
is_podはメタ関数と呼ばれていて、普通の関数とは違い下記のようにして結果を取得
bool PODか判定結果 = is_pod<PODかどうか調べたい型>::value;
ぜんぜん直観的ではないが、判定結果はstatic_assertに渡すことができる。
#include <type_traits>
template <class T>
void send_foo(T t) {
static_assert(std::is_pod<T>::value, "t is not POD");
}
最初は嫌だったけど、自分自身が似たようなテクニックを使わざるを得ない状況に陥ると
だんだん作った人の気持ちがわかり、この表記も許せるようになった。
いちおうマクロを定義すれば、直観的な関数呼び出しの書き方が使えるけどあんましおすすめしない。
#define IS_POD(arg) is_pod<decltype(arg)>::value
template <class T>
void send_foo(T t) {
static_assert(IS_POD(t), "T is not POD");
}
type_traitsについては、このサイトがめっちゃ詳しいです。
https://sites.google.com/site/cpprefjp/reference/type_traits
結果はこちら

外部の関数実行のテストはやりずらい。そのため、
まず実行するべき関数を配列に入れておき、
どれを実行するかを選ぶ新たな関数を作り、そいつに対してテストを行う。
template <class T>
void send_foo(T t) {
static_assert(sizeof(t) <= power(2, 10), "sizeof(t) is too big");
static_assert(std::is_pod<T>::value, "t is not POD");
// 実行すべき関数
std::function<void(void*)> functions[] = {
send_foo_0,
send_foo_1,
send_foo_2,
send_foo_3
// TODO ここにいっぱい関数が増える。
};
// どれを実行するか選ぶ関数
constexpr int index = get_index_by_size(sizeof(t));
static_assert(index >= 0 && index < _countof(functions), "array index out of bounds");
// 実行
functions[index](reinterpret_cast<void*>(&t));
}
get_index_by_size()関数で、実行するべき関数のを選ぶ。仕様は次のとおり。
static_assert(get_index_by_size(1) == 0, ""); static_assert(get_index_by_size(2) == 1, ""); static_assert(get_index_by_size(3) == 1, ""); static_assert(get_index_by_size(4) == 2, ""); static_assert(get_index_by_size(1023) == 9, ""); static_assert(get_index_by_size(1024) == 10, ""); // 最大値判定は略
実装を記述する。この実装にたどり着くまで30回くらいトライ&エラーをやった。
再帰関数は、正直人類には難しすぎる知恵だと思う。
constexpr int get_index_by_size(int size, int depth = 0) {
return (size < power(2, depth)) ?
depth : get_index_by_size(size, depth + 1);
}
そして、完成した時を見計らって、引数としてstd::vectorを渡し、計算で使ってたPODのサイズを
size()メソッドを使って計算するように仕様が変更される。無情な世の中である。
vectorなどを使われてしまうと、static_assertができない。その場合boost::testなどを使った通常のテストを行う必要がある。
しかし、static_assertと比べ、通常のテストには以下のような弱点がある

そのため、上記機能を実現するVisual Studioのアドインを開発中。dynamic_assert(仮)とした。
ソースとバイナリのダウンロードはこちら: DynamicAssertAddin.zip
アドインをVisual Studioに正常に追加できている場合、ツールメニューに「Dynamic Assertの開始」と
表示される。プロジェクトを開いてからそのメニューを選択すると、次のようになる。

現在はプロジェクト実行時にboost::testが走るVC++のプロジェクトのみ対応しています。「裏で走らせる」機能を、Jenkinsみたいな別のサーバーにやらせることができれば、大規模開発にも耐えると思う。
要するに、infinitestのパクリ。
以上です
companyだったかcompany_dataだったかcompany_infoだったかcompany_mstだったか思い出せなくてその都度IDEに怒られたり(静的言語)カンペを見たり(動的言語)してると集中力もってかれるんですよ!!!!!!!!!!!!orz
いや、プロジェクトで接尾語の明確な定義が決まってるんだったらいいんだけどさ.....
Emacsでファイル名とかを補完しようとすると*Completions*バッファが出るんだけど、そこには下記のようなヘルプメッセージが出る
Click <mouse-2> on a completion to select it. In this buffer, type RET to select the completion near point.
何度か見たら不要になる。下記の設定で消す/非表示/出さない(検索用)ようにできる。
(setq completion-show-help nil)
ブログのデザインいじってたら、どうもmixiとかRSSの新着に更新してもいないのに記事が乱立しちゃってました。すみません。
それだけではあれなので、近況報告します。今は相変わらずカスな日常を送っています。プログラミングの勉強会とかに出席し、前でプレゼンしている人に質問し、であとでustreamでの勉強会の動画をみて自分の声の予想外のキモさに悶えたりしています。
今春から、再度プログラマー的な仕事をすることになりますが、分野はローレイヤーからハイレイヤーに鞍替えすることになります。マイコンいじり(Webで操作できる自動風呂沸かし機とか、安く作りたい)はじめたりカーネルいじりは続行したいと思っていますが、余裕があるかは謎です。さしあたり流行りのIT技術を勉強&思い出す感じです。ちなみに俺情報工学科のくせにクラウド関連技術をまともに接触したこともないんです氏にたい。(Gmailとかは使うけど...)
研究室でLinuxのソースを触ることになったのだが、そこには生粋のVB厨C++厨であるどようびにとっては信じられない世界が広まっていた!
既に絶滅したかに思われていたさまざまなコードがどようびを襲う!
そして、カーネルいじりでもC++使えたらなぁ~。と思いぐぐってみたら次の記事が引っかかった!
Linux: C++ In The Kernel? | KernelTrap
Linus曰く
The fact is, C++ compilers are not trustworthy. They were even worse in 1992, but some fundamental facts haven't changed:
つまり、例外とnew/deleteを使わなければ良いんじゃね?良いよね!!!!new,deleteも自前で定義すればいいよね!
int a; int *b = &a; /* グローバル変数aに100を代入 */ void function1(void) { a = 100; } /* ローカル変数aに100を代入 */ void function2(void) { int a = 100; } /* グローバル変数bの示す先に100を代入 */ void function3(void) { *b = 100; } /* ローカル変数bの示す先に100を代・・・入・・・!? */ void function4(void) { int *b = 100; }「int *b;」の書き方は、「*bはint型」という意味である、という解説がなされる

(一部フィクションです)
そんなわけで、実行ファイルのグローバル変数とかが格納されている、.dataセクション、.rdataセクションのエンディアンを変更したい!
perlとかでがんばって書き変えるかめんどくせぇとか思ってたら、
objcopyコマンドで簡単にできてしまったのでメモ
gcc -c test.c -o test.o
objcopy -R .data -R .rdata test.o no-data.o
objcopy -R .text -R .bss --reverse-bytes=4 test.o reversed-data.o
gcc reversed-data.o no-data.o -o test.exe
test.cの内容は次のような感じであります
const char test_data[] = "ABCDEFGHIJKLMN";int main() {
return 0;
}
普通にコンパイルしたのと、エンディアン変換したのの比較をすると次のような具合です

え、このバイナリちゃんと実行できるのかって!?
実行してないからわかんないです!!!
件のハードウェアでは60%くらいは上手くいってます。
今の僕にまじめに検証してる時間と精神力なんてないんです.................... orz .......
.... 関係者の方々本当すんません ..........
エスケープシーケンス使うとUNIXシェルからの出力に色を付けることができる
でも、こいつをファイルにリダイレクトすると、ゴミが入ってしまう
(追記開始)
コメ欄より:
test -t 1 コマンドで、出力先が端末かそれ以外かの判断ができるので、その結果を用いてエスケープシーケンス出力の有無を判断すれば良い。
----- 終了 --------------------------------------------
(追記終了)
で、まぁシェルスクリプトからisatty()なんて呼べねーよなーとか思ってて放置してたんだけど、GNU版grepが、パターンにマッチした文字列に色を付ける機能を持っていることが判明。--color=autoと指定すると、grepが内部で端末への出力の場合エスケープシーケンスで色を付けて、ファイルへの出力では色をつけないように設定してくれるようです。
bashの関数にするとこんな感じです
function super_echo {
echo $* | GREP_COLOR='01' grep --color=auto '.' 2> /dev/null
if [ $? -ne 0 ]; then
echo $*
fi
}super_echo Hello, World!
実行結果はこんな感じです
autoconfを使う場合はこんな感じです。autoconfで使う場合、どんなシェルでも動くようにしなければならないためbashの関数機能は使えないです。代わりにマクロを利用します。あと、if文でよく使う[]が、m4マクロ的な理由で使えないです。
AC_DEFUN([SUPER_ECHO], [
echo $1 | GREP_COLOR='01' grep --color=auto '.' 2> /dev/null
if test $? -ne 0; then
echo $1
fi
])
SUPER_ECHO([Hello, World!])
実行結果はこんな感じです
enjoy!
--------
autoconf 2.65(最新版)でconfigureスクリプト作った。
最新版minixで実行したら、「grepが長いラインを処理できいか-eオプションが複数つかなくてnot acceptable」とかで停止した。
minix...(´;ω;`)ブワッ... パッケージisoにGNU版grepがあったので入れたら通った。
最近のコメント