VBScript と JScript でこんにちは!(その2)

次のステップとしてrem構文とrem変数を活用してみましょう。

rem=null?
[rem]="Hello, VBScript!":rem="Hello, JScript!"
WScript.Echo([rem])

これを hello2.txt などというファイル名で保存しておいて、コマンドラインから

C:\>cscript //E:VBScript hello2.txt

のように実行すると「Hello, VBScript!」と表示され

C:\>cscript //E:JScript hello2.txt

のように実行すると「Hello, JScript!」と表示されます。

Enjoy!

VBScript と JScript でこんにちは!(その1)

VBScript と JScript を交配させるたったひとつの方法 - 葉っぱ日記 のパクリです。

VBScriptJScript はとっても仲がいいので、かんたんに交配させることができます。

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 な環境で動作可能です。

● 参考文献

  1. http://www.x86-64.org/documentation/abi-0.99.2.pdf

はじめての 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

i386/Linux の頃からいろいろ変わってますね。

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 のソース貼っておきます

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++

とりあえず、今週はここまで。