呵呵,perl真的强大到外挂领域都有其身影,呵呵,好几个外挂研究的站点都有提及perl
并有他们几个很牛的外挂研究高手在指点如何学perl来开发外挂。hoho
呵呵,perl的AI居然是在外挂里面得以充分发挥,hoho!
Kore第一讲: Kore运行与编译
Kore第一讲: Kore运行与编译 by ICE-WR
Kore是用perl来编写,目前WIN32系统比较流行的是ActivePerl,网站为
http://www.activestate.com/,而如果你需要把Kore编译为exe执行文件,那你需要安装Perl PDK,里面有perlapp用来把.pl文件转为.exe文件
已经安装了ActivePerl和perl PDK了,但为什么还不能运行Kore.pl呢?请打开Kore.pl,你会看到类似这样的语句:
use Time::HiRes qw(time usleep);
use IO::Socket;
use语句用作调用模块,而你没有这些模块那就当然运行不了咯,PDK里包含一个叫VPM(Visual Package Manager)的模块管理软件,运行后会出现一个搜索网页,在搜索栏输入Time::HiRes和IO::Socket就会从网上把模块搜索出来并安装到你的电脑里,只要把模块都装好,那你就可以运行Kore.pl了
如何把.pl文件转为.exe文件呢?这里需要用PDK里的perlapp,在这里用KE的编译来举例:
perlapp --xclude --icon KoreEasy.ico KoreEasy.pl
--xclude 编译时不包含perl58.dll文件,这样会让编译出来的程序小点,但别的机器要运行时需要复制perl58.dll
--icon 这个是指定图标文件
ActivePerl下载地址:
http://www.activestate.com/Products/ActivePerl/
Perl PDK下载地址:http://www.activestate.com/Products/Perl_Dev_Kit/
以下为KoreEasy编译的完整语句,包含版本信息
perlapp --info FileVersion=0.8.00.0306roductVersion=0.8.00.0306;FileDe脚本ion=ICE-WR;CompanyName=http://www.mu20.com;LegalCopyright=ICE-WR;LegalTrademarks=ICE-WR;OriginalFilename=KoreEasy.exeroductName=KoreEasy --xclude --verbose --force --icon KoreEasy.ico KoreEasy.pl
我是按着kora的说明配置的Perl和PDK
This is the official homepage of the Kore Project - An open source, complete console-based Ragnarok client. Gravity (company that developed Ragnarok Online) has nothing to do with Kore, this project is unofficial.
What is Kore?
Kore is a console-based Ragnarok client/bot, written in Perl/C.
How do I install Kore?
Kore can be run on Windows and Linux, and if requested, versions can be compiled for Mac operating systems.
Kore is run like any other program. Grab Kore from the download page, unzip, and 执行. Grab the manual too for console commands, chat commands (receieve commands in-game!), and configuration options.
How do I compile Kore?
Windows
Download and install ActivePerl for Windows
Download and install Perl DevKit for windows
Next you must install the necessary Perl packages:
1) Go to Start->Run... and run \'ppm\'
2) In the console 无效 \'install Time::HiRes\'
3) Next 无效 \'install Win32::API\'
4) If it returns with multiple results 无效 \'install 1\'
In the Kore root directory make a file called \"Kore.bat\". In this file put the following line:
perl source\\Kore.pl
To run Kore without compiling double-click on Kore.bat.
In the Kore root directory make a file called \"Kore-compile.bat\". In this file put the following line:
perlapp --force -o=Kore.exe source\\Kore.pl
To compile Kore just double-click on Kore-compile.bat.
Kore第二讲: Kore与Perl
Kore第二讲: Kore与Perl
Kore第二讲: Kore与Perl by ICE-WR
Kore是用perl语言来编写的,perl是一种简单而强大的脚本语言,.pl的文件就是perl脚本,需要解释器来运行,例如:Windows平台下的Active Perl。
要想了解Kore,不需要对perl非常精通,当你第一次看Kore.pl时估计会非常头痛,一些语句甚至无法理解,大部分都是一些匹配模式,例如:
foreach () {
next if (/^#/);
s/[\r\n]//g;
s/\s+/ /g;
s/\s+$//g;
@args = split /\s/, $_;
}
建议你可以先不看这些语句,下面附带Perl的教学文件:
http://www.mu20.com/viewFile.asp?Boardid=14&ID=1281
编写.pl文件的话,严重推荐用这个软件:UltraEditor 32
Kore第三讲: Kore的程序结构及主程序解释
Kore第三讲: Kore的程序结构及主程序解释
Kore第三讲: Kore的程序结构及主程序解释 by ICE-WR
Kore的程序执行流程如下:
1)程序初始化:调用模块,初始化变量
2)读入数据文件:包括config文件夹和tables文件夹
3)建立控制台指令输入连接及与服务器连接
4)执行主程序,主程序是个循环,只要没有接收到quit指令就一直执行,包括如下功能:
a)执行控制台指令
b)分析接收到的封包,转化为Kore里的游戏信息,如HP/SP等
c)执行AI:AI决定人物角色需要做什么,发送什么指令到服务器
d)检查连接情况
5)接收到quit指令后结束连接和程序
Kore原版有8000行代码,但实际上主程序只有20行代码!了解主程序你就能知道整个运作原理了,以下位主程序代码的解释:
while ($quit != 1) {
#当不是$quit = 1时,一直执行以下程序,假如你在Kore里输入quit,那就会退出Kore啦
usleep($config{'sleepTime'});
#usleep就是要睡眠多少微秒,作用是等待封包接收及减低CPU占用率
if (dataWaiting(\$无效_socket)) {
#$无效_socket用来读取控制台指令,如果有指令输入,则执行以下代码
$无效_socket->recv($无效, $MAX_READ);
#从$无效_socket读取数据并储存在$无效,例如我们在Kore里输入“i\n”,那$无效就等于“i\n”,“\n”代表回车
parse无效($无效);
#parse无效是个子程序,用来执行控制台指令
} elsif (dataWaiting(\$remote_socket)) {
#$remote_socket用来接收封包,假如服务器发送了封包过来,则执行以下代码
$remote_socket->recv($new, $MAX_READ);
#从$remote_socket读取服务器发送过来的数据,$MAX_READ是用来限制一次读入的数据量
$msg .= $new;
#由于服务器每次发送的封包不一定是完整的数据,所以Kore会把封包连接起来并储存在$msg
$msg_length = length($msg);
#这里是计算解释封包前的长度
while ($msg ne "" {
#当有封包数据时,一直执行下面的代码
$msg = parseMsg($msg);
#parseMsg是封包解释子程序,负责把封包转换为各种数据,例如HP/SP等,运行时读取$msg,运行后把已经解释完毕的封包去掉,并返回给$msg,这个过程就是,读完一段就删除一段
last if ($msg_length == length($msg));
#因为封包解释子程序是解释完一段就删除一段的,所以这里比较解释前和解释后的$msg长度,来判断是否仍然有未解释的封包,假如解释前和解释后的$msg长度一样,那就代表已经没有要解释的封包,终止执行这个封包解释循环
$msg_length = length($msg);
#记录下解释后的封包长度,用作下一次比较
}
}
#上面的循环是用作接收指令和解释封包,完成后会运行以下子程序“AI”
$ai_cmdQue_shift = 0;
#$ai_cmdQue是用作远程控制(聊天指令控制)
do {
AI(\%{$ai_cmdQue[$ai_cmdQue_shift]}) if ($conState == 5 && timeOut(\%{$timeout{'ai'}}) && $remote_socket && $remote_socket->connected());
#$conState 5代表已经完成登陆过程,并开始游戏;因为进入游戏后会收到大量信息,所以等待timeout.txt里AI指定的秒数后才执行AI;后面两个判断是代表连接正常
undef %{$ai_cmdQue[$ai_cmdQue_shift++]};
$ai_cmdQue-- if ($ai_cmdQue > 0);
#以上两句是用作执行远程控制
} while ($ai_cmdQue > 0);
checkConnection();
#管理数据通讯连接状况的子程序,例如:登陆和断线等功能
}
Kore第四讲: RO封包简要说明
Kore第四讲: RO封包简要说明
Kore第四讲: RO封包简要说明 by ICE-WR
RO客户端与服务器间通过TCP协议连接,并互相传递信息,封包内容类似
B0 00 05 00 E0 10 00 00
#00b0 w l
RO的封包头包含2个字节,数值从0064到01FF,上面的封包头为00B0,每种封包分别代表不同类型的信息。
RO封包有2种,一种是固定长度,一种是非固定长度,类型和各封包长度在ragexe.exe里记录,KARASU把RAGEXE.EXE破解后翻译出全部封包长度。
例如00B0是固定长度8字节,但如果一个封包并不是固定长度,那服务器需要告知RO客户端封包的长度,对非固定长度的封包,在封包的第三和第四个字节用来表示封包长度
把类似“B0 00 05 00 E0 10 00 00”这种16进制形式的代码转换为数值,在perl里通常用substr和unpack函数从封包里转换为具体数值(perl函数请参考Kore第二讲里的perl教学文件)
例如Kore的封包分析子程序里对B0 00 05 00 E0 10 00 00的解释执行如下:
} elsif ($switch eq "00B0" {
#$switch在Kore里代表封包头,当封包头为00B0时执行下面代码
$无效 = unpack("S1",substr($msg, 2, 2));
#这里代表截取封包里05 00并翻译为无符号短整数,赋值给$无效,这时$无效 = 5
$val = unpack("L1",substr($msg, 4, 4));
#截取E0 10 00 00并翻译为无符号长整数,赋值给$val,这时$val = 4320
if ($无效 == 0) {
print "Something1: $val\n" if $config{'debug'};
} elsif ($无效 == 3) {
print "Something2: $val\n" if $config{'debug'};
} elsif ($无效 == 5) {
#当$无效等于5时执行
$chars[$config{'char'}]{'hp'} = $val;
#把$val赋值给$chars[$config{'char'}]{'hp'}
print "Hp: $val\n" if ($config{'debug'} >= 2);
} ...
以上的封包分析后,这个封包的内容就是告知客户端当前人物HP = 4320
Kore第五讲: 封包分析子程序运作详解
Kore第五讲: 封包分析子程序运作详解
Kore第五讲: 封包分析子程序运作详解 by ICE-WR
从前面的介绍我们知道当收到RO服务器封包时,会执行子程序parseMsg()来进行封包分析,那parseMsg()是怎么运作的呢?
首先从Kore主程序结构里得知,Kore采用阻塞的方式进行封包接收,就是每隔一段时间收一次,这个循环时间大概是AI执行时间加上config.txt里sleepTime的微妙数。收回来的封包会放在$msg里,然后把$msg交给parseMsg()来进行分析,parseMsg()的执行过程如下:
1)取$msg前两个字节,解释为封包头
2)根据封包头来选择分析代码
3)把分析了的封包信息删除,并把还没分析的返回给主程序
主程序根据$msg在执行parseMsg()分析前和分析后的长度是否相等,来判断是否还需要进行分析,假如分析前与分析后长度一样,代表分析完毕,假如不等,则继续循环(请参照第三节内容)
目前封包信息已知头信息均为0064~01FF,另外还有一个种只包含ACCOUNT ID,以下为KE内的分析代码,采用MODKORE的计算方式
在主程序阶段执行了$msg = parseMsg($msg);后,运行以下代码
sub parseMsg {
my $msg = shift;
#my为定义局部变量,shift为传递第一个子程序参数,并删除参数,perl里的子程序参数都以数组形式传递,这里是把主程序里的$msg引用到子程序中
my $msg_size;
#定义局部变量$msg_size,看字面解释就很清楚:“封包长度”
if (length($msg) < 2) {
#length()函数是用来计算字符串长度,假如封包长度小于2,那就执行下面语句
return $msg;
#结束子程序,并返回$msg给主程序
}
#以上代码表示,最短可分析的封包长度是2,假如长度小于2,那就不分析
$switch = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1)));
#这里把封包前两位截取出来并赋值给$switch,$switch在子程序内代表封包头
if (length($msg) >= 4 && substr($msg,0,4) ne $accountID && $conState >= 4 && $lastswitch ne $switch && length($msg) >= unpack("S1", substr($msg, 0, 2))) {
decrypt(\$msg, $msg);
}
#这里是执行封包解密,有些服务器是封包加密形式的,对CRO没用
$switch = uc(unpack("H2", substr($msg, 1, 1))) . uc(unpack("H2", substr($msg, 0, 1)));
#封包解密后从新计算封包头,对CRO没用
if ($lastswitch eq $switch && length($msg) > $lastMsgLength) {
$errorCount++;
} else {
$errorCount = 0;
}
if ($errorCount > 3) {
dumpData($msg);
$errorCount = 0;
$msg_size = length($msg);
print "封包解释错误: $last_know_switch > $switch ($msg_size)\n";
}
#以上是效验封包完整性,假如一个封包经过三次接收都还没被分析,会被认为是封包错误,把已经接收到的封包全部去掉。造成封包错误的主要原因是封包长度错误,例如00B0长度8,假如我设置了00B0为长度6,经过一次封包解释后,删除了前面6个字节,得到后面的封包头为00 00,不在0064~01FF这个范围,那就不能进行分析,Kore必须丢弃这些这些封包才能确保封包分析能继续执行下去。
$lastswitch = $switch;
#计算前记录下上一次分析前的封包头(用作完整性判断)
if (substr($msg,0,4) ne $accountID || ($conState != 2 && $conState != 4)) {
#RO封包里有个特殊的封包只返回Account ID,假如不是这个封包则进行下面的封包长度计算,$rpackets{$switch}代表从rpackets.txt里读取出来的封包长度,例如$rpackets{'00B0'} = 8。$switch范围为0064~01FF,超出这个范围的封包头肯定代表封包接收有问题!
if ($rpackets{$switch} eq "-" {
#这种封包会把整个封包截取出来分析(实际上RO并没有这这种封包,只是在KARASU未解析出全部封包长度前,所遗留下来的问题- -b)
$msg_size = length($msg);
} elsif ($rpackets{$switch} eq "0" {
#判断是否非固定长度类封包,封包头为前2字节,封包长度保存在封包的第3-4字节里
if (length($msg) < 4) {
return $msg;
}
#由于这种封包前4位分别记录封包头和封包长度,所以小于4字节时代表封包还没接收完整,因此返回到主程序
$msg_size = unpack("S1", substr($msg, 2, 2));
#这里是从第3~4字节里计算出封包长度
if (length($msg) < $msg_size) {
return $msg;
}
#假如收到的$msg还没到这个长度,代表封包还没接收完整,返回到主程序
} elsif ($rpackets{$switch} > 1) {
#判断是否固定长度封包
if (length($msg) < $rpackets{$switch}) {
return $msg;
}
#假如接收到的封包小于该封包的长度,代表封包还没接收完整,返回主程序
$msg_size = $rpackets{$switch};
#计算封包长度
} else {
dumpData($last_know_msg.$msg);
#假如封包头不是在0064~01FF之间,代表封包接收或分析有问题,dumpDate会记录下目前已经收到的封包,并记录在dump.txt里
}
$last_know_msg = substr($msg, 0, $msg_size);
$last_know_switch = $switch;
#以上用作效验封包完整性
}
$lastMsgLength = length($msg);
#以上用作计算封包完整性
接着会是一大堆if ($switch eq "xxxx"... elsif ... elsif ... elsif,这些就是具体到每种封包的分析...
完成分析后,在parseMsg()的最后会从$msg删除已经分析过的封包,如下:
$msg = (length($msg) >= $msg_size) ? substr($msg, $msg_size, length($msg) - $msg_size) : "";
return $msg;
--- luckyboa
扑(16):多种Kore代码下载
2004年8月3日更新,以下为压缩包含:
Kore-0.92.81
作者:Kura
简介:全部Kore类外挂的祖先,停止开发时已经具备了大部分功能
X-Kore-0.58.02
作者:Kura
简介:RO内挂的祖先,几乎具备了Kore全部的功能,可以执行Kore的脚本
openkore-win32-1.2.0
作者:openKore Team
简介:openKore持续发展,第一个Kore与X-Kore相结合,并把Kore的程序标准化
modKoer-Lite
作者:未知
简介:国外非常出名的Kore,有着自己特殊的写作风格,代码参照早期的SKore和openKore
Kore-XP-1.0405.2004
作者:modKore的作者
简介:最好的图形化Kore,modKore的延续
zKore-clio-snapshot
作者:Karasu
简介:台湾Kore的技术支柱!与日本jKore有很大关联,KoreC大量参考zKore核心。
KoreEasy-0.7.99t
作者:ICE-WR
简介:以简单和挂机为主要开发理念,因为一直没作为开放代码而写,所以比较乱
KoreMVP-0.5.51
作者:ICE-WR
简介:第一个专门针对打MVP的加速内挂
--- luckyboa
猫(17):JAVA的KERO代码:
public void run (){
buf = ByteBuffer.allocate(RobotConst.MAX_LENGTH);
while ( true ) {
try {
if ( (socket != null && socket.isConnected() == true) && (msgLength = socket.read(buf)) != 0 ) {
System.out.println("A";
if (msgLength != 0){
getMessage = new byte [msgLength];
buf.flip();
buf.get(getMessage);
System.out.println(msgLength);
recvMessage = MsgDeal.filterByte(getMessage ,msgLength);
System.out.println("C";
if ( lastMessage != null ){
recvMessage = MsgDeal.byteArrayCat(lastMessage ,recvMessage);
System.out.println("D";
} //if end
while ( true ){
System.out.println(MsgDeal.getMsgHead( recvMessage , 0 , 2 ));
dataLength = MsgDeal.getMsgLength(MsgDeal.getMsgHead( recvMessage , 0 , 2 ));
System.out.println("dataLength:"+dataLength);
if ( dataLength == 0 ){
//translate the length of unfixed message
dataLength = Integer.parseInt(
MsgDeal.toHexMsg(MsgDeal.getInfoFromMsg( recvMessage , 2 , 2 )) , 16);
System.out.println("true dataLength:"+dataLength);
} else if( dataLength == -1 ){
recvMessage = null ;
break ;
}//if end
System.out.println(recvMessage.length);
//if date is not integrity ,then break
if ( dataLength > recvMessage.length ){
System.out.println("break";
lastMessage = recvMessage ;
break ;
} // if end
fullMessage = MsgDeal.getInfoFromMsg( recvMessage , 0 , dataLength );
System.out.println(MsgDeal.displayMsg(fullMessage));
ParseMsg.translateTcpData(fullMessage);
System.out.println("3";
//get the left meesage
recvMessage = MsgDeal.getInfoFromMsg( recvMessage , dataLength , recvMessage.length - dataLength);
System.out.println("4");
if ( recvMessage == null ){
lastMessage = null ;
System.out.println("5");
break ;
} // if end
lastMessage = recvMessage ;
System.out.println("6");
} //while end
} // if end
buf = ByteBuffer.allocate(RobotConst.MAX_LENGTH);
} // if end
} catch (ClosedChannelException e) {
System.out.println(e.toString());
lastMessage = null ;
e.printStackTrace();
} catch (IOException e) {
}
try{
if ( (socket != null && socket.isConnected() == true)
&& RoBot.srvInfo.getSendMsgList().isEmpty() != true){
if ( RoBot.srvInfo.getConState() == RobotConst.LoginSuccess ){
if(false){
sendMessage = MsgDeal.getTickCount();
}
}
sendMessage = (byte[])RoBot.srvInfo.getSendMsgList().get(0);
System.out.println(MsgDeal.displayMsg(sendMessage));
ByteBuffer bb = ByteBuffer.wrap(sendMessage);
socket.write(bb);
RoBot.srvInfo.getSendMsgList().remove(0);
}//if end
}catch (IOException e){
e.printStackTrace();
sendMessage = null;
}
try {
checkConnection();
} catch (IOException e1) {
e1.printStackTrace();
}
checkTimeOut();
try {
Thread.sleep(50);
} catch (InterruptedException e2) {
e2.printStackTrace();
}
} // while end
} //class end
/**
* server socket
*/
private SocketChannel socket ;
/**
* ip address
*/
private InetSocketAddress inetSocketAddress ;
/**
* buffered the byteArray
*/
private ByteBuffer buf ;
/**
* the message what will send to server
*/
private byte[] sendMessage ;
/**
* the meesage get from the socket in a tcp package whick has be filtered
*/
private byte[] recvMessage ;
/**
* the message which has not parsed is set in it
*/
private byte[] lastMessage ;
/**
* the package is all data in it
*/
private byte[] fullMessage ;
/**
* the meesage get from the socket in a tcp package
*/
private byte[] getMessage ;
/**
* the length of a tcp package
*/
private int msgLength ;
/**
* a data package's true length
*/
private int dataLength ;
我在最近编译一些perl的脚本时候发现,perlapp的确很少有人说的比较详细的使用方法
基本最多就是perlapp一些参数介绍没有太多实际用途,百度,google都没有找到太多有价值的,只能自己琢磨了。
现在稍微悟到了一些,希望能和大家共享
首先来谈一个问题,perlapp 编译的那个脚本,对于模块,如果是标准模块,自然不需要太多顾虑,但是如果有些自己定义的模块,那就首先需要在自己的脚本上加
unshift @INC,"e:\somedir";
然后大胆的使用
如果你不放心自己的@INC
可以打印出来看看print join("\n",@INC);
use xxx;(尽可能多的把你自己定义的模块全部use进来)
require "xxx.pl";
代码若干。。。。。。。
然后开始编译
perlapp --version start.pl --lib=e:\somedir\src --bind=e:\somedir\xxx.dll --bind=e:\somedir\dir2\xxx.pl 可以绑定很多的,
然后就是有慢慢等待成果了,hoho!