Skip to content

Commit

Permalink
增加一个导出器
Browse files Browse the repository at this point in the history
  • Loading branch information
VillanCh committed May 31, 2022
1 parent 003c712 commit 2136a35
Show file tree
Hide file tree
Showing 6 changed files with 210 additions and 14 deletions.
35 changes: 35 additions & 0 deletions app/main/handlers/generalExport.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
const {ipcMain} = require("electron");

module.exports = (win, getClient) => {
const exportTasks = new Map();

ipcMain.handle("ExtractDataToFile", async (e, request) => {
const {token, params} = request;
if (token === undefined || !token) {
return
}

let currentStream = exportTasks.get(token);
if (currentStream === undefined) {
currentStream = getClient().ExtractDataToFile()
currentStream.on("error", e => {
if (win) {
win.webContents.send(`${token}-error`, `${e}`)
}
})
currentStream.on("data", data => {
if (win) {
win.webContents.send(`${token}-data`, data)
}
})
currentStream.on("end", () => {
if (win) {
win.webContents.send(`${token}-end`)
}
exportTasks.delete(token)
})
exportTasks.set(token, currentStream)
}
currentStream.write(params)
})
}
3 changes: 3 additions & 0 deletions app/main/ipc.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ module.exports = {
// 数据对比
require("./handlers/dataCompare")(win, getClient);

// 增加一个通用的导出功能
require("./handlers/generalExport")(win, getClient);

//
require("./handlers/facadeServer")(win, getClient);
// 小工具插件
Expand Down
2 changes: 2 additions & 0 deletions app/renderer/src/main/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {Form, Modal, notification, Spin, Tabs, Typography} from "antd";
import {yakEcho} from "./utils/yakEcho";
import {failed, info, success} from "./utils/notification";
import {AutoSpin} from "./components/AutoSpin";
import {useHotkeys} from "react-hotkeys-hook";
import {testExportData} from "./utils/exporter";

const InterceptKeyword = [
// "KeyA",
Expand Down
15 changes: 9 additions & 6 deletions app/renderer/src/main/src/pages/hacker/httpHacker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ import {HackerPlugin} from "./HackerPlugin"
import ReactDOM from "react-dom"

import "../main.css"
import {useHotkeys} from "react-hotkeys-hook";
import {info} from "../../utils/notification";

export interface HTTPHackerProp {}
export interface HTTPHackerProp {
}

const defaultHTTPPacket = `GET / HTTP/1.1
Host: www.example.com
Expand Down Expand Up @@ -51,7 +54,7 @@ const HTTPHacker: React.FC<HTTPHackerProp> = (props) => {
hideAdd={true}
onTabClick={(key, e) => {
const divExisted = document.getElementById("yakit-cursor-menu")
if(divExisted){
if (divExisted) {
const div: HTMLDivElement = divExisted as HTMLDivElement
const unmountResult = ReactDOM.unmountComponentAtNode(div)
if (unmountResult && div.parentNode) {
Expand All @@ -62,20 +65,20 @@ const HTTPHacker: React.FC<HTTPHackerProp> = (props) => {
>
<Tabs.TabPane tab={"MITM:中间人代理与劫持"} key={"mitm"} closable={false}>
<div style={{height: "100%", overflow: "auto"}}>
<MITMPage />
<MITMPage/>
</div>
</Tabs.TabPane>
<Tabs.TabPane tab={"HTTP History"} key={"history"} closable={false} forceRender={true}>
<div style={{height: "100%"}}>
<HTTPHistory />
<HTTPHistory/>
</div>
</Tabs.TabPane>
<Tabs.TabPane tab={"插件输出"} key={"plugin"} closable={false}>
<YakScriptExecResultTable />
<YakScriptExecResultTable/>
</Tabs.TabPane>
<Tabs.TabPane tab={"网站树视角"} key={"website-tree"} closable={false}>
<div style={{height: "100%"}}>
<WebsiteTreeViewer />
<WebsiteTreeViewer/>
</div>
</Tabs.TabPane>
</Tabs>
Expand Down
15 changes: 7 additions & 8 deletions app/renderer/src/main/src/pages/mitm/MITMPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
Alert,
Button,
Checkbox,
Col, Divider,
Col,
Divider,
Form,
Input,
InputNumber,
Expand All @@ -18,7 +19,7 @@ import {
Typography
} from "antd";
import {failed, info, success} from "../../utils/notification";
import {CheckOutlined, PoweroffOutlined, ReloadOutlined, CopyOutlined} from "@ant-design/icons";
import {CheckOutlined, CopyOutlined, PoweroffOutlined, ReloadOutlined} from "@ant-design/icons";
import {HTTPPacketEditor, YakEditor} from "../../utils/editors";
import {MITMFilters, MITMFilterSchema} from "./MITMFilters";
import {showDrawer, showModal} from "../../utils/showModal";
Expand All @@ -28,20 +29,18 @@ import {ExecResultLog} from "../invoker/batch/ExecMessageViewer";
import {ExtractExecResultMessage} from "../../components/yakitLogSchema";
import {YakExecutorParam} from "../invoker/YakExecutorParams";
import "./MITMPage.css";
import {CopyableField, SelectOne, SwitchItem} from "../../utils/inputUtil";
import {MITMPluginOperator} from "./MITMPluginOperator";
import {useGetState, useHistoryTravel, useLatest, useMemoizedFn, useMouse} from "ahooks";
import {CopyableField, SelectOne} from "../../utils/inputUtil";
import {useGetState, useLatest, useMemoizedFn, useMouse} from "ahooks";
import {StatusCardProps} from "../yakitStore/viewers/base";
import {useHotkeys} from "react-hotkeys-hook";
import * as monaco from 'monaco-editor';
import CopyToClipboard from "react-copy-to-clipboard";
import {AutoCard} from "../../components/AutoCard";
import {ResizeBox} from "../../components/ResizeBox";
import {MITMPluginLogViewer} from "./MITMPluginLogViewer";
import {MITMPluginList, MITMPluginListProp} from "./MITMPluginList";
import {openABSFileLocated, saveABSFileToOpen} from "../../utils/openWebsite";
import {MITMPluginList} from "./MITMPluginList";
import {saveABSFileToOpen} from "../../utils/openWebsite";
import {getValue, saveValue} from "../../utils/kv";
import {PluginList} from "../../components/PluginList";
import {SimplePluginList} from "../../components/SimplePluginList";
import {MITMContentReplacer, MITMContentReplacerRule} from "./MITMContentReplacer";
import {MITMContentReplacerViewer} from "./MITMContentReplacerViewer";
Expand Down
154 changes: 154 additions & 0 deletions app/renderer/src/main/src/utils/exporter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
import React, {useEffect, useState} from "react";
import {randomString} from "./randomUtil";
import {info} from "./notification";
import {showModal} from "./showModal";
import {Button, Form, Space} from "antd";
import {InputItem, SwitchItem} from "./inputUtil";
import {AutoCard} from "../components/AutoCard";
import {useGetState} from "ahooks";
import {openABSFileLocated} from "./openWebsite";

export interface ExtractableValue {
StringValue: string,
BytesValue: Uint8Array
}

export interface ExtractableData {
[key: string]: ExtractableValue
}

export interface GeneralExporterProp extends basicConfig {
Data: ExtractableData[]
}

interface basicConfig {
JsonOutput: boolean
CSVOutput: boolean
DirName: string
FilePattern: string
}

const {ipcRenderer} = window.require("electron");

const GeneralExporter: React.FC<GeneralExporterProp> = (props) => {
const [token, setToken] = useState(randomString(30));
const [paths, setPaths, getPaths] = useGetState<string[]>([]);

useEffect(() => {
if (!token) {
return
}

ipcRenderer.on(`${token}-data`, (_, data: { FilePath: string }) => {
const origin = getPaths();
origin.push(data.FilePath)
setPaths(origin.map(v => v))
})
ipcRenderer.on(`${token}-end`, () => {
info("导出结束")
})
ipcRenderer.on(`${token}-error`, (_, e) => {

})

const {JsonOutput, CSVOutput, DirName, FilePattern} = props;
ipcRenderer.invoke("ExtractDataToFile", {
token,
params: {JsonOutput, CSVOutput, DirName, FilePattern}
}).then(() => {
info("发送生成文件配置成功...")
})
props.Data.forEach(value => {
ipcRenderer.invoke("ExtractDataToFile", {
token,
params: {Data: value}
}).then(() => {
})
})
ipcRenderer.invoke("ExtractDataToFile", {token, params: {Finished: true}})

return () => {
ipcRenderer.removeAllListeners(`${token}-data`)
ipcRenderer.removeAllListeners(`${token}-error`)
ipcRenderer.removeAllListeners(`${token}-end`)
}
}, [token])

return <AutoCard title={"获取生成的文件"}>
<Space direction={"vertical"}>
{paths.map(i => {
return <Button
type={"link"}
onClick={() => {
openABSFileLocated(i)
}}
>{i}</Button>
})}
</Space>
</AutoCard>
};

export const exportData = (data: ExtractableData[]) => {
showModal({
title: "导出数据",
width: "60%",
content: (
<>
<GeneralExporterForm Data={data}/>
</>
)
})
}

export const testExportData = () => {
exportData([
{"KYE": {StringValue: "asdfasdfasdfasdfasdf", BytesValue: new Uint8Array}},
{"KYE": {StringValue: "asdfasasdf", BytesValue: new Uint8Array}},
{"KYE": {StringValue: "asdfassdf", BytesValue: new Uint8Array}},
])
}

interface GeneralExporterFormProp {
Config?: basicConfig,
Data: ExtractableData[]
}

const GeneralExporterForm: React.FC<GeneralExporterFormProp> = (props) => {
const [params, setParams] = useState<basicConfig>(!!props.Config ? props.Config : {
CSVOutput: true,
DirName: "",
FilePattern: "",
JsonOutput: true
});
return <Form
labelCol={{span: 5}} wrapperCol={{span: 14}}
onSubmitCapture={e => {
showModal({
title: "生成导出文件", width: "50%",
content: (
<GeneralExporter {...params} Data={props.Data}/>
)
})
}}
>
<SwitchItem
label={"导出 JSON"}
setValue={JsonOutput => setParams({...params, JsonOutput})} value={params.JsonOutput}
/>
<SwitchItem
label={"导出 CSV"}
setValue={CSVOutput => setParams({...params, CSVOutput})} value={params.CSVOutput}
/>
<InputItem
label={"输出到目录"} placeholder={"可为空,默认为 yakit 临时目录"}
setValue={DirName => setParams({...params, DirName})} value={params.DirName}
/>
<InputItem
label={"文件名"} placeholder={"'*' 可作为随机字符串填空,不需要填写后缀"}
setValue={FilePattern => setParams({...params, FilePattern})} value={params.FilePattern}
/>
<Form.Item colon={false} label={" "}>
<Button type="primary" htmlType="submit"> 生成数据到本地文件 </Button>
</Form.Item>
</Form>
};

0 comments on commit 2136a35

Please sign in to comment.