web版RDP录像播放,可以播放鼠标移动的录像了,下一步是显示rdp的图像。

pull/105/head
Apex Liu 2017-12-23 03:46:08 +08:00
parent 9305953218
commit 39c8fd32d5
8 changed files with 572 additions and 41 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

View File

@ -161,10 +161,9 @@ $app.on_table_host_cell_created = function (tbl, row_id, col_key, cell_obj) {
var action = $(this).attr('data-action');
if (action === 'replay') {
//$app.dlg_edit_host.show_edit(row_id);
if (row_data.protocol_type === TP_PROTOCOL_TYPE_RDP) {
// $tp.notify_error('sorry, not impl.');
$app.do_replay_rdp(row_data.id, row_data.user_username, row_data.acc_username, row_data.host_ip, row_data.time_begin);
// $app.do_replay_rdp(row_data.id, row_data.user_username, row_data.acc_username, row_data.host_ip, row_data.time_begin);
window.open('/audit/replay/' + row_data.protocol_type + '/' + row_data.id);
} else if (row_data.protocol_type === TP_PROTOCOL_TYPE_SSH) {
window.open('/audit/replay/' + row_data.protocol_type + '/' + row_data.id);
}

File diff suppressed because one or more lines are too long

View File

@ -1,29 +1,5 @@
"use strict";
$app.req_record_data = function (record_id, offset) {
$tp.ajax_post_json('/audit/get-record-data', {id: record_id, offset: offset},
function (ret) {
if (ret.code === TPE_OK) {
// console.log('data', ret.data);
$app.record_data = $app.record_data.concat(ret.data.data_list);
$app.record_data_offset += ret.data.data_size;
if ($app.record_data.length < $app.record_hdr.pkg_count) {
$app.req_record_data(record_id, $app.record_data_offset);
}
} else {
$app.dom.status.text("读取录像数据失败:" + tp_error_msg(ret.code));
$tp.notify_error('读取录像数据失败:' + tp_error_msg(ret.code, ret.message));
console.log('req_record_info error ', ret.code);
}
},
function () {
console.log('req_record_info error');
},
30 * 1000
);
};
$app.on_init = function (cb_stack) {
var record_id = $app.options.record_id;
@ -70,7 +46,7 @@ $app.on_init = function (cb_stack) {
Terminal.cursorBlink = false;
$tp.ajax_post_json('/audit/get-record-header', {id: record_id},
$tp.ajax_post_json('/audit/get-record-header', {protocol: TP_PROTOCOL_TYPE_SSH, id: record_id},
function (ret) {
if (ret.code === TPE_OK) {
$app.record_hdr = ret.data;
@ -184,6 +160,30 @@ $app.on_init = function (cb_stack) {
cb_stack.exec();
};
$app.req_record_data = function (record_id, offset) {
$tp.ajax_post_json('/audit/get-record-data', {protocol: TP_PROTOCOL_TYPE_SSH, id: record_id, offset: offset},
function (ret) {
if (ret.code === TPE_OK) {
// console.log('data', ret.data);
$app.record_data = $app.record_data.concat(ret.data.data_list);
$app.record_data_offset += ret.data.data_size;
if ($app.record_data.length < $app.record_hdr.pkg_count) {
$app.req_record_data(record_id, $app.record_data_offset);
}
} else {
$app.dom.status.text("读取录像数据失败:" + tp_error_msg(ret.code));
$tp.notify_error('读取录像数据失败:' + tp_error_msg(ret.code, ret.message));
console.log('req_record_info error ', ret.code);
}
},
function () {
console.log('req_record_info error');
},
30 * 1000
);
};
$app.init_and_play = function() {
if (_.isNull($app.player_console_term)) {
$app.player_console_term = new Terminal({

View File

@ -0,0 +1,80 @@
<%!
page_title_ = '录像回放'
%>
<%inherit file="../page_single_base.mako"/>
<%block name="extend_js_file">
## <script type="text/javascript" src="${ static_url('plugins/xterm/xterm.js') }"></script>
<script type="text/javascript" src="${ static_url('js/audit/replay-rdp.js') }"></script>
</%block>
<%block name="extend_css_file">
## <link href="${ static_url('plugins/xterm/xterm.css') }" rel="stylesheet" type="text/css"/>
</%block>
<%block name="embed_css">
<style type="text/css">
#screen {
margin-top: 5px;
##background-color: #547cff;
background-color: #000;
border: 1px solid #525252;
border-right-color: #fff;
border-bottom-color: #fff;
## padding: 1px;
}
</style>
</%block>
<%block name="page_header">
<div class="container-fluid top-navbar">
<div class="breadcrumb-container">
<ol class="breadcrumb">
<li><i class="fa fa-server"></i> ${self.attr.page_title_}</li>
<li class="sub-title"><span id="recorder-info"></span></li>
</ol>
</div>
</div>
</%block>
<div class="page-content">
<div id="toolbar" style="display: inline-block;">
<button id="btn-play" type="button" class="btn btn-primary btn-sm" style="width:80px;"><i class="fa fa-pause fa-fw"> 暂停</i></button>
<button id="btn-restart" type="button" class="btn btn-success btn-sm"><i class="fa fa-refresh fa-fw"></i> 重新播放</button>
<button id="btn-speed" type="button" class="btn btn-info btn-sm" style="width:80px;">正常速度</button>
<button id="btn-big-font" type="button" class="btn btn-default btn-sm"><i class="fa fa-font fa-fw"></i>+</button>
<button id="btn-small-font" type="button" class="btn btn-default btn-sm"><i class="fa fa-font fa-fw"></i>-</button>
<div style="display:inline-block;position:relative;top:4px;margin-left:10px;margin-right:15px;">
<span id="btn-skip" style="cursor:pointer;"><i class="fa fa-check-square-o fa-fw"></i> 跳过无操作时间</span>
</div>
<span id="play-status" class="badge badge-normal" style="margin-left:5px;">正在获取数据</span>
<span id="play-time" class="badge badge-success" style="margin-left:5px;">总时长:未知</span>
</div>
<input id="progress" type="range" value="0" min=0 max=100 style="margin-top: 10px;"/>
## <div id="xterm-box"></div>
<div id="screen"></div>
</div>
<%block name="extend_content">
</%block>
<%block name="embed_js">
<script type="text/javascript">
$app.add_options(${page_param});
</script>
</%block>

View File

@ -494,7 +494,8 @@ class ReplayHandler(TPBaseHandler):
def get(self, protocol, record_id):
protocol = int(protocol)
if protocol == TP_PROTOCOL_TYPE_RDP:
return
param = {'record_id': record_id}
self.render('audit/replay-rdp.mako', page_param=json.dumps(param))
elif protocol == TP_PROTOCOL_TYPE_SSH:
param = {'record_id': record_id}
self.render('audit/replay-ssh.mako', page_param=json.dumps(param))
@ -603,12 +604,13 @@ class DoGetRecordHeaderHandler(TPBaseJsonHandler):
return self.write_json(TPE_JSON_FORMAT)
try:
protocol_type = int(args['protocol'])
record_id = int(args['id'])
except:
log.e('\n')
return self.write_json(TPE_PARAM)
header, err = record.read_record_head(record_id)
header, err = record.read_record_head(protocol_type, record_id)
if header is None:
return self.write_json(err)
@ -627,13 +629,19 @@ class DoGetRecordDataHandler(TPBaseJsonHandler):
return self.write_json(TPE_JSON_FORMAT)
try:
protocol_type = int(args['protocol'])
record_id = int(args['id'])
offset = int(args['offset'])
except:
log.e('\n')
return self.write_json(TPE_PARAM)
data_list, data_size, err = record.read_record_data(record_id, offset)
if protocol_type == TP_PROTOCOL_TYPE_RDP:
data_list, data_size, err = record.read_rdp_record_data(record_id, offset)
elif protocol_type == TP_PROTOCOL_TYPE_RDP:
data_list, data_size, err = record.read_ssh_record_data(record_id, offset)
else:
self.write_json(TPE_NOT_EXISTS)
self.write_json(err, data={'data_list': data_list, 'data_size': data_size})

View File

@ -114,12 +114,26 @@ def get_records(handler, sql_filter, sql_order, sql_limit, sql_restrict, sql_exc
return err, s.total_count, s.recorder
def read_record_head(record_id):
def _remove_padding_space(s):
r = []
for i in range(len(s)):
if s[i] == 0x00:
break
r.append(s[i])
return bytearray(r)
def read_record_head(protocol_type, record_id):
if not tp_cfg().core.detected:
return None, TPE_NO_CORE_SERVER
record_path = os.path.join(tp_cfg().core.replay_path, 'ssh', '{:09d}'.format(int(record_id)))
header_file_path = os.path.join(record_path, 'tp-ssh.tpr')
if protocol_type == TP_PROTOCOL_TYPE_RDP:
path_name = 'rdp'
elif protocol_type == TP_PROTOCOL_TYPE_SSH:
path_name = 'ssh'
record_path = os.path.join(tp_cfg().core.replay_path, path_name, '{:09d}'.format(int(record_id)))
header_file_path = os.path.join(record_path, 'tp-{}.tpr'.format(path_name))
if not os.path.exists(header_file_path):
return None, TPE_NOT_EXISTS
@ -156,22 +170,22 @@ def read_record_head(record_id):
# offset += 4
user_name, = struct.unpack_from('64s', data, offset)
user_name = user_name.decode()
user_name = _remove_padding_space(user_name).decode()
offset += 64
account, = struct.unpack_from('64s', data, offset)
account = account.decode()
account = _remove_padding_space(account).decode()
offset += 64
host_ip, = struct.unpack_from('40s', data, offset)
host_ip = host_ip.decode()
host_ip = _remove_padding_space(host_ip).decode()
offset += 40
conn_ip, = struct.unpack_from('40s', data, offset)
conn_ip = conn_ip.decode()
conn_ip = _remove_padding_space(conn_ip).decode()
offset += 40
conn_port, = struct.unpack_from('H', data, offset)
offset += 2
client_ip, = struct.unpack_from('40s', data, offset)
client_ip = client_ip.decode()
client_ip = _remove_padding_space(client_ip).decode()
offset += 40
except Exception as e:
@ -197,11 +211,82 @@ def read_record_head(record_id):
return header, TPE_OK
def read_record_data(record_id, offset):
def read_rdp_record_data(record_id, offset):
if not tp_cfg().core.detected:
return None, TPE_NO_CORE_SERVER
record_path = os.path.join(tp_cfg().core.replay_path, 'rdp', '{:09d}'.format(int(record_id)))
file_data = os.path.join(record_path, 'tp-rdp.dat')
if not os.path.exists(file_data):
return None, 0, TPE_NOT_EXISTS
data_list = list()
data_size = 0
file = None
try:
file_size = os.path.getsize(file_data)
if offset >= file_size:
return None, 0, TPE_FAILED
file = open(file_data, 'rb')
if offset > 0:
file.seek(offset, io.SEEK_SET)
# read 1000 packages one time from offset.
for i in range(1000):
"""
// 一个数据包的头
typedef struct TS_RECORD_PKG
{
ex_u8 type; // 包的数据类型
ex_u32 size; // 这个包的总大小不含包头
ex_u32 time_ms; // 这个包距起始时间的时间差毫秒意味着一个连接不能持续超过49天
ex_u8 _reserve[3]; // 保留
}TS_RECORD_PKG;
"""
_data = file.read(12)
data_size += 12
_action, _size, _time, = struct.unpack_from('=BII', _data)
if offset + data_size + _size > file_size:
return None, 0, TPE_FAILED
_data = file.read(_size)
data_size += _size
temp = dict()
temp['a'] = _action
temp['t'] = _time
if _action == 0x10:
# this is mouse movement event.
x, y = struct.unpack_from('HH', _data)
temp['x'] = x
temp['y'] = y
elif _action == 0x11:
# this is a data package.
_data = base64.b64encode(_data)
temp['d'] = _data.decode()
else:
return None, 0, TPE_FAILED
data_list.append(temp)
if offset + data_size == file_size:
break
except Exception:
log.e('failed to read record file: {}\n'.format(file_data))
return None, 0, TPE_FAILED
finally:
if file is not None:
file.close()
return data_list, data_size, TPE_OK
def read_ssh_record_data(record_id, offset):
if not tp_cfg().core.detected:
return None, TPE_NO_CORE_SERVER
# read 1000 packages one time from offset.
record_path = os.path.join(tp_cfg().core.replay_path, 'ssh', '{:09d}'.format(int(record_id)))
file_data = os.path.join(record_path, 'tp-ssh.dat')
@ -220,6 +305,7 @@ def read_record_data(record_id, offset):
if offset > 0:
file.seek(offset, io.SEEK_SET)
# read 1000 packages one time from offset.
for i in range(1000):
"""
// 一个数据包的头