Skip to content
Snippets Groups Projects
Commit f9e65fc9 authored by echicken's avatar echicken :chicken:
Browse files

De-obfuscate crappy RLogin client. Meh.

parent 8ae955d7
Branches
Tags
No related merge requests found
......@@ -17,10 +17,342 @@ function getSession(un) {
return session;
}
// Obfuscated lazy port of an unfinished rlogin client I made quite some time
// ago. It does what it needs to do. Unless it doesn't - in which case,
// replace it with something better.
var RLogin=function(e){var n=this;const t=24,i=13,s=17,o=19,c=10;var r=[],p=[],u={connected:!1,cooked:!0,suspendInput:!1,suspendOutput:!1,watchForClientEscape:!0,clientHasEscaped:!1},d={rows:24,columns:80,pixelsX:640,pixelsY:480,clientEscape:"~"},f={DOT:n.disconnect,EOT:n.disconnect,SUB:function(){u.suspendInput=u.suspendInput?!1:!0,u.suspendOutput=u.suspendInput},EOM:function(){u.suspendInput=u.suspendInput?!1:!0,u.suspendOutput=!1}};this.__defineGetter__("connected",function(){return u.connected}),this.__defineSetter__("connected",function(e){"boolean"!=typeof e||e||n.disconnect()}),this.__defineGetter__("rows",function(){return d.rows}),this.__defineSetter__("rows",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'rows' setting "+e;d.rows=e}),this.__defineGetter__("columns",function(){return d.columns}),this.__defineSetter__("columns",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'columns' setting "+e;d.columns=e}),this.__defineGetter__("pixelsX",function(){return d.pixelsX}),this.__defineSetter__("pixelsX",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'pixelsX' setting "+e;d.pixelsX=e}),this.__defineGetter__("pixelsY",function(){return d.pixelsY}),this.__defineSetter__("pixelsY",function(e){if(!("number"==typeof e&&e>0))throw"RLogin: Invalid 'pixelsY' setting "+e;d.pixelsY=e}),this.__defineGetter__("clientEscape",function(){return d.clientEscape}),this.__defineSetter__("clientEscape",function(e){if("string"!=typeof e||1!=e.length)throw"RLogin: Invalid 'clientEscape' setting "+e;d.clientEscape=e});var a=new Socket,l=function(){if(!(a.nread<1)){for(var e=[];a.nread>0;)e.push(a.recvBin(1));if(!u.connected)if(0==e[0]){if(u.connected=!0,!(e.length>1))return;e=e.slice(1)}else n.disconnect();u.suspendOutput||(r=r.concat(e))}};this.send=function(e){if(!u.connected)throw"RLogin.send: not connected.";if(u.suspendInput)throw"RLogin.send: input has been suspended.";"string"==typeof e&&(e=e.split("").map(function(e){return ascii(e)}));for(var n=[],r=0;r<e.length;r++)u.watchForClientEscape&&e[r]==d.clientEscape.charCodeAt(0)?(u.watchForClientEscape=!1,u.clientHasEscaped=!0):u.clientHasEscaped?(u.clientHasEscaped=!1,"undefined"!=typeof f[e[r]]&&f[e[r]]()):!u.cooked||e[r]!=s&&e[r]!=o?((r>0&&e[r-1]==i&&e[r]==c||e[r]==t)&&(u.watchForClientEscape=!0),n.push(e[r])):u.suspendOutput==(e[r]==o);p=p.concat(n)},this.receive=function(){return r.splice(0,r.length)},this.addClientEscape=function(e,n){if("string"!=typeof e&&"number"!=typeof e||"string"==typeof e&&e.length>1||"function"!=typeof n)throw"RLogin.addClientEscape: invalid arguments.";f[e.charCodeAt(0)]=n},this.connect=function(){if("number"!=typeof e.port||"string"!=typeof e.host)throw"RLogin: invalid host or port argument.";if("string"!=typeof e.clientUsername)throw"RLogin: invalid clientUsername argument.";if("string"!=typeof e.serverUsername)throw"RLogin: invalid serverUsername argument.";if("string"!=typeof e.terminalType)throw"RLogin: invalid terminalType argument.";if("number"!=typeof e.terminalSpeed)throw"RLogin: invalid terminalSpeed argument.";if(!a.connect(e.host,e.port))throw"RLogin: Unable to connect to server.";for(a.sendBin(0,1),a.send(e.clientUsername),a.sendBin(0,1),a.send(e.serverUsername),a.sendBin(0,1),a.send(e.terminalType+"/"+e.terminalSpeed),a.sendBin(0,1);a.is_connected&&a.nread<1;)mswait(5);l()},this.cycle=function(){if(l(),!(u.suspendInput||p.length<1))for(;p.length>0;)a.sendBin(p.shift(),1)},this.disconnect=function(){a.close(),u.connected=!1}};
var RLoginClient = function(options) {
var self = this;
const CAN = 0x18,
CR = 0x0D,
DC1 = 0x11,
DC3 = 0x13,
DOT = 0x2E,
EOM = 0x19,
EOT = 0x04,
LF = 0x0A,
SUB = 0x1A,
DISCARD = 0x02,
RAW = 0x10,
COOKED = 0x20,
WINDOW = 0x80;
var serverBuffer = []; // From server
var clientBuffer = []; // From client
var state = {
connected : false,
cooked : true,
suspendInput : false,
suspendOutput : false,
watchForClientEscape : true,
clientHasEscaped : false
};
var properties = {
rows : 24,
columns : 80,
pixelsX : 640,
pixelsY : 480,
clientEscape : '~'
};
// As suggested by RFC1282
var clientEscapes = {
DOT : self.disconnect,
EOT : self.disconnect,
SUB : function() {
state.suspendInput = (state.suspendInput) ? false : true;
state.suspendOutput = state.suspendInput;
},
EOM : function() {
state.suspendInput = (state.suspendInput) ? false : true;
state.suspendOutput = false;
}
};
this.__defineGetter__('connected', function () { return state.connected; });
this.__defineSetter__(
'connected',
function (value) {
if (typeof value === 'boolean' && !value) self.disconnect();
}
);
this.__defineGetter__('rows', function () { return properties.rows; });
this.__defineSetter__(
'rows',
function(value) {
if (typeof value === 'number' && value > 0) {
properties.rows = value;
} else {
throw 'RLogin: Invalid \'rows\' setting ' + value;
}
}
);
this.__defineGetter__(
'columns',
function () { return properties.columns; }
);
this.__defineSetter__(
'columns',
function (value) {
if (typeof value === 'number' && value > 0) {
properties.columns = value;
} else {
throw 'RLogin: Invalid \'columns\' setting ' + value;
}
}
);
this.__defineGetter__(
'pixelsX',
function () { return properties.pixelsX; }
);
this.__defineSetter__(
'pixelsX',
function (value) {
if (typeof value === 'number' && value > 0) {
properties.pixelsX = value;
} else {
throw 'RLogin: Invalid \'pixelsX\' setting ' + value;
}
}
);
this.__defineGetter__(
'pixelsY',
function () { return properties.pixelsY; }
);
this.__defineSetter__(
'pixelsY',
function (value) {
if (typeof value === 'number' && value > 0) {
properties.pixelsY = value;
} else {
throw 'RLogin: Invalid \'pixelsY\' setting ' + value;
}
}
);
this.__defineGetter__(
'clientEscape',
function() { return properties.clientEscape; }
);
this.__defineSetter__(
'clientEscape',
function (value) {
if (typeof value === 'string' && value.length === 1) {
properties.clientEscape = value;
} else {
throw 'RLogin: Invalid \'clientEscape\' setting ' + value;
}
}
);
var handle = new Socket();
function getServerData() {
if (handle.nread < 1) return;
var data = [];
while (handle.nread > 0) {
data.push(handle.recvBin(1));
}
if (!state.connected) {
if (data[0] === 0) {
state.connected = true;
if (data.length > 1) {
data = data.slice(1);
} else {
return;
}
} else {
self.disconnect();
}
}
// If I could tell if the TCP urgent-data pointer had been set,
// I would uncomment (and complete) this block. We'll settle
// for a partial implementation for the time being.
// We would need something to tell us if urgent data was sent,
// eg. var lookingForControlCode = urgentDataPointerIsSet();
/*
var temp = [];
for (var d = 0; d < data.length; d++) {
if (!lookingForControlCode) {
temp.push(data[d]);
continue;
}
switch (data[d]) {
case DISCARD:
temp = [];
// We found our control code
lookingForControlCode = false;
break;
case RAW:
state.cooked = false;
lookingForControlCode = false;
break;
case COOKED:
state.cooked = true;
lookingForControlCode = false;
break;
case WINDOW:
self.sendWCCS();
lookingForControlCode = false;
break;
default:
temp.push(data[d]);
break;
}
}
if (!state.suspendOutput) self.emit('data', new Buffer(temp));
*/
if (!state.suspendOutput) serverBuffer = serverBuffer.concat(data);
}
// Send a Window Change Control Sequence
// this.sendWCCS = function() {
// var magicCookie = [0xFF, 0xFF, 0x73, 0x73];
// var rcxy = new Buffer(8);
// rcxy.writeUInt16LE(properties.rows, 0);
// rcxy.writeUInt16LE(properties.columns, 2);
// rcxy.writeUInt16LE(properties.pixelsX, 4);
// rcxy.writeUInt16LE(properties.pixelsY, 6);
// if(state.connected)
// handle.write(Buffer.concat([magicCookie, rcxy]));
// }
// Send 'data' (String or Buffer) to the rlogin server
this.send = function (data) {
if (!state.connected) throw 'RLogin.send: not connected.';
if (state.suspendInput) throw 'RLogin.send: input has been suspended.';
if (typeof data === 'string') {
data = data.split('').map(function (d) { return ascii(d); });
}
var temp = [];
for (var d = 0; d < data.length; d++) {
if (state.watchForClientEscape &&
data[d] == properties.clientEscape.charCodeAt(0)
) {
state.watchForClientEscape = false;
state.clientHasEscaped = true;
continue;
}
if (state.clientHasEscaped) {
state.clientHasEscaped = false;
if (typeof clientEscapes[data[d]] !== 'undefined') {
clientEscapes[data[d]]();
}
continue;
}
if (state.cooked && (data[d] === DC1 || data[d] === DC3)) {
state.suspendOutput = (data[d] === DC3);
continue;
}
if ((d > 0 && data[d - 1] === CR && data[d] === LF) ||
data[d] == CAN
) {
state.watchForClientEscape = true;
}
temp.push(data[d]);
}
clientBuffer = clientBuffer.concat(temp);
}
this.receive = function () {
return serverBuffer.splice(0, serverBuffer.length);
}
/* If 'ch' is found in client input immediately after the
'this.clientEscape' character when:
- this is the first input after connection establishment or
- these are the first characters on a new line or
- these are the first characters after a line-cancel character
then the function 'callback' will be called. Use this to allow
client input to trigger a particular action. */
this.addClientEscape = function (ch, callback) {
if( (typeof ch !== 'string' && typeof ch !== 'number') ||
(typeof ch === 'string' && ch.length > 1) ||
typeof callback !== 'function'
) {
throw 'RLogin.addClientEscape: invalid arguments.';
}
clientEscapes[ch.charCodeAt(0)] = callback;
}
this.connect = function () {
if (typeof options.port !== 'number' ||
typeof options.host != 'string'
) {
throw 'RLogin: invalid host or port argument.';
}
if (typeof options.clientUsername !== 'string') {
throw 'RLogin: invalid clientUsername argument.';
}
if (typeof options.serverUsername !== 'string') {
throw 'RLogin: invalid serverUsername argument.';
}
if (typeof options.terminalType !== 'string') {
throw 'RLogin: invalid terminalType argument.';
}
if (typeof options.terminalSpeed !== 'number') {
throw 'RLogin: invalid terminalSpeed argument.';
}
if (handle.connect(options.host, options.port)) {
handle.sendBin(0, 1);
handle.send(options.clientUsername);
handle.sendBin(0, 1);
handle.send(options.serverUsername);
handle.sendBin(0, 1);
handle.send(options.terminalType + '/' + options.terminalSpeed);
handle.sendBin(0, 1);
while (handle.is_connected && handle.nread < 1) {
mswait(5);
}
getServerData();
} else {
throw 'RLogin: Unable to connect to server.';
}
}
this.cycle = function () {
getServerData();
if (state.suspendInput || clientBuffer.length < 1) return;
while (clientBuffer.length > 0) {
handle.sendBin(clientBuffer.shift(), 1);
}
}
this.disconnect = function () {
handle.close();
state.connected = false;
}
}
try {
......@@ -65,7 +397,7 @@ try {
var ini = f.iniGetObject('BBS');
f.close();
rlogin = new RLogin(
rlogin = new RLoginClient(
{ host : system.inet_addr,
port : ini.RLoginPort,
clientUsername : usr.security.password,
......@@ -90,11 +422,13 @@ try {
rlogin.send(data);
}
mswait(5);
}
} catch (err) {
log(err);
log(LOG_ERR, err);
} finally {
rlogin.disconnect();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment