feat: 解析导入表
This commit is contained in:
parent
ce85787be9
commit
9806c9bacd
@ -16,11 +16,13 @@
|
|||||||
"@tauri-apps/plugin-shell": "^2",
|
"@tauri-apps/plugin-shell": "^2",
|
||||||
"antd": "^5.22.3",
|
"antd": "^5.22.3",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"moment": "^2.30.1",
|
||||||
"react": "^18.0.0",
|
"react": "^18.0.0",
|
||||||
"react-dom": "^18.0.0"
|
"react-dom": "^18.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^2",
|
"@tauri-apps/cli": "^2",
|
||||||
|
"@types/antd": "^1.0.4",
|
||||||
"@types/lodash-es": "^4.17.12",
|
"@types/lodash-es": "^4.17.12",
|
||||||
"@types/react": "^18.0.0",
|
"@types/react": "^18.0.0",
|
||||||
"@types/react-dom": "^18.0.0",
|
"@types/react-dom": "^18.0.0",
|
||||||
|
@ -22,10 +22,13 @@ importers:
|
|||||||
version: 2.0.1
|
version: 2.0.1
|
||||||
antd:
|
antd:
|
||||||
specifier: ^5.22.3
|
specifier: ^5.22.3
|
||||||
version: 5.22.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
version: 5.22.3(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
lodash-es:
|
lodash-es:
|
||||||
specifier: ^4.17.21
|
specifier: ^4.17.21
|
||||||
version: 4.17.21
|
version: 4.17.21
|
||||||
|
moment:
|
||||||
|
specifier: ^2.30.1
|
||||||
|
version: 2.30.1
|
||||||
react:
|
react:
|
||||||
specifier: ^18.0.0
|
specifier: ^18.0.0
|
||||||
version: 18.3.1
|
version: 18.3.1
|
||||||
@ -36,6 +39,9 @@ importers:
|
|||||||
'@tauri-apps/cli':
|
'@tauri-apps/cli':
|
||||||
specifier: ^2
|
specifier: ^2
|
||||||
version: 2.1.0
|
version: 2.1.0
|
||||||
|
'@types/antd':
|
||||||
|
specifier: ^1.0.4
|
||||||
|
version: 1.0.4(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
'@types/lodash-es':
|
'@types/lodash-es':
|
||||||
specifier: ^4.17.12
|
specifier: ^4.17.12
|
||||||
version: 4.17.12
|
version: 4.17.12
|
||||||
@ -659,6 +665,10 @@ packages:
|
|||||||
'@tauri-apps/plugin-shell@2.0.1':
|
'@tauri-apps/plugin-shell@2.0.1':
|
||||||
resolution: {integrity: sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw==}
|
resolution: {integrity: sha512-akU1b77sw3qHiynrK0s930y8zKmcdrSD60htjH+mFZqv5WaakZA/XxHR3/sF1nNv9Mgmt/Shls37HwnOr00aSw==}
|
||||||
|
|
||||||
|
'@types/antd@1.0.4':
|
||||||
|
resolution: {integrity: sha512-gp4PGQckP1kNjj2H6juhjKIVwkpXwCIyIvOlwp2DC6geuhVpDHEEB5gwH4hJabVgBAFtrjBPJ58VIRV9VV9W2g==}
|
||||||
|
deprecated: This is a stub types definition. antd provides its own type definitions, so you do not need this installed.
|
||||||
|
|
||||||
'@types/babel__core@7.20.5':
|
'@types/babel__core@7.20.5':
|
||||||
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==}
|
||||||
|
|
||||||
@ -825,6 +835,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
|
||||||
engines: {node: '>=8.6'}
|
engines: {node: '>=8.6'}
|
||||||
|
|
||||||
|
moment@2.30.1:
|
||||||
|
resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==}
|
||||||
|
|
||||||
ms@2.1.3:
|
ms@2.1.3:
|
||||||
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
|
||||||
|
|
||||||
@ -1704,6 +1717,16 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
'@tauri-apps/api': 2.1.1
|
'@tauri-apps/api': 2.1.1
|
||||||
|
|
||||||
|
'@types/antd@1.0.4(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||||
|
dependencies:
|
||||||
|
antd: 5.22.3(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- date-fns
|
||||||
|
- luxon
|
||||||
|
- moment
|
||||||
|
- react
|
||||||
|
- react-dom
|
||||||
|
|
||||||
'@types/babel__core@7.20.5':
|
'@types/babel__core@7.20.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/parser': 7.26.3
|
'@babel/parser': 7.26.3
|
||||||
@ -1755,7 +1778,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
antd@5.22.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
antd@5.22.3(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ant-design/colors': 7.1.0
|
'@ant-design/colors': 7.1.0
|
||||||
'@ant-design/cssinjs': 1.22.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
'@ant-design/cssinjs': 1.22.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
@ -1787,7 +1810,7 @@ snapshots:
|
|||||||
rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
rc-motion: 2.9.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
rc-notification: 5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
rc-notification: 5.6.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
rc-pagination: 4.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
rc-pagination: 4.3.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
rc-picker: 4.8.3(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
rc-picker: 4.8.3(dayjs@1.11.13)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
rc-progress: 4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
rc-progress: 4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
rc-rate: 2.13.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
rc-rate: 2.13.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
rc-resize-observer: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
rc-resize-observer: 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
@ -1934,6 +1957,8 @@ snapshots:
|
|||||||
picomatch: 2.3.1
|
picomatch: 2.3.1
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
moment@2.30.1: {}
|
||||||
|
|
||||||
ms@2.1.3: {}
|
ms@2.1.3: {}
|
||||||
|
|
||||||
nanoid@3.3.8: {}
|
nanoid@3.3.8: {}
|
||||||
@ -2104,7 +2129,7 @@ snapshots:
|
|||||||
react: 18.3.1
|
react: 18.3.1
|
||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
|
|
||||||
rc-picker@4.8.3(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
rc-picker@4.8.3(dayjs@1.11.13)(moment@2.30.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.26.0
|
'@babel/runtime': 7.26.0
|
||||||
'@rc-component/trigger': 2.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
'@rc-component/trigger': 2.2.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||||
@ -2116,6 +2141,7 @@ snapshots:
|
|||||||
react-dom: 18.3.1(react@18.3.1)
|
react-dom: 18.3.1(react@18.3.1)
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
dayjs: 1.11.13
|
dayjs: 1.11.13
|
||||||
|
moment: 2.30.1
|
||||||
|
|
||||||
rc-progress@4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
rc-progress@4.0.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -173,7 +173,7 @@ impl AppState {
|
|||||||
import_module.module_name_rva = origin_item.name;
|
import_module.module_name_rva = origin_item.name;
|
||||||
import_module.timestamp = origin_item.time_date_stamp;
|
import_module.timestamp = origin_item.time_date_stamp;
|
||||||
// 2. 获取函数表
|
// 2. 获取函数表
|
||||||
let mut functions: Vec<ImportFunctionTableItem> = Vec::new();
|
let mut functions: Vec<ImportFunctionTable> = Vec::new();
|
||||||
// 获取函数表的FOA
|
// 获取函数表的FOA
|
||||||
let mut base_offset = mmap.rva_to_foa(origin_item.original_first_thunk)? as usize;
|
let mut base_offset = mmap.rva_to_foa(origin_item.original_first_thunk)? as usize;
|
||||||
// 循环解析函数表
|
// 循环解析函数表
|
||||||
@ -222,10 +222,11 @@ impl AppState {
|
|||||||
CStr::from_ptr(ptr).to_str()?.to_string()
|
CStr::from_ptr(ptr).to_str()?.to_string()
|
||||||
};
|
};
|
||||||
import_function_table.function_name = function_name;
|
import_function_table.function_name = function_name;
|
||||||
functions.push(ImportFunctionTableItem::Named(import_function_table));
|
functions.push(import_function_table);
|
||||||
}
|
}
|
||||||
false => {
|
false => {
|
||||||
functions.push(ImportFunctionTableItem::Ordinal(import_function_table));
|
import_function_table.function_type = ImportFunctionTableItem::Ordinal;
|
||||||
|
functions.push(import_function_table);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 下一个
|
// 下一个
|
||||||
@ -242,7 +243,7 @@ impl AppState {
|
|||||||
#[derive(Debug, Default, Clone, Serialize)]
|
#[derive(Debug, Default, Clone, Serialize)]
|
||||||
pub struct ImportModuleTable {
|
pub struct ImportModuleTable {
|
||||||
pub module_name: String, // 模块名称
|
pub module_name: String, // 模块名称
|
||||||
pub functions: Vec<ImportFunctionTableItem>, // 这里要另一个结构体来描述
|
pub functions: Vec<ImportFunctionTable>, // 这里要另一个结构体来描述
|
||||||
pub timestamp: u32, // 时间戳
|
pub timestamp: u32, // 时间戳
|
||||||
pub forwarder_chain: u32, // 转发链
|
pub forwarder_chain: u32, // 转发链
|
||||||
pub module_name_rva: u32, // dll名称的RVA
|
pub module_name_rva: u32, // dll名称的RVA
|
||||||
@ -251,12 +252,13 @@ pub struct ImportModuleTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 因为导入查找表可能是函数名、也可能是序号 所以用枚举包一下最好
|
// 因为导入查找表可能是函数名、也可能是序号 所以用枚举包一下最好
|
||||||
#[derive(Debug, Clone, Serialize)]
|
#[derive(Debug, Clone, Serialize, Default)]
|
||||||
pub enum ImportFunctionTableItem {
|
pub enum ImportFunctionTableItem {
|
||||||
// 命名的导入函数
|
// 命名的导入函数
|
||||||
Named(ImportFunctionTable),
|
#[default]
|
||||||
|
Named,
|
||||||
// 序号表示的
|
// 序号表示的
|
||||||
Ordinal(ImportFunctionTable),
|
Ordinal,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Serialize)]
|
#[derive(Debug, Default, Clone, Serialize)]
|
||||||
@ -264,4 +266,5 @@ pub struct ImportFunctionTable {
|
|||||||
pub function_name: String, // 函数名称
|
pub function_name: String, // 函数名称
|
||||||
pub function_address: u32, // 函数地址 IAT
|
pub function_address: u32, // 函数地址 IAT
|
||||||
pub function_hint: u16, // 函数提示
|
pub function_hint: u16, // 函数提示
|
||||||
|
pub function_type: ImportFunctionTableItem
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
// import MainLayout from "./layouts/MainLayout";
|
// import MainLayout from "./layouts/MainLayout";
|
||||||
import MainPage from "./pages/MainPage";
|
import MainPage from "./pages/MainPage";
|
||||||
|
import moment from "moment";
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
|
// 初始化moment地区
|
||||||
|
moment.locale("zh-cn");
|
||||||
|
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
.moduleContainer{
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.functionContainer{
|
||||||
|
|
||||||
|
}
|
@ -1,10 +1,178 @@
|
|||||||
import { invoke } from "@tauri-apps/api/core";
|
import { invoke } from "@tauri-apps/api/core";
|
||||||
import { useEffect } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
|
import styles from "./ImportDirectory.module.scss";
|
||||||
|
import moment from "moment";
|
||||||
|
import { Flex, Table } from "antd";
|
||||||
|
import Title from "antd/es/typography/Title";
|
||||||
|
|
||||||
|
interface ModuleProps {
|
||||||
|
module_name: String;
|
||||||
|
timestamp: number;
|
||||||
|
forwarder_chain: number;
|
||||||
|
module_name_rva: number;
|
||||||
|
functions: FunctionProps[];
|
||||||
|
}
|
||||||
|
interface FunctionProps {
|
||||||
|
function_name: string;
|
||||||
|
function_address: number;
|
||||||
|
function_hint: number;
|
||||||
|
function_type: "Named" | "Ordinal";
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ImportModuleComponentProps {
|
||||||
|
moduleList: ModuleProps[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ModuleFuntionsComponentProps {
|
||||||
|
functionList: FunctionProps[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const ImportModuleComponent: React.FC<ImportModuleComponentProps> = (props) => {
|
||||||
|
const { moduleList } = props;
|
||||||
|
const [selectedRowKey, setSelectedRowKey] = useState([]);
|
||||||
|
const handleRowSelection = (selectedRowKeys: any) => {
|
||||||
|
setSelectedRowKey(selectedRowKeys);
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "模块名称",
|
||||||
|
dataIndex: "module_name",
|
||||||
|
key: "module_name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "导入函数数量",
|
||||||
|
render: (_text: any, record: any) => {
|
||||||
|
console.log("record", record);
|
||||||
|
return record.functions.length;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "时间戳",
|
||||||
|
dataIndex: "timestamp",
|
||||||
|
key: "timestamp",
|
||||||
|
render: (text: number) => {
|
||||||
|
return moment(text).format("YYYY-MM-DD HH:mm:ss");
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "转发链",
|
||||||
|
dataIndex: "forwarder_chain",
|
||||||
|
key: "forwarder_chain",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "模块名称的RVA",
|
||||||
|
dataIndex: "module_name_rva",
|
||||||
|
key: "module_name_rva",
|
||||||
|
render: (text: number) => {
|
||||||
|
return "0x" + text.toString(16).toUpperCase();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const select_functions = useMemo(() => {
|
||||||
|
return moduleList.find((module) => module.module_name === selectedRowKey[0])
|
||||||
|
?.functions;
|
||||||
|
}, [selectedRowKey]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex className={styles.moduleContainer}>
|
||||||
|
<div>
|
||||||
|
<Title level={3}>导入模块</Title>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
rowSelection={{
|
||||||
|
type: "radio",
|
||||||
|
selectedRowKeys: selectedRowKey,
|
||||||
|
selections: [
|
||||||
|
{
|
||||||
|
key: "module_name",
|
||||||
|
text: "模块名称",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}}
|
||||||
|
onRow={(record, rowIndex) => {
|
||||||
|
return {
|
||||||
|
onClick: (event) => {
|
||||||
|
handleRowSelection([record.module_name]);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
bordered
|
||||||
|
dataSource={moduleList}
|
||||||
|
rowKey="module_name"
|
||||||
|
pagination={false}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Title level={3}>导入模块</Title>
|
||||||
|
<ModuleFuntionsComponent functionList={select_functions} />
|
||||||
|
</div>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const ModuleFuntionsComponent: React.FC<ModuleFuntionsComponentProps> = (
|
||||||
|
props
|
||||||
|
) => {
|
||||||
|
const { functionList } = props;
|
||||||
|
console.log("functionList", functionList);
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: "序号",
|
||||||
|
render:(_text,_record,index)=>`${index+1}`,
|
||||||
|
rowScope: 'row',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "函数名称",
|
||||||
|
dataIndex: "function_name",
|
||||||
|
key: "function_name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "函数地址",
|
||||||
|
dataIndex: "function_address",
|
||||||
|
key: "function_address",
|
||||||
|
render: (text: number) => {
|
||||||
|
return "0x" + text.toString(16).toUpperCase();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "函数提示",
|
||||||
|
dataIndex: "function_hint",
|
||||||
|
key: "function_hint",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "函数类型",
|
||||||
|
dataIndex: "function_type",
|
||||||
|
key: "function_type",
|
||||||
|
render: (text: string) => {
|
||||||
|
console.log("text", text);
|
||||||
|
return text == "Named" ? "命名" : "序数";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Flex className={styles.functionContainer}>
|
||||||
|
<Table
|
||||||
|
columns={columns}
|
||||||
|
dataSource={functionList}
|
||||||
|
rowKey="function_name"
|
||||||
|
pagination={false}
|
||||||
|
bordered
|
||||||
|
/>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
export default function ImportDirectory() {
|
export default function ImportDirectory() {
|
||||||
|
const [data, setData] = useState([]);
|
||||||
|
|
||||||
const getData = () => {
|
const getData = () => {
|
||||||
invoke("command_get_pe_data_import_directory")
|
invoke("command_get_pe_data_import_directory")
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log("res", res);
|
const { fields } = res as any;
|
||||||
|
setData(fields);
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
console.log("err", err);
|
console.log("err", err);
|
||||||
@ -15,5 +183,9 @@ export default function ImportDirectory() {
|
|||||||
getData();
|
getData();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return <div>ImportDirectory</div>;
|
return (
|
||||||
|
<div>
|
||||||
|
<ImportModuleComponent moduleList={data} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user