VBScript と JScript でこんにちは!(その1)
VBScript と JScript を交配させるたったひとつの方法 - 葉っぱ日記 のパクリです。
VBScript と JScript はとっても仲がいいので、かんたんに交配させることができます。
rem=[1,2,3] ["Hello, JScript!"] = "Hello, VBScript!" WScript.Echo(["Hello, JScript!"])
例えば、これを hello1.txt などというファイル名で保存しておいて、コマンドラインから
C:\>cscript //E:VBScript hello1.txt
のように実行すると「Hello, VBScript!」と表示され
C:\>cscript //E:JScript hello1.txt
のように実行すると「Hello, JScript!」と表示されます。
Enjoy!
defined &func の不思議
Ubuntu8.10(x86_64) な環境で perl -e "require 'syscall.ph'" すると
Prototype mismatch: sub main::__LONG_MAX__ () vs none at /usr/lib/perl/5.10/_h2ph_pre.ph line 309. Constant subroutine __LONG_MAX__ redefined at /usr/lib/perl/5.10/_h2ph_pre.ph line 309.
という警告メッセージが出てしまう問題を深追いしていたんですけど、、、
該当コード /usr/lib/perl/5.10/_h2ph_pre.ph はこんな感じ:
... 192 unless (defined &__LONG_MAX__) { sub __LONG_MAX__() { 9223372036854775807 } } ... 308 # gross hack 309 unless (defined &__LONG_MAX__) { sub __LONG_MAX__ { 2147483647 } }
Prototype mismatch と Constant subroutine redefined の問題を切り分けるためにサンプルプログラムを書いてみたんですが、
unless (defined &PPP) { sub PPP() { 1 } } warn unless ( PPP() == 2);# Warning: something's wrong at - line 1. unless (defined &PPP) { sub PPP() { 2 } } warn unless ( PPP() == 2); unless (defined &PPS) { sub PPS() { 1 } } warn unless (&PPS == 2); unless (defined &PPS) { sub PPS() { 2 } } warn unless (&PPS == 2); unless (defined &PSP) { sub PSP() { 1 } } warn unless ( PSP() == 2);# Warning: something's wrong at - line 5. unless (defined &PSP) { sub PSP { 2 } } warn unless ( PSP() == 2); unless (defined &PSS) { sub PSS() { 1 } } warn unless (&PSS == 2); unless (defined &PSS) { sub PSS { 2 } } warn unless (&PSS == 2); unless (defined &SPP) { sub SPP { 1 } } warn unless ( SPP() == 2); unless (defined &SPP) { sub SPP() { 2 } } warn unless ( SPP() == 2); unless (defined &SPS) { sub SPS { 1 } } warn unless (&SPS == 2); unless (defined &SPS) { sub SPS() { 2 } } warn unless (&SPS == 2); unless (defined &SSP) { sub SSP { 1 } } warn unless ( SSP() == 2); unless (defined &SSP) { sub SSP { 2 } } warn unless ( SSP() == 2); unless (defined &SSS) { sub SSS { 1 } } warn unless (&SSS == 2); unless (defined &SSS) { sub SSS { 2 } } warn unless (&SSS == 2);
これを実行すると
Warning: something's wrong at - line 1. Warning: something's wrong at - line 3.
となっちゃいます。。。
コンパイルフェーズと実行フェーズの違いなんでしょうけど、、、意味がわかりません(><)
元々の意図からすると # gross hack は意味をなしてないってことなんですかね。
Perl で Win32::API プログラミング入門
ActivePerl (Windows版) には Win32::API - Perl Win32 API Import Facility - metacpan.org が付属しているので、今日からすぐに Win32::API を利用したプログラミングが出来ます。
簡単なメッセージボックスを表示するPerlプログラムは以下になります。
#!/usr/bin/perl use Win32::API; my $MessageBox = Win32::API->new("user32", "MessageBoxA", "NPPN", "N"); $MessageBox->Call(0, "Hello, World!\n", "Message", 0);
このプログラム実行すると、「Hello, World!」と書かれたWindowsメッセージボックスが表示されます。
これを Win32::API を使わない形に書き直すと以下のようになります。
#!/usr/bin/perl use DynaLoader; sub GetProcAddress { my ($DLL, $API) = @_; my $path = "$ENV{SystemRoot}\\system32\\$DLL"; my $libref = DynaLoader::dl_load_file($path); pack "L", DynaLoader::dl_find_symbol($libref, $API); } my $x86 = "" . "h\0\0\0\0" . "h" . pack("P", "Message") . "h" . pack("P", "Hello, World!\n") . "h\0\0\0\0" . "\xb8" . GetProcAddress("user32.dll", "MessageBoxA") . "\xff\xd0" # call eax . "\xc3" # ret ; DynaLoader::dl_install_xsub("X",unpack"L",pack"P",$x86);&X;
DynaLoader はとっても便利なモジュールですね。
Perl 5.10.0 / 日本語 Windows XP SP2 (x86) の環境で試しました。
(もしかしたら Vista や 64bit Windows 環境では動かないかもしれませんが、手元に環境がないので)→追記の動作報告があり、動いたみたいです。
■追記:
Twitter/ワッサーで以下の方々から動作報告をいただきました。Vistaやstrawberry perlでも動作するようです。
- "Win32::APIを使わない方" WinXP pro, Strawberry Perl v5.10.0でも実行可能なことを確認しますた by likkさん
- ちなむと、便利なDynaLoaderはVista上のActivePerlでもちゃんと動いてますよ。 by printf("hsegawa\n"); //DEBUG さん
- x64版Vistaでも動きました! (Perlは5.10 x86) by kaorunさん
ありがとうございます!
Perl で動的 x86_64 プログラミング
相対ジャンプのcall命令を動的に生成できるようにしておくとx86実行コード中に任意の文字列を埋め込めるので大変便利です。
● Linux/x86_64 で /usr/bin/perl -e 'print "Hello, world!\n"' を実行するサンプル
#!/usr/bin/perl -w sub P{"\xe8".pack('L',1+length$_[0]).$_[0]."\x00"}; use DynaLoader;DynaLoader::dl_install_xsub('X',unpack'L',pack'P', $_=P($^X)."_h-e\x00\x00H\x89\xe1".P('print "Hello, world!\n"') ."[1\xd2RSQWH\x89\xe6j;X\x0f\x05j\x01_j<\x0f\x05");&X
CentOS 4.4/x86_64 + AMD Opteron 240 EE + Perl 5.8.5 で動作確認
Hello, world!
上記プログラムは AMD64 ABI(*1) を利用した syscall で Linux のシステムコールを呼び出しているため、i386 環境では動作しません。uname -i が x86_64 な環境で動作可能です。
● 参考文献
はじめての x86_64/Linux プログラミング
$ vi amd64linux.pl
#!/usr/bin/perl use DynaLoader; my $x="" . "\x48\xb8Hello64!" # mov 'Hello64!',%rax . "\x50" # push %rax . "\x48\x89\xe6" # mov %rsp,%rsi . "\x48\xc7\xc7\x01\x00\x00\x00" # mov $0x1,%rdi . "\x48\xc7\xc2\x08\x00\x00\x00" # mov $0x8,%rdx . "\x48\xc7\xc0\x01\x00\x00\x00" # mov $0x1,%rax . "\x0f\x05" # syscall . "\x48\x89\xf8" # mov %rdi,%rax . "\x0f\x05" # syscall . "\x58" # pop %rax . "\xc3" # ret ; DynaLoader::dl_install_xsub("X",unpack("L",pack("P*",$x)));X(); print "ok\n";
$ perl amd64linux.pl
Hello64!Hello64!ok
FreeBSD/i386 な環境で perl のコードを実行する方法
今日からはじめての FreeBSD プログラミング、ということで、
まず最初に execve() を使って Hello, world! するプログラムを書きました。
8048080: 68 2d 65 00 00 push $0x652d 8048085: 89 e6 mov %esp,%esi 8048087: 68 6c 00 00 00 push $0x6c 804808c: 68 2f 70 65 72 push $0x7265702f 8048091: 68 2f 62 69 6e push $0x6e69622f 8048096: 68 2f 75 73 72 push $0x7273752f 804809b: 89 e7 mov %esp,%edi 804809d: 68 5c 6e 22 00 push $0x226e5c 80480a2: 68 72 6c 64 21 push $0x21646c72 80480a7: 68 2c 20 77 6f push $0x6f77202c 80480ac: 68 65 6c 6c 6f push $0x6f6c6c65 80480b1: 68 74 20 22 48 push $0x48222074 80480b6: 68 70 72 69 6e push $0x6e697270 80480bb: 89 e2 mov %esp,%edx 80480bd: 31 c0 xor %eax,%eax 80480bf: 50 push %eax 80480c0: 52 push %edx 80480c1: 56 push %esi 80480c2: 57 push %edi 80480c3: 89 e1 mov %esp,%ecx 80480c5: 50 push %eax 80480c6: 51 push %ecx 80480c7: 57 push %edi 80480c8: b0 3b mov $0x3b,%al 80480ca: 50 push %eax 80480cb: cd 80 int $0x80 80480cd: 31 db xor %ebx,%ebx 80480cf: 53 push %ebx 80480d0: 31 c0 xor %eax,%eax 80480d2: 40 inc %eax 80480d3: 50 push %eax 80480d4: cd 80 int $0x80
FreeBSD は syscall の呼び出し規約は Linux と違っていて、引数は全部スタック渡して int 80h するみたい。
FreeBSD Assembly Language Programming
kernel: int 80h ; Call kernel ret open: push dword mode push dword flags push dword path mov eax, 5 call kernel add esp, byte 12 ret
call 命令を使いたくない場合は、余分に1回ダミーでpushすれば大丈夫。
open: push dword mode push dword flags push dword path mov eax, 5 push eax ; Or any other dword int 80h add esp, byte 16
これに対して Linux は引数の一部にレジスタを使う(EBX, ECX, EDX …)。EAX の使い方はほぼ同じ。
open: mov eax, 5 mov ebx, path mov ecx, flags mov edx, mode int 80h
FreeBSD で定義されている syscall 番号を EAX レジスタに入れて、int 80h すればシステムコールが実行されます。
$ cat /usr/src/sys/kern/syscalls.master
$FreeBSD: src/sys/kern/syscalls.master,v 1.198.2.5 2006/10/10 13:19:47 rwatson Exp $ ; from: @(#)syscalls.master 8.2 (Berkeley) 1/13/94 ; ; System call name/number master file. ; Processed to created init_sysent.c, syscalls.c and syscall.h. ・・・(略)・・・ 0 AUE_NULL MSTD { int nosys(void); } syscall nosys_args int 1 AUE_EXIT MSTD { void sys_exit(int rval); } exit \ sys_exit_args void 2 AUE_FORK MSTD { int fork(void); } 3 AUE_NULL MSTD { ssize_t read(int fd, void *buf, \ size_t nbyte); } 4 AUE_NULL MSTD { ssize_t write(int fd, const void *buf, \ size_t nbyte); } ・・・(略)・・・ 59 AUE_EXECVE MSTD { int execve(char *fname, char **argv, \ char **envv); } ・・・(略)・・・
とりあえず、勢いで作ってみた NASM のソース貼っておきます
section .text global _start _start: push 0x00652d mov esi, esp push 0x006c push 0x7265702f push 0x6e69622f push 0x7273752f mov edi, esp push 0x00226e5c push 0x21646c72 push 0x6f77202c push 0x6f6c6c65 push 0x48222074 push 0x6e697270 mov edx, esp xor eax, eax push eax push edx push esi push edi mov ecx, esp push eax push ecx push edi mov al, 59 push eax int 0x80 xor ebx, ebx push ebx xor eax, eax inc eax push eax int 0x80
■ 実行結果
$ nasm -f elf e.asm && ld -s -o e e.o && ./e && echo ok Hello, world! ok $
FreeBSD の標準の as コマンドは gas みたいなのですが、どうも私は GAS の表記に慣れていなくて、ついつい NASM を使ってしまいます。。。
参考文献: Linux のアセンブラー: GAS と NASM を比較する
■ perl から任意の C ライブラリを呼び出す方法 - kazuhoのメモ置き場
id:kazuhooku さんの手法を応用すると、以下のように感じで Perl のプログラムから perl のコードを実行することが出来ます。
#!/usr/bin/perl use DynaLoader; my $s="h\x2de\x00\x00\x89\xe6hl\x00\x00\x00". "h\x2fperh\x2fbinh\x2fusr\x89\xe7". "h\x5cn\x22\x00hrld\x21h\x2c\x20wohelloht\x20\x22Hhprin". "\x89\xe21\xc0PRVW\x89\xe1PQW\xb0\x3bP\xcd\x80". "1\xdbS1\xc0\x40P\xcd\x80"; DynaLoader::dl_install_xsub("X",unpack("L",pack("P".length($s),$s)));X();
ここでは print "Hello, world!\n" の文字列が4byte毎にpush命令に分割されています。自分でコードをpushする際は、文字列の終端の \0 も忘れないように push しましょう。
FreeBSD FreeBSD 6.3-RELEASE #0: Wed Jan 16 04:18:52 UTC 2008 i386 で実行した結果は以下になりました。
Hello, world!
kazuhooku++
とりあえず、今週はここまで。