2025年2月1日土曜日

Go言語入門(4)

 

興味本位でGo言語に触れてみようと思う。WSLで32bit Windows実行ファイルを生成して32bit DLLを呼び出すってしてみる。<--もうまじで需要なさそう;


まず32bit DLLを生成せんと動作確認できないので、32bit DLLつくる。Windows 32bitターゲットでビルドできる環境を用意せんといかんのだが、ここからはWSL Ubuntuでやることにして、WSLにMingwを入れておけばよいわけだ。ところで、DLLはVisual StudioとBuild toolsで作るのが正攻法だと思うが、(以前も書いたけど、)Visual Studio Community EditionのLicense Termsが気に入らないのでVisual Studioは使わない。で、実は以前記事にしていた( WSLでWindows実行ファイルを作るまでの地味な道のり(1)WSLでWindows実行ファイルを作るまでの地味な道のり(2) )ので環境整備に関しては省略(<--手抜きではない)。Go開発環境はUbuntuなら、「Go言語入門(1)」に書いた通りそのまんまでいい。<--注 : WSLのUbuntuはUbuntu-24.04にしないといけない。さもないと古いバージョンのgolangがインストールされる。
プロジェクトは~/work/go/calldll32に作っていくことにする。
まぁこんな感じ
  • calldll32
  • |-- calldll32.go
  • `-- keeloq
  •     |-- Makefile
  •     |-- keeloq.c
  •     |-- keeloq.def
  •     `-- keeloq_call.c

keeloq/keeloq.c
  1. #include <stdio.h>
  2. #include <stdint.h>
  3.  
  4. uint32_t __stdcall keeloq_enc(uint32_t pr,uint64_t kr){
  5.     uint32_t nlf_table=0x3a5c742e;
  6.     uint32_t nlf_arg;
  7.     uint32_t nlf;
  8.     uint32_t newz;
  9.     uint32_t i;
  10.     for(i=0;i<528;i++){
  11.         nlf_arg=((pr&0x80000000)>>(31-4))
  12.                |((pr&0x04000000)>>(26-3))
  13.                |((pr&0x00100000)>>(20-2))
  14.                |((pr&0x00000200)>>(9-1))
  15.                |((pr&0x00000002)>>(1-0));
  16.         nlf=((nlf_table>>nlf_arg)&0x00000001);
  17.         newz=nlf^((pr&0x00010000)>>16)^(pr&0x00000001)^(uint32_t)(kr&0x0000000000000001);
  18.         pr=(newz<<31)|(pr>>1);
  19.         kr=((kr&0x0000000000000001)<<63)|(kr>>1);
  20.     }
  21.     return pr;
  22. }
  23. uint32_t __stdcall keeloq_dec(uint32_t cr,uint64_t kr){
  24.     uint32_t nlf_table=0x3a5c742e;
  25.     uint32_t nlf_arg;
  26.     uint32_t nlf;
  27.     uint32_t newz;
  28.     uint32_t i;
  29.     for(i=0;i<528;i++){
  30.         nlf_arg=((cr&0x40000000)>>(30-4))
  31.                |((cr&0x02000000)>>(25-3))
  32.                |((cr&0x00080000)>>(19-2))
  33.                |((cr&0x00000100)>>(8-1))
  34.                |((cr&0x00000001)>>(0-0));
  35.         nlf=((nlf_table>>nlf_arg)&0x00000001);
  36.         newz=nlf^((cr&0x00008000)>>15)^((cr&0x80000000)>>31)^(uint32_t)((kr&0x0000000000008000)>>15);
  37.         cr=(cr<<1)|(newz);
  38.         kr=((kr&0x8000000000000000)>>63)|(kr<<1);
  39.     }
  40.     return cr;
  41. }
  42. #if 0
  43. int main(int argc,char** argv){
  44.     uint32_t pr=0xf741e2db;
  45.     uint64_t kr=0x5cec6701b79fd949;
  46.     uint32_t cr;
  47.     cr=keeloq_enc(pr,kr);
  48.     printf("%08X\n",cr);
  49.     pr=keeloq_dec(cr,kr);
  50.     printf("%08X\n",pr);
  51.     return 0;
  52. }
  53. #endif

keeloq/keeloq.def
  1. LIBRARY keeloq
  2. EXPORTS
  3.     keeloq_enc
  4.     keeloq_dec

keeloq/Makefile
  1. CC=/usr/bin/i686-w64-mingw32-gcc
  2. CXX=/usr/bin/i686-w64-mingw32-g++
  3. LINK=/usr/bin/i686-w64-mingw32-gcc
  4. CFLAGS= -Wall -c
  5. LFLAGS= -shared -static-libgcc -static-libstdc++
  6.  
  7. keeloq.dll: keeloq.o
  8.     $(LINK) $(LFLAGS) -o keeloq.dll keeloq.o keeloq.def
  9.   
  10. keeloq.o: keeloq.c
  11.     $(CC) $(CFLAGS) keeloq.c -c -o keeloq.o
  12.  
  13. clean:
  14.     rm keeloq.o keeloq.dll

keeloq/keeloq_call.c
  1. #include <stdio.h>
  2. #include <stdint.h>
  3. #include <windows.h>
  4. typedef uint32_t (__stdcall *keeloq_enc_type)(uint32_t pr,uint64_t kr);
  5. typedef uint32_t (__stdcall *keeloq_dec_type)(uint32_t cr,uint64_t kr);
  6. HMODULE dll;
  7. keeloq_enc_type keeloq_enc;
  8. keeloq_dec_type keeloq_dec;
  9.  
  10. int main(int argc,char** argv){
  11.     uint32_t pr=0xf741e2db;
  12.     uint64_t kr=0x5cec6701b79fd949;
  13.     uint32_t cr;
  14.     dll=LoadLibrary("keeloq.dll");
  15.     keeloq_enc=(keeloq_enc_type)GetProcAddress(dll,"keeloq_enc");
  16.     keeloq_dec=(keeloq_dec_type)GetProcAddress(dll,"keeloq_dec");
  17.  
  18.     cr=keeloq_enc(pr,kr);
  19.     printf("%08X\n",cr);
  20.     pr=keeloq_dec(cr,kr);
  21.     printf("%08X\n",pr);
  22.     return 0;
  23. }

で、DLLを作って動作確認してみる。
cd keeloq
make
/usr/bin/i686-w64-mingw32-gcc keeloq_call.c
./a.exe
まぁ、こんなもんよ。
cd ..
で、goのコードを書いていく。DLLをcallするにはUbuntu Linuxでやった時と同じようにcgoを使う方法があるけど、今回は別の方法にしてみる。参考サイトはこちら(https://go.dev/wiki/WindowsDLLs)。で、こんなコードにする。

calldll32.go
  1. package main
  2.  
  3. import (
  4.     "fmt"
  5.     "syscall"
  6. )
  7.  
  8. var (
  9.     hdll,_=syscall.LoadLibrary("keeloq\\keeloq.dll")
  10.     keeloq_enc,_=syscall.GetProcAddress(hdll,"keeloq_enc")
  11.     keeloq_dec,_=syscall.GetProcAddress(hdll,"keeloq_dec")
  12. )
  13.  
  14. func Keeloq_enc(pr uint32,kr uint64)(cr uint32){
  15.     var nargs uintptr=3
  16.     ret,_,_:=syscall.Syscall9(uintptr(keeloq_enc),
  17.     nargs,
  18.     uintptr(pr),uintptr(kr),uintptr(kr>>32),0,0,0,0,0,0)
  19.     cr=uint32(ret)
  20.     return
  21. }
  22. func Keeloq_dec(cr uint32,kr uint64)(pr uint32){
  23.     var nargs uintptr=3
  24.     ret,_,_:=syscall.Syscall9(uintptr(keeloq_dec),
  25.     nargs,
  26.     uintptr(cr),uintptr(kr),uintptr(kr>>32),0,0,0,0,0,0)
  27.     pr=uint32(ret)
  28.     return
  29. }
  30.  
  31. func main() {
  32.     var pr uint32
  33.     var cr uint32
  34.     var kr uint64
  35.     defer syscall.FreeLibrary(hdll)
  36.     pr = 0xf741e2db
  37.     kr = 0x5cec6701b79fd949
  38.     cr = Keeloq_enc(pr, kr)
  39.     fmt.Printf("%08X\n", cr)
  40.     pr = Keeloq_dec(cr, kr)
  41.     fmt.Printf("%08X\n", pr)
  42. }
cgoもわかりにくかったけど、こいつもわかりにくい。ていうかなんとなくカンでやった感じ。Syscall9の引数が必ずuintptrなので、uint64をどう渡せばいいんや?ってなって、DLL側のkeeloq.cにprintfデバッグとかを埋め込んだりして、結局、上のようなコードでuint64が渡せるってわかった。これってドキュメントがなくてマジで手探り。コンピュータの関数呼び出しの仕組みから雑に推測した。処理系依存になっちゃうと思うけど、DLL使っている時点で処理系依存だよね。
で、
GOOS=windows GOARCH=386 go run calldll32.go
で、ビルドもしてみる。
GOOS=windows GOARCH=386 go build calldll32.go
ls -la
./calldll32.exe
うまくいった。
いちおう32bit windows実行ファイルであることを確認してみた。
はい。それらしくできています。
WSLでmingw使って実行ファイル作るとWSLの中で実行できる(Linuxなはずなのに.exeを実行できる)ってのが確かに便利だよね。

今日から2月。もう1年の12分の1(=8.33%)が過ぎ去ったと思うとなんか焦るね。人生はそんなに長くない。若い頃気付くべきだった。「やらなかった後悔より、やった後悔」(ニッコロ・マキャベリ)。やっちまった~ってのはすぐ忘れるか割り切れる。やっとけばよかった~ってのは忘れられないし傷が深い。

0 件のコメント:

コメントを投稿