Nasm::X86 - Generate Nasm assembler code
Write and execute x64 instructions from perl, for example:
Use avx512 instructions to reorder data using 512 bit zmm registers:
Start; my $q = Rs my $s = join '', ('a'..'p')x4;; Mov rax, Ds('0'x128); Vmovdqu32 zmm0, "[$q]"; Vprolq zmm1, zmm0, 32; Vmovdqu32 "[rax]", zmm1; Mov rdi, length $s; PrintOutMemory; Exit; ok $s =~ m(abcdefghijklmnopabcdefghijklmnopabcdefghijklmnopabcdefghijklmnop)s; ok assemble() =~ m(efghabcdmnopijklefghabcdmnopijklefghabcdmnopijklefghabcdmnopijkl)s;
Start a child process and wait for it, printing out the process identifiers of each process involved:
Start; # Start the program Fork; # Fork Test rax,rax; If # Parent {Mov rbx, rax; WaitPid; PrintOutRegisterInHex rax; PrintOutRegisterInHex rbx; GetPid; # Pid of parent as seen in parent Mov rcx,rax; PrintOutRegisterInHex rcx; } sub # Child {Mov r8,rax; PrintOutRegisterInHex r8; GetPid; # Child pid as seen in child Mov r9,rax; PrintOutRegisterInHex r9; GetPPid; # Parent pid as seen in child Mov r10,rax; PrintOutRegisterInHex r10; }; Exit; # Return to operating system my $r = assemble(); # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child # r9: 0000 0000 0003 0C63 #2 Pid of child # r10: 0000 0000 0003 0C60 #3 Pid of parent from child # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result # rcx: 0000 0000 0003 0C60 #6 Pid of parent
Read this file:
Start; # Start the program Mov rax, Rs($0); # File to read ReadFile; # Read file PrintOutMemory; # Print memory Exit; # Return to operating system my $r = assemble(); # Assemble and execute ok index($r, readFile($0)) > -1; # Output contains this file
You will need the Intel Software Development Emulator and the Networkwide Assembler installed on your test system. For full details of how to do this see: https://github.com/philiprbrenan/NasmX86/blob/main/.github/workflows/main.yml
Generate Nasm assembler code
Version "202104013".
The following sections describe the methods in each functional area of this module. For an alphabetic listing of all methods by name see Index.
Generate assembler code that can be assembled with Nasm
Set a label in the code section
Parameter Description 1 $l Label
Initialize the assembler
Example:
Start; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 PrintOutString "Hello World"; Exit; ok assemble =~ m(Hello World);
Layout bytes in memory and return their label
Parameter Description 1 @d Data to be laid out
Start; my $q = Rs('a'..'z'); Mov rax, Ds('0'x64); # Output area # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 Vmovdqu32(xmm0, "[$q]"); # Load Vprolq (xmm0, xmm0, 32); # Rotate double words in quad words Vmovdqu32("[rax]", xmm0); # Save Mov rdi, 16; PrintOutMemory; Exit; ok assemble() =~ m(efghabcdmnopijkl)s;
Layout bytes in read only memory and return their label
Start; Comment "Print a string from memory"; my $s = "Hello World"; Mov rax, Rs($s); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 Mov rdi, length $s; PrintOutMemory; Exit; ok assemble =~ m(Hello World);
Layout data
Parameter Description 1 $s Element size 2 @d Data to be laid out
Layout bytes in the data segment and return their label
Parameter Description 1 @bytes Bytes to layout
Layout words in the data segment and return their label
Parameter Description 1 @words Words to layout
Layout double words in the data segment and return their label
Parameter Description 1 @dwords Double words to layout
Layout quad words in the data segment and return their label
Parameter Description 1 @qwords Quad words to layout
Insert a comment into the assembly code
Parameter Description 1 @comment Text of comment
Start; Comment "Print a string from memory"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 my $s = "Hello World"; Mov rax, Rs($s); Mov rdi, length $s; PrintOutMemory; Exit; ok assemble =~ m(Hello World);
Exit with the specified return code or zero if no return code supplied
Parameter Description 1 $c Return code
Start; PrintOutString "Hello World"; Exit; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 ok assemble =~ m(Hello World);
Save the first 4 parameter registers
Restore the first 4 parameter registers
Restore the first 4 parameter registers except rax so it can return its value
Save the first 7 parameter registers
Restore the first 7 parameter registers
Restore the first 7 parameter registers except rax which is being used to return the result
Restore the first 7 parameter registers except rax and rdi which are being used to return the results
If
Parameter Description 1 $then Then - required 2 $else Else - optional
Start; Mov rax, 0; Test rax,rax; If # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 {PrintOutRegisterInHex rax; } sub {PrintOutRegisterInHex rbx; }; Mov rax, 1; Test rax,rax; If # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 {PrintOutRegisterInHex rcx; } sub {PrintOutRegisterInHex rdx; }; Exit; ok assemble() =~ m(rbx.*rcx)s;
For
Parameter Description 1 $body Body 2 $register Register 3 $limit Limit on loop 4 $increment Increment
Start; # Start the program For # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 {PrintOutRegisterInHex rax } rax, 16, 1; Exit; # Return to operating system my $r = assemble; ok $r =~ m(( 0000){3} 0000)i; ok $r =~ m(( 0000){3} 000F)i;
Create a sub with optional parameters name=> the name of the subroutine so it can be reused rather than regenerated, comment=> a comment describing the sub
Parameter Description 1 $body Body 2 %options Options.
Return the size of a register
Parameter Description 1 $r Register
Push registers onto the stack
Parameter Description 1 @r Register
Pop registers from the stack
Peek at register on stack
Write a new line
Start; Comment "Print a string from memory"; my $s = "Hello World"; Mov rax, Rs($s); Mov rdi, length $s; PrintOutMemory; Exit; ok assemble =~ m(Hello World);
Write a constant string to sysout.
Parameter Description 1 $string String
Start; PrintOutString "Hello World"; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 Exit; ok assemble =~ m(Hello World);
Print the memory addressed by rax for a length of rdi
Write the content of register rax to stderr in hexadecimal in big endian notation
Start; my $q = Rs('abababab'); Mov(rax, "[$q]"); PrintOutString "rax: "; PrintOutRaxInHex; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 PrintOutNl; Xor rax, rax; PrintOutString "rax: "; PrintOutRaxInHex; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 PrintOutNl; Exit; ok assemble() =~ m(rax: 6261 6261 6261 6261.*rax: 0000 0000 0000 0000)s;
Clear registers by setting them to zero
Parameter Description 1 @registers Registers
Reverse the bytes in rax
Write the content of register rax to stderr in hexadecimal in little endian notation
Start; Mov rax, 0x88776655; Shl rax, 32; Or rax, 0x44332211; PrintOutRaxInHex; PrintOutRaxInReverseInHex; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 Exit; ok assemble =~ m(8877 6655 4433 2211 1122 3344 5566 7788)s;
Print any register as a hex string
Parameter Description 1 $r Name of the register to print
Start; my $q = Rs(('a'..'p')x4); Mov r8,"[$q]"; PrintOutRegisterInHex r8; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 Exit; ok assemble() =~ m(r8: 6867 6665 6463 6261)s;
Print the instruction pointer in hex
Print the flags register in hex
Print the general purpose registers in hex
Start; my $q = Rs('abababab'); Mov(rax, 1); Mov(rbx, 2); Mov(rcx, 3); Mov(rdx, 4); Mov(r8, 5); Lea r9, "[rax+rbx]"; PrintOutRegistersInHex; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 Exit; my $r = assemble(); ok $r =~ m( r8: 0000 0000 0000 0005.* r9: 0000 0000 0000 0003.*rax: 0000 0000 0000 0001)s; ok $r =~ m(rbx: 0000 0000 0000 0002.*rcx: 0000 0000 0000 0003.*rdx: 0000 0000 0000 0004)s;
Dump memeory from teh address in rax for the length in rdi
Allocate the amount of memory specified in rax via mmap and return the address of the allocated memeory in rax
Start; my $N = 2048; my $q = Rs('a'..'p'); Mov rax, $N; allocateMemory; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 PrintOutRegisterInHex rax; Vmovdqu8 xmm0, "[$q]"; Vmovdqu8 "[rax]", xmm0; Mov rdi,16; PrintOutMemory; PrintOutNl; Mov rdi, $N; freeMemory; PrintOutRegisterInHex rax; Exit; ok assemble() =~ m(abcdefghijklmnop)s; Start; my $N = 4096; my $S = registerSize rax; Mov rax, $N; allocateMemory; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 PrintOutRegisterInHex rax; Mov rdi, $N; MemoryClear; PrintOutRegisterInHex rax; PrintOutMemoryInHex; Exit; my $r = assemble; if ($r =~ m((0000.*0000))s) {is_deeply length($1), 10269; }
Free memory via mmap. The address of the memory is in rax, the length to free is in rdi
Start; my $N = 2048; my $q = Rs('a'..'p'); Mov rax, $N; allocateMemory; PrintOutRegisterInHex rax; Vmovdqu8 xmm0, "[$q]"; Vmovdqu8 "[rax]", xmm0; Mov rdi,16; PrintOutMemory; PrintOutNl; Mov rdi, $N; freeMemory; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 PrintOutRegisterInHex rax; Exit; ok assemble() =~ m(abcdefghijklmnop)s; Start; my $N = 4096; my $S = registerSize rax; Mov rax, $N; allocateMemory; PrintOutRegisterInHex rax; Mov rdi, $N; MemoryClear; PrintOutRegisterInHex rax; PrintOutMemoryInHex; Exit; my $r = assemble; if ($r =~ m((0000.*0000))s) {is_deeply length($1), 10269; }
Fork
Start; # Start the program Fork; # Fork # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 Test rax,rax; If # Parent {Mov rbx, rax; WaitPid; PrintOutRegisterInHex rax; PrintOutRegisterInHex rbx; GetPid; # Pid of parent as seen in parent Mov rcx,rax; PrintOutRegisterInHex rcx; } sub # Child {Mov r8,rax; PrintOutRegisterInHex r8; GetPid; # Child pid as seen in child Mov r9,rax; PrintOutRegisterInHex r9; GetPPid; # Parent pid as seen in child Mov r10,rax; PrintOutRegisterInHex r10; }; Exit; # Return to operating system my $r = assemble(); # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child # r9: 0000 0000 0003 0C63 #2 Pid of child # r10: 0000 0000 0003 0C60 #3 Pid of parent from child # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result # rcx: 0000 0000 0003 0C60 #6 Pid of parent if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s) {ok $2 eq $4; ok $2 eq $5; ok $3 eq $6; ok $2 gt $6; } Start; # Start the program GetUid; # Userid PrintOutRegisterInHex rax; Exit; # Return to operating system my $r = assemble(); ok $r =~ m(rax:( 0000){3});
Get process identifier
Start; # Start the program Fork; # Fork Test rax,rax; If # Parent {Mov rbx, rax; WaitPid; PrintOutRegisterInHex rax; PrintOutRegisterInHex rbx; GetPid; # Pid of parent as seen in parent # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 Mov rcx,rax; PrintOutRegisterInHex rcx; } sub # Child {Mov r8,rax; PrintOutRegisterInHex r8; GetPid; # Child pid as seen in child # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 Mov r9,rax; PrintOutRegisterInHex r9; GetPPid; # Parent pid as seen in child Mov r10,rax; PrintOutRegisterInHex r10; }; Exit; # Return to operating system my $r = assemble(); # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child # r9: 0000 0000 0003 0C63 #2 Pid of child # r10: 0000 0000 0003 0C60 #3 Pid of parent from child # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result # rcx: 0000 0000 0003 0C60 #6 Pid of parent if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s) {ok $2 eq $4; ok $2 eq $5; ok $3 eq $6; ok $2 gt $6; } Start; # Start the program GetUid; # Userid PrintOutRegisterInHex rax; Exit; # Return to operating system my $r = assemble(); ok $r =~ m(rax:( 0000){3});
Get parent process identifier
Start; # Start the program Fork; # Fork Test rax,rax; If # Parent {Mov rbx, rax; WaitPid; PrintOutRegisterInHex rax; PrintOutRegisterInHex rbx; GetPid; # Pid of parent as seen in parent Mov rcx,rax; PrintOutRegisterInHex rcx; } sub # Child {Mov r8,rax; PrintOutRegisterInHex r8; GetPid; # Child pid as seen in child Mov r9,rax; PrintOutRegisterInHex r9; GetPPid; # Parent pid as seen in child # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 Mov r10,rax; PrintOutRegisterInHex r10; }; Exit; # Return to operating system my $r = assemble(); # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child # r9: 0000 0000 0003 0C63 #2 Pid of child # r10: 0000 0000 0003 0C60 #3 Pid of parent from child # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result # rcx: 0000 0000 0003 0C60 #6 Pid of parent if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s) {ok $2 eq $4; ok $2 eq $5; ok $3 eq $6; ok $2 gt $6; } Start; # Start the program GetUid; # Userid PrintOutRegisterInHex rax; Exit; # Return to operating system my $r = assemble(); ok $r =~ m(rax:( 0000){3});
Get userid of current process
Wait for the pid in rax to complete
Start; # Start the program Fork; # Fork Test rax,rax; If # Parent {Mov rbx, rax; WaitPid; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 PrintOutRegisterInHex rax; PrintOutRegisterInHex rbx; GetPid; # Pid of parent as seen in parent Mov rcx,rax; PrintOutRegisterInHex rcx; } sub # Child {Mov r8,rax; PrintOutRegisterInHex r8; GetPid; # Child pid as seen in child Mov r9,rax; PrintOutRegisterInHex r9; GetPPid; # Parent pid as seen in child Mov r10,rax; PrintOutRegisterInHex r10; }; Exit; # Return to operating system my $r = assemble(); # r8: 0000 0000 0000 0000 #1 Return from fork as seen by child # r9: 0000 0000 0003 0C63 #2 Pid of child # r10: 0000 0000 0003 0C60 #3 Pid of parent from child # rax: 0000 0000 0003 0C63 #4 Return from fork as seen by parent # rbx: 0000 0000 0003 0C63 #5 Wait for child pid result # rcx: 0000 0000 0003 0C60 #6 Pid of parent if ($r =~ m(r8:( 0000){4}.*r9:(.*)\s{5,}r10:(.*)\s{5,}rax:(.*)\s{5,}rbx:(.*)\s{5,}rcx:(.*)\s{2,})s) {ok $2 eq $4; ok $2 eq $5; ok $3 eq $6; ok $2 gt $6; } Start; # Start the program GetUid; # Userid PrintOutRegisterInHex rax; Exit; # Return to operating system my $r = assemble(); ok $r =~ m(rax:( 0000){3});
Read the time stamp counter
Start; for(1..10) {readTimeStampCounter; # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 PrintOutRegisterInHex rax; } Exit; my @s = split / /, assemble(); my @S = sort @s; is_deeply \@s, \@S;
Open a file, whose name is addressed by rax, for read and return the file descriptor in rax
Start; # Start the program Mov rax, Rs($0); # File to stat OpenRead; # Open file # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 PrintOutRegisterInHex rax; Close(rax); # Close file PrintOutRegisterInHex rax; Exit; # Return to operating system my $r = assemble(); ok $r =~ m(( 0000){3} 0003)i; # Expected file number ok $r =~ m(( 0000){4})i; # Expected file number
Close a file descriptor
Parameter Description 1 $fdes File descriptor
Start; # Start the program Mov rax, Rs($0); # File to stat OpenRead; # Open file PrintOutRegisterInHex rax; Close(rax); # Close file # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 PrintOutRegisterInHex rax; Exit; # Return to operating system my $r = assemble(); ok $r =~ m(( 0000){3} 0003)i; # Expected file number ok $r =~ m(( 0000){4})i; # Expected file number
Map local data
Start a local data area on the stack
Parameter Description 1 $local Local data descriptor
Free a local data area on the stack
Add a local variable
Parameter Description 1 $local Local data descriptor 2 $length Length of data 3 $comment Optional comment
Address a local variable on the stack
Parameter Description 1 $variable Variable
Add some 8 byte local variables and return an array of variable definitions
Parameter Description 1 $local Local data descriptor 2 @comments Optional comment
Create a local data descriptor consisting of the specified number of 8 byte local variables and return an array: (local data descriptor, variable definitions...)
Parameter Description 1 $N Number of variables required
Clear memory - the address of the memory is in rax, the length in rdi
Stat a file whise name is addressed by rax to get its size in rax
Start; # Start the program Mov rax, Rs($0); # File to stat StatSize; # Stat the file # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 PrintOutRegisterInHex rax; Exit; # Return to operating system my $r = assemble() =~ s( ) ()gsr; if ($r =~ m(rax:([0-9a-f]{16}))is) # Compare file size obtained with that from fileSize() {is_deeply $1, sprintf("%016X", fileSize($0)); }
Read a file whose name is addressed by rax into memory. The address of the mapped memory and its length are returned in registers rax,rdi
Start; # Start the program Mov rax, Rs($0); # File to read ReadFile; # Read file # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲 PrintOutMemory; # Print memory Exit; # Return to operating system my $r = assemble(); # Assemble and execute ok index($r, readFile($0)) > -1; # Output contains this file
Assemble the generated code
Parameter Description 1 %options Options
Start; PrintOutString "Hello World"; Exit; ok assemble =~ m(Hello World); # 𝗘𝘅𝗮𝗺𝗽𝗹𝗲
Create a unique label
1 AllocateAll8 - Create a local data descriptor consisting of the specified number of 8 byte local variables and return an array: (local data descriptor, variable definitions.
2 allocateMemory - Allocate the amount of memory specified in rax via mmap and return the address of the allocated memeory in rax
3 assemble - Assemble the generated code
4 ClearRegisters - Clear registers by setting them to zero
5 Close - Close a file descriptor
6 Comment - Insert a comment into the assembly code
7 Db - Layout bytes in the data segment and return their label
8 Dbwdq - Layout data
9 Dd - Layout double words in the data segment and return their label
10 Dq - Layout quad words in the data segment and return their label
11 Ds - Layout bytes in memory and return their label
12 Dw - Layout words in the data segment and return their label
13 Exit - Exit with the specified return code or zero if no return code supplied
14 For - For
15 Fork - Fork
16 freeMemory - Free memory via mmap.
17 GetPid - Get process identifier
18 GetPPid - Get parent process identifier
19 GetUid - Get userid of current process
20 If - If
21 label - Create a unique label
22 localData - Map local data
23 LocalData::allocate8 - Add some 8 byte local variables and return an array of variable definitions
24 LocalData::free - Free a local data area on the stack
25 LocalData::start - Start a local data area on the stack
26 LocalData::variable - Add a local variable
27 LocalVariable::stack - Address a local variable on the stack
28 MemoryClear - Clear memory - the address of the memory is in rax, the length in rdi
29 OpenRead - Open a file, whose name is addressed by rax, for read and return the file descriptor in rax
30 PeekR - Peek at register on stack
31 PopR - Pop registers from the stack
32 PrintOutMemory - Print the memory addressed by rax for a length of rdi
33 PrintOutMemoryInHex - Dump memeory from teh address in rax for the length in rdi
34 PrintOutNl - Write a new line
35 PrintOutRaxInHex - Write the content of register rax to stderr in hexadecimal in big endian notation
36 PrintOutRaxInReverseInHex - Write the content of register rax to stderr in hexadecimal in little endian notation
37 PrintOutRegisterInHex - Print any register as a hex string
38 PrintOutRegistersInHex - Print the general purpose registers in hex
39 PrintOutRflagsInHex - Print the flags register in hex
40 PrintOutRipInHex - Print the instruction pointer in hex
41 PrintOutString - Write a constant string to sysout.
42 PushR - Push registers onto the stack
43 Rb - Layout bytes in the data segment and return their label
44 Rbwdq - Layout data
45 Rd - Layout double words in the data segment and return their label
46 ReadFile - Read a file whose name is addressed by rax into memory.
47 readTimeStampCounter - Read the time stamp counter
48 registerSize - Return the size of a register
49 RestoreFirstFour - Restore the first 4 parameter registers
50 RestoreFirstFourExceptRax - Restore the first 4 parameter registers except rax so it can return its value
51 RestoreFirstSeven - Restore the first 7 parameter registers
52 RestoreFirstSevenExceptRax - Restore the first 7 parameter registers except rax which is being used to return the result
53 RestoreFirstSevenExceptRaxAndRdi - Restore the first 7 parameter registers except rax and rdi which are being used to return the results
54 ReverseBytesInRax - Reverse the bytes in rax
55 Rq - Layout quad words in the data segment and return their label
56 Rs - Layout bytes in read only memory and return their label
57 Rw - Layout words in the data segment and return their label
58 S - Create a sub with optional parameters name=> the name of the subroutine so it can be reused rather than regenerated, comment=> a comment describing the sub
59 SaveFirstFour - Save the first 4 parameter registers
60 SaveFirstSeven - Save the first 7 parameter registers
61 SetLabel - Set a label in the code section
62 Start - Initialize the assembler
63 StatSize - Stat a file whise name is addressed by rax to get its size in rax
64 WaitPid - Wait for the pid in rax to complete
This module is written in 100% Pure Perl and, thus, it is easy to read, comprehend, use, modify and install via cpan:
sudo cpan install Nasm::X86
philiprbrenan@gmail.com
http://www.appaapps.com
Copyright (c) 2016-2021 Philip R Brenan.
This module is free software. It may be used, redistributed and/or modified under the same terms as Perl itself.
To install Nasm::X86, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Nasm::X86
CPAN shell
perl -MCPAN -e shell install Nasm::X86
For more information on module installation, please visit the detailed CPAN module installation guide.