33import burp .*;
44
55import com .sqlmapwebui .burp .dialogs .*;
6+ import com .sqlmapwebui .burp .util .CommandExecutor ;
7+ import com .sqlmapwebui .burp .util .SqlCommandBuilder ;
68
79import javax .swing .*;
810import java .awt .*;
@@ -145,11 +147,6 @@ public Component getUiComponent() {
145147 public List <JMenuItem > createMenuItems (IContextMenuInvocation invocation ) {
146148 List <JMenuItem > menuItems = new ArrayList <>();
147149
148- // 只有已连接状态才显示菜单
149- if (!configManager .isConnected ()) {
150- return menuItems ;
151- }
152-
153150 byte invocationContext = invocation .getInvocationContext ();
154151 if (invocationContext == IContextMenuInvocation .CONTEXT_MESSAGE_EDITOR_REQUEST ||
155152 invocationContext == IContextMenuInvocation .CONTEXT_MESSAGE_VIEWER_REQUEST ||
@@ -166,9 +163,15 @@ public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {
166163 FilterResult filterResult = filterBinaryRequests (selectedMessages );
167164 String menuSuffix = filterResult .getMenuSuffix ();
168165
166+ // ==================== 需要后端连接的菜单项(未连接时置灰) ====================
167+ boolean connected = configManager .isConnected ();
168+
169169 // 使用用户选择的配置发送
170170 JMenuItem sendWithDefault = new JMenuItem ("Send to SQLMap WebUI" + menuSuffix );
171- if (filterResult .allBinary ()) {
171+ if (!connected ) {
172+ sendWithDefault .setEnabled (false );
173+ sendWithDefault .setToolTipText ("未连接到SQLMap WebUI后端,请检查连接配置" );
174+ } else if (filterResult .allBinary ()) {
172175 sendWithDefault .setEnabled (false );
173176 sendWithDefault .setToolTipText ("所有选中的报文都是二进制格式,无法发起扫描任务" );
174177 } else {
@@ -182,7 +185,10 @@ public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {
182185 // 标记注入点并扫描 - 支持多选报文
183186 int maxMarkCount = configManager .getMaxInjectionMarkCount ();
184187 JMenuItem markInjectionPoints = new JMenuItem ("标记注入点并扫描 (*)" + menuSuffix );
185- if (filterResult .allBinary ()) {
188+ if (!connected ) {
189+ markInjectionPoints .setEnabled (false );
190+ markInjectionPoints .setToolTipText ("未连接到SQLMap WebUI后端,请检查连接配置" );
191+ } else if (filterResult .allBinary ()) {
186192 markInjectionPoints .setEnabled (false );
187193 markInjectionPoints .setToolTipText ("所有选中的报文都是二进制格式,无法发起扫描任务" );
188194 } else {
@@ -204,7 +210,10 @@ public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {
204210
205211 // 配置扫描发送(高级配置对话框)
206212 JMenuItem sendWithOptions = new JMenuItem ("Send to SQLMap WebUI (配置扫描)..." + menuSuffix );
207- if (filterResult .allBinary ()) {
213+ if (!connected ) {
214+ sendWithOptions .setEnabled (false );
215+ sendWithOptions .setToolTipText ("未连接到SQLMap WebUI后端,请检查连接配置" );
216+ } else if (filterResult .allBinary ()) {
208217 sendWithOptions .setEnabled (false );
209218 sendWithOptions .setToolTipText ("所有选中的报文都是二进制格式,无法发起扫描任务" );
210219 } else {
@@ -219,22 +228,62 @@ public List<JMenuItem> createMenuItems(IContextMenuInvocation invocation) {
219228 }
220229 menuItems .add (sendWithOptions );
221230
222- // 提交会话Header 和 Header规则 - 仅在选中单条请求时显示
231+ // 提交会话Header 和 Header规则 - 仅在选中单条请求时显示,且需要后端连接
223232 if (selectedMessages .length == 1 && filterResult .hasTextMessages ()) {
224233 JMenuItem submitSessionHeaders = new JMenuItem ("提交会话Header" );
225- submitSessionHeaders .addActionListener (e -> {
226- SessionHeaderDialog dialog = new SessionHeaderDialog (callbacks , apiClient , uiTab );
227- dialog .show (filterResult .textMessages .get (0 ));
228- });
234+ if (!connected ) {
235+ submitSessionHeaders .setEnabled (false );
236+ submitSessionHeaders .setToolTipText ("未连接到SQLMap WebUI后端,请检查连接配置" );
237+ } else {
238+ submitSessionHeaders .addActionListener (e -> {
239+ SessionHeaderDialog dialog = new SessionHeaderDialog (callbacks , apiClient , uiTab );
240+ dialog .show (filterResult .textMessages .get (0 ));
241+ });
242+ }
229243 menuItems .add (submitSessionHeaders );
230244
231245 JMenuItem submitHeaderRule = new JMenuItem ("提交Header规则" );
232- submitHeaderRule .addActionListener (e -> {
233- HeaderRuleDialog dialog = new HeaderRuleDialog (callbacks , apiClient , uiTab );
234- dialog .show (filterResult .textMessages .get (0 ));
235- });
246+ if (!connected ) {
247+ submitHeaderRule .setEnabled (false );
248+ submitHeaderRule .setToolTipText ("未连接到SQLMap WebUI后端,请检查连接配置" );
249+ } else {
250+ submitHeaderRule .addActionListener (e -> {
251+ HeaderRuleDialog dialog = new HeaderRuleDialog (callbacks , apiClient , uiTab );
252+ dialog .show (filterResult .textMessages .get (0 ));
253+ });
254+ }
236255 menuItems .add (submitHeaderRule );
237256 }
257+
258+ // ==================== 不依赖后端的菜单项(始终可用) ====================
259+
260+ // 复制SQLMap命令
261+ JMenuItem copySqlCommand = new JMenuItem ("复制SQLMap命令" + menuSuffix );
262+ if (filterResult .allBinary ()) {
263+ copySqlCommand .setEnabled (false );
264+ copySqlCommand .setToolTipText ("所有选中的报文都是二进制格式,无法生成SQLMap命令" );
265+ } else {
266+ copySqlCommand .addActionListener (e -> {
267+ if (filterResult .hasTextMessages ()) {
268+ handleCopySqlCommand (filterResult .textMessages .get (0 ));
269+ }
270+ });
271+ }
272+ menuItems .add (copySqlCommand );
273+
274+ // 执行SQLMap扫描
275+ JMenuItem executeSqlMap = new JMenuItem ("执行SQLMap扫描" + menuSuffix );
276+ if (filterResult .allBinary ()) {
277+ executeSqlMap .setEnabled (false );
278+ executeSqlMap .setToolTipText ("所有选中的报文都是二进制格式,无法执行SQLMap扫描" );
279+ } else {
280+ executeSqlMap .addActionListener (e -> {
281+ if (filterResult .hasTextMessages ()) {
282+ handleExecuteSqlMap (filterResult .textMessages .get (0 ));
283+ }
284+ });
285+ }
286+ menuItems .add (executeSqlMap );
238287 }
239288
240289 return menuItems ;
@@ -398,4 +447,182 @@ private void sendRequestToBackend(IHttpRequestResponse requestResponse, ScanConf
398447 stderr .println ("[-] Error: " + e .getMessage ());
399448 }
400449 }
450+
451+ // ==================== 新增:复制SQLMap命令和执行SQLMap扫描 ====================
452+
453+ /**
454+ * 处理"复制SQLMap命令"菜单点击
455+ */
456+ private void handleCopySqlCommand (IHttpRequestResponse message ) {
457+ try {
458+ // 检查SQLMap路径是否配置
459+ String sqlmapPath = configManager .getDirectSqlmapPath ();
460+ if (sqlmapPath == null || sqlmapPath .trim ().isEmpty ()) {
461+ if (CommandPreviewDialog .showConfigWarning (uiTab , "SQLMap路径(请在\" 直接执行配置\" 选项卡中配置)" )) {
462+ uiTab .switchToDirectExecuteTab ();
463+ }
464+ return ;
465+ }
466+
467+ // 生成HTTP请求字符串
468+ String httpRequest = buildHttpRequest (message );
469+
470+ // 生成临时文件
471+ String requestFilePath = SqlCommandBuilder .generateRequestFile (
472+ httpRequest ,
473+ configManager .getClipboardTempDir ()
474+ );
475+
476+ // 构建SQLMap命令
477+ String command = SqlCommandBuilder .buildCopyableCommand (
478+ configManager .getDirectPythonPath (),
479+ sqlmapPath ,
480+ requestFilePath ,
481+ buildAdditionalParams (configManager .getSelectedScanConfig ())
482+ );
483+
484+ // 根据配置决定是直接复制还是显示预览
485+ if (configManager .isClipboardAutoCopy ()) {
486+ // 直接复制到剪贴板
487+ CommandExecutor .copyToClipboard (command );
488+ uiTab .appendLog ("[+] SQLMap命令已复制到剪贴板" );
489+ uiTab .appendLog (" 请求文件: " + requestFilePath );
490+ JOptionPane .showMessageDialog (uiTab ,
491+ "SQLMap命令已复制到剪贴板!\n \n " +
492+ "请求文件: " + requestFilePath ,
493+ "复制成功" , JOptionPane .INFORMATION_MESSAGE );
494+ } else {
495+ // 显示预览对话框
496+ CommandPreviewDialog dialog = new CommandPreviewDialog (configManager );
497+ dialog .showCopyDialog (uiTab , command , requestFilePath );
498+ }
499+
500+ } catch (Exception e ) {
501+ uiTab .appendLog ("[-] 生成SQLMap命令失败: " + e .getMessage ());
502+ JOptionPane .showMessageDialog (uiTab ,
503+ "生成SQLMap命令失败:\n " + e .getMessage (),
504+ "错误" , JOptionPane .ERROR_MESSAGE );
505+ }
506+ }
507+
508+ /**
509+ * 处理"执行SQLMap扫描"菜单点击
510+ */
511+ private void handleExecuteSqlMap (IHttpRequestResponse message ) {
512+ try {
513+ // 检查SQLMap路径是否配置
514+ String sqlmapPath = configManager .getDirectSqlmapPath ();
515+ if (sqlmapPath == null || sqlmapPath .isEmpty ()) {
516+ if (CommandPreviewDialog .showConfigWarning (uiTab , "SQLMap路径" )) {
517+ uiTab .switchToDirectExecuteTab ();
518+ }
519+ return ;
520+ }
521+
522+ // 生成HTTP请求字符串
523+ String httpRequest = buildHttpRequest (message );
524+
525+ // 生成临时文件
526+ String requestFilePath = SqlCommandBuilder .generateRequestFile (
527+ httpRequest ,
528+ configManager .getClipboardTempDir ()
529+ );
530+
531+ // 构建SQLMap命令
532+ String sqlmapCommand = SqlCommandBuilder .buildSqlMapCommand (
533+ configManager .getDirectPythonPath (),
534+ configManager .getDirectSqlmapPath (),
535+ requestFilePath ,
536+ buildAdditionalParams (configManager .getSelectedScanConfig ())
537+ );
538+
539+ // 构建终端命令
540+ String terminalCommand = SqlCommandBuilder .buildTerminalCommand (
541+ sqlmapCommand ,
542+ configManager .getDirectTerminalType (),
543+ configManager .isDirectKeepTerminal ()
544+ );
545+
546+ uiTab .appendLog ("[+] 正在启动SQLMap扫描..." );
547+ uiTab .appendLog (" 请求文件: " + requestFilePath );
548+
549+ // 执行命令
550+ CommandExecutor .ExecutionResult result = CommandExecutor .executeInTerminal (
551+ sqlmapCommand ,
552+ configManager .getDirectTerminalType (),
553+ configManager .isDirectKeepTerminal ()
554+ );
555+
556+ if (result .isSuccess ()) {
557+ uiTab .appendLog ("[+] SQLMap扫描已在终端中启动" );
558+ JOptionPane .showMessageDialog (uiTab ,
559+ "SQLMap扫描已在终端中启动!\n \n " +
560+ "终端窗口会独立运行,您可以继续使用Burp。" ,
561+ "执行成功" , JOptionPane .INFORMATION_MESSAGE );
562+ } else {
563+ uiTab .appendLog ("[-] 启动终端失败: " + result .getMessage ());
564+ JOptionPane .showMessageDialog (uiTab ,
565+ "启动终端失败:\n " + result .getMessage (),
566+ "执行失败" , JOptionPane .ERROR_MESSAGE );
567+ }
568+
569+ } catch (Exception e ) {
570+ uiTab .appendLog ("[-] 执行SQLMap扫描失败: " + e .getMessage ());
571+ JOptionPane .showMessageDialog (uiTab ,
572+ "执行SQLMap扫描失败:\n " + e .getMessage (),
573+ "错误" , JOptionPane .ERROR_MESSAGE );
574+ }
575+ }
576+
577+ /**
578+ * 从IHttpRequestResponse构建HTTP请求字符串
579+ */
580+ private String buildHttpRequest (IHttpRequestResponse message ) {
581+ StringBuilder request = new StringBuilder ();
582+
583+ // 获取请求信息
584+ IRequestInfo requestInfo = helpers .analyzeRequest (message );
585+
586+ // 请求行
587+ String method = requestInfo .getMethod ();
588+ String path = requestInfo .getUrl ().getPath ();
589+ String query = requestInfo .getUrl ().getQuery ();
590+
591+ request .append (method ).append (" " ).append (path );
592+ if (query != null && !query .isEmpty ()) {
593+ request .append ("?" ).append (query );
594+ }
595+ request .append (" HTTP/1.1\r \n " );
596+
597+ // 请求头
598+ List <String > headers = requestInfo .getHeaders ();
599+ for (String header : headers ) {
600+ request .append (header ).append ("\r \n " );
601+ }
602+
603+ // 空行
604+ request .append ("\r \n " );
605+
606+ // 请求体
607+ byte [] body = message .getRequest ();
608+ if (body != null && body .length > 0 ) {
609+ int bodyOffset = requestInfo .getBodyOffset ();
610+ String bodyStr = new String (body , bodyOffset , body .length - bodyOffset , StandardCharsets .UTF_8 );
611+ request .append (bodyStr );
612+ }
613+
614+ return request .toString ();
615+ }
616+
617+ /**
618+ * 构建额外的SQLMap参数(CLI格式)
619+ */
620+ private String buildAdditionalParams (ScanConfig config ) {
621+ if (config == null ) {
622+ return "" ;
623+ }
624+ // 使用 toCommandLineString() 生成正确的 CLI 参数(如 --random-agent, --batch 等)
625+ return config .toCommandLineString ().trim ();
626+ }
401627}
628+
0 commit comments