#! /usr/bin/perl # hexed was written by Dennis Kaptain # July 2005 # released under GPL available at www.gnu.org/copyleft/gpl.html # agree to the license or don't use this program. # There is NO Warranty of any kind on this software # You agree to use this software AT YOUR OWN RISK. # If your disk is erased, your data is damaged or lost, # your head explodes, your girlfriend leaves you, or you get some # kind of disease from the use of this software, I won't help you. # hexed is available for free at http://www.kaptain.us # Written using Perl 5.8.6 and Tk 804.27-6 # on a Fedora Core 4 system. # This is intended to be a simple program. It does no error # checking in this version. There are lots of features that # can be added in the future. If you have comments, suggestions, # criticism, advise, dinner invitations, donations, death threats, # or any other communication you wish to relay, you may do so # at http://www.kaptain.us/downloads # Thank you for using this program, I hope you find it useful. my $version = '1.0'; # Version Number my $relDate = 'July 2005'; # Release Date use strict; use Tk; use Tk::FileSelect; use Cwd; my @bytes; # store the data my $numBytes = 0; # size of file my $pos = 0; # current position in file my $file; # file name my $openfile; # the current open file # gui variables my $mainWindow; my $menubar; my $fileMenu; my $editMenu; my $toolMenu; my $helpMenu; my $workWindow; my $workWindowFrame; my $statFrame; my $addressLab; my $addressBox; my $hexLab; my $hexBox; my $hexTag; my $dec2hexWin; my $dec2hexWinFrame; my $d2hdecLab; my $d2hdecBox; my $d2hhexLab; my $d2hhexBox; my $aboutWin; # convert a single ascii char to 2 digit ascii hex representation sub asc2hex { my @hchars = ('0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F'); my $num = ord(@_[0]); # changes char to decimal number my $hinib= $hchars[int($num/16)]; my $lonib= $hchars[($num%16)]; my $hex = $hinib.$lonib; return $hex; } #convert a number to a 16 bit hex string sub addr2hex { my @hchars = ('0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F'); my $num = @_[0]; my $char; my $hex = ""; for (my $x=0; $x<16; $x++) { $char = $hchars[$num%16]; $num = int($num/16); $hex = $char.$hex; $x++; } return $hex; } # user tool for conversions sub dec2hex { $dec2hexWin = $mainWindow->Toplevel; $dec2hexWinFrame = $dec2hexWin->Frame->pack; $d2hdecLab = $dec2hexWinFrame->Label('-text'=>'Decimal')->pack('-side'=>'left'); $d2hdecBox = $dec2hexWinFrame->Entry('-width'=>'8')->pack('-side'=>'left'); $d2hhexLab = $dec2hexWinFrame->Label('-text'=>'Hex')->pack('-side'=>'left'); $d2hhexBox = $dec2hexWinFrame->Entry('-width'=>'8')->pack('-side'=>'left'); $dec2hexWin->Button('-text'=>"Close", '-command'=>[sub{$dec2hexWin->destroy;}] )->pack('-side'=>'bottom', '-padx'=>'10'); $dec2hexWin->Button('-text'=>"Calculate", '-command'=>[\&dec2hexCalc] )->pack('-side'=>'bottom', '-padx'=>'10'); $d2hdecBox->insert('0','0'); } # about box sub about { $aboutWin = $mainWindow->Toplevel; my $aboutFrame = $aboutWin->Frame->pack; my $aboutLab = $aboutFrame->Label('-text'=>"hexed, a hex editor Written by Dennis Kaptain available at http://www.kaptain.us $relDate Version $version Released without warranty of any kind under GPL. Please read the license agreement which is available at: http://www.gnu.org/copyleft/gpl.html See the source code for more. ")->pack('-side'=>'left'); $aboutWin->Button('-text'=>"Close", '-command'=>[sub{$aboutWin->destroy;}] )->pack('-side'=>'bottom', '-padx'=>'10'); } # one byte decimal to hex sub dec2hexCalc { my @hchars = ('0','1','2','3','4','5','6','7','8','9', 'A','B','C','D','E','F'); my $num = $d2hdecBox->get(); my $hinib= $hchars[int($num/16)]; my $lonib= $hchars[($num%16)]; my $hex = $hinib.$lonib; $d2hhexBox->delete('0','end'); if ($num > 255 || $num < 0) { $d2hhexBox->insert('0','Error'); } else { $d2hhexBox->insert('0',$hex); } } # read a file byte by byte into @bytes[$numBytes] sub readfile { $numBytes =0; $file = @_[0]; open(FILE,"<$file"); while (read(FILE, $bytes[$numBytes], 1)) { $numBytes++; } close(FILE); } # change a byte at pos sub changeByte { $bytes[$pos] = chr(hex($hexBox->get())); redraw(); } # redraw the screen with all the data sub redraw { $workWindow->configure("-state"=>"normal"); my $addr = 0; my $addrhex; my $row = 1; my $ctr = 0; my $hexnum; $addressBox->delete('0','end'); $hexBox->delete('0','end'); $workWindow->delete('1.0','end'); while ($ctr < $numBytes-1) { # print the address $addrhex = addr2hex($addr); $addressBox->insert('0',$addrhex); $workWindow->insert("$row.end","$addrhex "); # print the hex codes for 8 bytes for (my $x=0; $x<8; $x++) { if ($ctr <= $numBytes) { # dont continue past the end of file $hexnum = asc2hex($bytes[$ctr]); $workWindow->insert("$row.end","$hexnum "); } else { $workWindow->insert("$row.end"," "); } $ctr++; } # print the ascii $workWindow->insert("$row.end"," "); $ctr-=8; for (my $x=0; $x<8; $x++) { if ($ctr <= $numBytes) { # dont continue past the end of file if(ord($bytes[$ctr]) > 31) { $workWindow->insert("$row.end","$bytes[$ctr]"); } else { $workWindow->insert("$row.end",chr(20)); } } $ctr++; } # next row... $workWindow->insert("$row.end","\n"); $row++; $addr+=8; } # update the status boxes $addrhex = addr2hex($pos); $addressBox->insert('0',$addrhex); $hexBox->insert('0',asc2hex($bytes[$pos])); # where to put the tags based on $pos # initial settings are for $pos=0 my $r = 1; # which row my $c1 = 10; # start tag position for the hex code my $c2 = 12; # end tag position for the hex code my $c3 = 35; # start tag position for the ascii code my $c4 = 36; # end tag position for the ascii code $r = int($pos/8)+1; $c1 = ($pos%8) * 3 + 10; $c2 = $c1 + 2; $c3 = ($pos%8) + 35; $c4 = $c3 + 1; $workWindow->tagAdd('hexTag',"$r.$c1","$r.$c2",); # create a tag $workWindow->tagConfigure('hexTag','-background'=>'yellow',); # create a tag $workWindow->tagAdd('decTag',"$r.$c3","$r.$c4",); # create a tag $workWindow->tagConfigure('decTag','-background'=>'yellow',); # create a tag $workWindow->see("$r.$c1"); # make pos visible in the window #$workWindow->configure("-state"=>"disabled"); } # catch arrow key presses and mouse clicks for navigation sub leftArrowPress { if ($pos > 0){ $pos--; redraw; } } sub rightArrowPress { if ($pos < $numBytes){ $pos++; redraw; } } sub upArrowPress { if ($pos -8 > 0){ $pos-=8; redraw; } else { leftArrowPress; } } sub downArrowPress { if ($pos < $numBytes + 8){ $pos+=8; redraw; } else { rightArrowPress; } } sub mouseLeftPress { my $lineDotChar; my $dot = 0; my $line = 1; my $colm = 0; my $colmStart = 10; my $colmEnd = 0; my $char = 0; my $ind1 = ""; my $ind2 = ""; my $ind3 = ""; my $ind4 = ""; my $tmp; my $addrhex = 0; #where did I get clicked? $lineDotChar=$workWindow->index('current'); chomp $lineDotChar; $dot = rindex($lineDotChar,'.'); $line = substr($lineDotChar,0,$dot); $colm = substr($lineDotChar,$dot+1); # find the file postion # which of 8 chars on that line is it? if ($colm > 9 ) { $char = 0; $colmStart = 10 } if ($colm > 12 ) { $char = 1; $colmStart = 13 } if ($colm > 15 ) { $char = 2; $colmStart = 16 } if ($colm > 18 ) { $char = 3; $colmStart = 19 } if ($colm > 21 ) { $char = 4; $colmStart = 22 } if ($colm > 24 ) { $char = 5; $colmStart = 25 } if ($colm > 27 ) { $char = 6; $colmStart = 28 } if ($colm > 30 ) { $char = 7; $colmStart = 31 } $colmEnd = $colmStart + 2; $pos = $char + (($line - 1) * 8); # got it! # fix up the window redraw; } # Menu Commands # File -> open sub openFile { my $fs = $workWindow->FileSelect( '-directory'=>Cwd::cwd(), '-initialfile'=>'', '-filter'=>'*.*', '-filelabel'=>'File to Open', '-filelistlabel'=>'File List', '-dirlabel'=>'Directories', '-dirlistlabel'=>'Dir List' #'-verify'=>[-T], #only text files ); $openfile = $fs->Show; readfile("$openfile"); $pos = 0; redraw; } # File -> save sub saveFile { $file = $openfile; open(FILE,">$file"); for (my $x =0;$x<$numBytes;$x++) { print FILE $bytes[$x]; } close(FILE); } # File -> save as sub saveFileAs { my $fs = $workWindow->FileSelect( '-directory'=>Cwd::cwd(), '-initialfile'=>'', '-filter'=>'*.*', '-filelabel'=>'File to Open', '-filelistlabel'=>'File List', '-dirlabel'=>'Directories', '-dirlistlabel'=>'Dir List' #'-verify'=>[-T], #only text files ); $openfile = $fs->Show; saveFile(); } # File -> close sub closeFile { for (my $x=0; $x < $numBytes; $x++) { $bytes[$x]=chr(0); } $numBytes = 0; $#bytes = -1; redraw; } # File -> exit sub quitProg { exit(0); } # Edit -> insert before sub editInsBefore { for (my $x=$numBytes; $x>$pos-1; $x--) { $bytes[$x+1] = $bytes[$x]; } $bytes[$pos] = chr(0); $numBytes++; redraw; } # Edit -> insert after sub editInsAfter { my $x; for ($x=$numBytes; $x>$pos; $x--) { $bytes[$x+1] = $bytes[$x]; } $bytes[$pos+1] = chr(0); $numBytes++; redraw; } # Edit -> delete # delete a byte at position $pos sub editDelByte { for (my $x=$pos; $x<$numBytes; $x++) { $bytes[$x] = $bytes[$x+1]; } $numBytes--; redraw(); } # ######################### # start the gui # ######################### $mainWindow = MainWindow->new(); $mainWindow->configure(-title=>"Hex Editor"); $mainWindow->geometry("+100+50"); # where to display on the screen # menu $menubar = $mainWindow->Frame; $menubar->pack('-side'=>'top', '-fill'=>'x'); # File menu and options $fileMenu = $menubar->Menubutton('-text'=>'File', '-underline'=>'0', '-tearoff'=>'0')->pack('-side'=>'left'); $fileMenu->command('-label'=>'Open','-underline'=>'0', '-command'=>[\&openFile]); $fileMenu->command('-label'=>'Save','-underline'=>'0', '-command'=>[\&saveFile]); $fileMenu->command('-label'=>'Save As','-underline'=>'5', '-command'=>[\&saveFileAs]); $fileMenu->command('-label'=>'Close','-underline'=>'0', '-command'=>[\&closeFile]); $fileMenu->command('-label'=>'Exit','-underline'=>'1', '-command'=>[\&quitProg]); # Edit menu and options $editMenu = $menubar->Menubutton('-text'=>'Edit', '-underline'=>'0', '-tearoff'=>'0')->pack('-side'=>'left'); $editMenu->command('-label'=>'Insert Before','-underline'=>'7', '-command'=>[\&editInsBefore]); $editMenu->command('-label'=>'Insert After','-underline'=>'7', '-command'=>[\&editInsAfter]); $editMenu->command('-label'=>'Delete','-underline'=>'0', '-command'=>[\&editDelByte]); # Tools menu and options $toolMenu = $menubar->Menubutton('-text'=>'Tools', '-underline'=>'0', '-tearoff'=>'0')->pack('-side'=>'left'); $toolMenu->command('-label'=>'Dec 2 Hex','-underline'=>'0', '-command'=>[\&dec2hex]); # Help menu and options $helpMenu = $menubar->Menubutton('-text'=>'Help', '-underline'=>'0', '-tearoff'=>'0')->pack('-side'=>'left'); $helpMenu->command('-label'=>'About','-underline'=>'0', '-command'=>[\&about]); # work window $workWindowFrame = $mainWindow->Frame->pack; $workWindow = $workWindowFrame->Scrolled('Text','-scrollbars'=>'e', '-wrap'=>'word','-height'=>'10','-width'=>'50' )->pack; $workWindow->bind('',\&rightArrowPress); $workWindow->bind('',\&leftArrowPress); $workWindow->bind('',\&upArrowPress); $workWindow->bind('',\&downArrowPress); $workWindow->bind('',\&mouseLeftPress); $statFrame = $mainWindow->Frame->pack; $addressLab = $statFrame->Label('-text'=>'Address')->pack('-side'=>'left'); $addressBox = $statFrame->Entry('-width'=>'8')->pack('-side'=>'left'); $hexLab = $statFrame->Label('-text'=>'Hex')->pack('-side'=>'left'); $hexBox = $statFrame->Entry('-width'=>'3')->pack('-side'=>'left'); $statFrame->Button('-text'=>"Change Byte", '-command'=>[\&changeByte] )->pack('-side'=>'left','-padx'=>'10'); $statFrame->Button('-text'=>"Exit", '-command'=>[sub{exit(0);}] )->pack('-side'=>'bottom','-padx'=>'10'); MainLoop; # run the program. wait for events.