body { font-size: 15px; color: rgba(51, 51, 51, 1); background: rgba(255, 255, 255, 1); font-family: Helvetica, Arial, "PingFang SC", "Microsoft YaHei", "WenQuanYi Micro Hei", "tohoma,sans-serif"; margin: 0; padding: 10% }
h1 { font-size: 2.2em; font-weight: 700; line-height: 1.1; padding-top: 16px; margin-bottom: 4px }
h2, h3, h4, h5, h6 { line-height: 1.5em; margin-top: 2.2em; margin-bottom: 4px }
h2 { color: rgba(255, 255, 255, 1) !important; background-color: rgba(255, 155, 152, 1); border-left: 12px solid rgba(255, 126, 121, 1); padding: 1px 1px 1px 20px }
h3 { font-weight: 700; font-size: 1.2em; line-height: 1.4; margin: 10px 0 5px; padding-top: 10px }
h4 { font-weight: 700; text-transform: uppercase; font-size: 1.1em; line-height: 1.4; margin: 10px 0 5px; padding-top: 10px }
h5, h6 { font-size: 0.9em }
h5 { font-weight: bold; text-transform: uppercase }
h6 { font-weight: normal; color: rgba(170, 170, 170, 1) }
img { width: 100%; border-radius: 5px; display: block; margin-bottom: 15px; height: auto }
dl, ol, ul { margin-top: 12px; margin-bottom: 20px; padding-left: 5%; line-height: 1.8 }
p { margin: 0 0 20px; padding: 0; line-height: 1.8 }
a { color: rgba(242, 47, 39, 1); text-decoration: none }
a:hover { color: rgba(245, 88, 82, 1); text-decoration: underline }
a:focus { outline-offset: -2px }
blockquote { font-size: 1em; font-style: normal; padding: 30px 38px; margin: 0 0 15px; position: relative; line-height: 1.8; text-indent: 0; border: none; color: rgba(136, 136, 136, 1) }
blockquote:before { content: "“"; left: 12px; top: 0; color: rgba(224, 224, 224, 1); font-size: 4em; font-family: Arial, serif; line-height: 1em; font-weight: 700; position: absolute }
blockquote:after { content: "”"; right: 12px; bottom: -31px; color: rgba(224, 224, 224, 1); font-size: 4em; font-family: Arial, serif; line-height: 1em; font-weight: 700; position: absolute }
strong, dfn { font-weight: 700 }
em, dfn { font-style: italic; font-weight: 400 }
del { text-decoration: line-through }
pre { margin: 0 0 25px; font-size: 13px; line-height: 1.42857; word-break: break-all; word-wrap: break-word; border-radius: 4px; white-space: pre-wrap; display: block; background: rgba(248, 248, 248, 1); padding: 10px 20px; border: none; color: rgba(102, 102, 102, 1); font-family: Courier, sans-serif }
code { color: rgba(199, 37, 78, 1); background-color: rgba(249, 242, 244, 1); border-radius: 4px; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; padding: 2px 4px; font-size: 90% }
p>code { color: rgba(199, 38, 78, 1); background-color: rgba(249, 242, 244, 1); font-size: 0.95em; border-radius: 3px; -moz-border-radius: 3px; -webkit-border-radius: 3px }
figure { margin: 1em 0 }
figcaption { font-size: 0.75em; padding: 0.5em 2em; margin-bottom: 2em }
figure img { margin-bottom: 0 }
hr { border: 0; height: 1px; background: linear-gradient(90deg, rgba(204, 204, 204, 1), rgba(51, 51, 51, 1), rgba(204, 204, 204, 1)) rgba(51, 51, 51, 1) }
ol p, ul p { margin-bottom: 0 }
li { margin-bottom: 0.75em; margin-top: 0.75em }
ol#footnotes { font-size: 0.95em; padding-top: 1em; margin: 20px 0; border-top: 1px solid rgba(234, 234, 234, 1); counter-reset: footer-counter 0; list-style: none; color: rgba(85, 85, 85, 1); padding-left: 5% }
ol#footnotes li { margin-bottom: 10px; margin-left: 16px; font-weight: 400; line-height: 2; list-style-type: none }
ol#footnotes li:before { content: counter(footer-counter) ". "; counter-increment: footer-counter 1; font-weight: 800; font-size: 0.95em }
@keyframes highfade { 0% { } 20% { background-color: rgba(255, 255, 0, 1) } 100% { } }
@-webkit-keyframes highfade{0%{background-color:none}20%{background-color:yellow}100%{background-color:none}}
a:target, ol#footnotes li:target, sup a:target { animation-name: highfade; animation-duration: 2s; animation-iteration-count: 1; animation-timing-function: ease-in-out; -webkit-animation-name: highfade; -webkit-animation-duration: 2s; -webkit-animation-iteration-count: 1; -webkit-animation-timing-function: ease-in-out }
a:target { border: 0; outline: 0 }
a:target { border: 0; outline: 0 }
a:target { border: 0; outline: 0 }

每天一个技术点

(2023.7.24)软件加密与解密-2-1-程序分析方法[XDbg]

本文作者:XDbgPYG(小吧唧)

发布时间:2023年7月24日

内容概要:练一道题

img { box-shadow: 0 0 2px rgba(6, 24, 44, 0.4), 0 4px 6px -1px rgba(6, 24, 44, 0.65), inset 0 1px rgba(255, 255, 255, 0.08) }

0. 收集信息

程序名:CrackMeDemo.tvmp.1.exe

程序界面长相如下:

程序内存长相如下:

程序内存字符串长相如下:

看样子是个 MFC 程序(“应用程序向导生成的本地应用程序”、“CWINAPP”)

除此之外有用的信息似乎就是 “恭喜你,注册成功!” 了。

1.反调试

1.1 学习反调试

扔进 OllyDbg 里,F9跑起来。

查看 OllyDbg 日志,并无太多有用信息,只是知道它跑了俩个异常。

  1. 浮点数除以零
  2. 内存访问违规

嗯。。想想有没有办法定位反调试代码范围。

通过例子来学习反调试,点击图片开始学习。

这是我刚才 CV 过来的反调试例子。看样子我们可以试试通过 API 断点,来定位反调试。

if (IsDebuggerPresent())
ExitProcess(-1); BOOL bDebuggerPresent;
if (TRUE == CheckRemoteDebuggerPresent(GetCurrentProcess(), &bDebuggerPresent) &&
TRUE == bDebuggerPresent)
ExitProcess(-1);

1.2 跟踪反调试

在 CheckRemoteDebuggerPresent 函数下断点,程序断下后,Ctrl + F9 出 CALL。

似乎走到了 ".tvm" 这个代码段来了,简单的使用下我们在日后编写的分析工具 - “基于 CPU 模拟的 xxx ” 。

好的,成功模拟到了 0x040157A 这个地址。通过反汇编代码可以看出,这个代码块似乎含有多个反调试函数,有俩个 jcc 指令跳转到了含有 CALL ExitThread 指令的基本块。

F9 运行到 0x040157A 里,由于我们的反反调试插件强大,所以CheckRemoteDebuggerPresent 返回了 0 ,即程序未被调试的意思。

继续模拟程序,运行到 IsDebuggerPresent 函数中。嗯,是刚才我们学过的反调试内容。

1.3 总结 Call 运行流程

1.3.0 随便看看

将程序扔入 IDA 中 F5 继续分析。根据 IDA 提供的符号,我们大致可以看出这个反调试是在执行 MFC m = new M(b,c) 后才开始检测的。这个信息对于我们完整还原这个程序至关重要,没错,我想把这个程序还原掉~

1.3.1 检测到调试后会执行什么?

让我们看看程序检测调试到后会做些什么?

发现有个 sub_4031f0 call,学过设计模式的朋友应该知道单例模式:即一个类负责一个功能。所以 sub_403530 我是优先不管的。

  if ( cObf_CheckRemoteDebug() == 1 || cObf_IsDebug() == 1 )
{
v4 = sub_403530(1); // thiscall 不用理他,估计就是销毁 Dialog 用的。
sub_4031F0(v4); // cdeclCall 有点意思~
ExitThread(8u);
}

对 sub_4031f0 扫引用,发现有个未被 Ida 识别成函数的地址(0x4021B6)。

转到 0x4021B6 看看,发现有个 call MessageBoxA ,字符串参数为 byte_428D64 。

用 OD 转到 byte_428D64 看看是什么内容。运气不错,直逼关键内容。

让我们对 0x4021B6 附近的代码进行简单的分析。发现一个循环代码,跳转到 loc_401F9A 看看。

发现了一个立即数:8。(如果你足够细心就可以发现,这是我们可输入的用户名的最大长度)

看样子关键算法代码,我们已经定位到了,即在 0x00401FA9 附近。

1.4 拿下第一个符号:MFC_INIT

经过刚才的分析,我们基本可以判断这个 Call 是在做 MFC 初始化 + AntiDebuger 操作了。将其记录下来~ 这是我们逆向出来的第一个符号。这是一个重要的阶段性胜利。

1.5 Patch 掉反调试

这里用最容易被 Anti 掉的过检测方式来过掉反调试。将其 leave ret 即可过掉反调试。(还有内存校验没有解决)

可以看到,这里程序已经正常运行了。hiahia。

2. 逆向算法

OD 转到 0x00401FA9 下查看情况,似乎 IDA 有好多代码没有识别出来,有点小尴尬哈。

找了下函数头部 0x00401B35 ,回到 IDA ,一路选中未识别的代码,按下 C 键完成格式转换。

先静态分析看看函数的控制流程好了,在这里我给出我认为简洁有用的信息。

.text:00401DA6                 call    ?UpdateData@CWnd@@QAEHH@Z ; CWnd::UpdateData(int)
.text:00401DFC call ?GetDlgItem@CWnd@@QBEPAV1@H@Z ; CWnd::GetDlgItem(int)
.text:00401E1F call ?GetDlgItem@CWnd@@QBEPAV1@H@Z ; CWnd::GetDlgItem(int) .text:00401E9A cmp dword ptr [ebp-8C4h], 8
.text:00401EA1 jge short loc_401F21 .text:00402204 repe cmpsd
.text:00402206 jnz short loc_40221C
.text:00402208 push 0 ; unsigned int
.text:0040220A push 0 ; lpCaption
.text:0040220C push offset Text ; lpText
.text:00402211 mov ecx, [ebp-8D0h] ; this
.text:00402217 call ?MessageBoxA@CWnd@@QAEHPBD0I@Z ; CWnd::MessageBoxA(char const *,char const *,uint)

用 OD 看看和 MsgBoxA 有关的信息 text:00402204 repe cmpsd。经过测试,发现算法不允许输入中文字符串,尴尬了。

uN: XXXXXDbg
pW: EasyXEasyXEasyXEasyXEasyXEasyXEasyXEasyXEasyX

程序是明码比较,这似乎意味着我们在算法部分可以轻松一下了 O(∩_∩)O。

用 IDA 进行 F5 操作。嗯,好模糊的代码,看样子需要清理花指令才能看清楚。(可是现在已经是 22:11:38 了,一天要结束了,就不写脚本清理了)

数了下大致的代码行数,只有 213 行汇编,直接用 OD 调试还原掉好了。

好了,回到让我们到 0x0401E9A 下断点.

.text:00401E9A                 cmp     dword ptr [ebp-8C4h], 8
.text:00401EA1 jge short loc_401F21

经过我努力的跟踪,还原出了如下代码,发现程序获取了代码段的内容并参与到了算法的计算当中,真是个不错的设计。

这样做的话,程序如果直接爆破,生成出来的 serial 必然是错的,难怪程序会直接明码比较,原来暗藏着一股气。

打开 VS ,编写我们的第一段 KeyGen。

// KeyGen.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
// #include <iostream> unsigned char t[0x0010] =
{
0xf7, 0xff, 0xff, 0x89, 0x8d, 0x4c, 0xf7, 0xff, 0xff, 0xc7, 0x85, 0x48, 0xf7, 0xff, 0xff, 0x14
}; unsigned char t2[0x0010] =
{
0x07, 0x82, 0x19, 0xe8, 0x1f, 0xfe, 0xff, 0xff, 0x0f, 0xb6, 0xd0, 0x85, 0xd2, 0x74, 0x05, 0xe9
}; void getUn2(unsigned char* _un, unsigned char* _un2)
{
unsigned char* un = _un;
unsigned char* un2 = _un2; for (size_t i = 0; i < 8; i++)
{
int a = (int)*(char*)(t+i);
int a2 = (int)*(char*)(un + i) ^ i;
un2[i] = a2 ^ a;
int b = (int)*(char*)(t2 + i);
int b2 = (int)*(char*)(un2 + i) ;
un2[i] = b2 ^ b;
} } int main()
{
std::cout << "Hello KeyGen!\n";
unsigned char uN[] = "XXXXXDbg";
unsigned char* uN2 = new unsigned char[sizeof(uN)];
getUn2(uN,uN2); return 0;
} a8 24 bc 3a ce f3 4c 40 // cd fd fd fd
A8 24 BC 3A CE F3 4C 40 // 00 00 00 00

可以看到结果完全正确,让我们继续还原。发现了一个未知的值,[EBP - 0x828],OD 往上翻翻,看看它的算法。

可以看到一个循环很多次的代码,结合刚才的经验,可以看出这个 constV 也是通过代码段的内容算出来的。

这个函数就不用还原了,取消所有软件断点,然后在 0x0401C53 下硬件执行断点,拿到 constV 即可。

constV = 0xCF238EE8;
int constV = 0xCF238EE8;
*(int*)((int*)uN2) ^= constV;
*(int*)((int*)uN2+1) ^= constV;

接下来又是一个循环,估计又有好多表要 CV 下来,哈哈,可以看到依旧循环 8 次。

继续单步~发现了三个 flag 将这段汇编还原成 C 代码~。

还原代码如下。

for (size_t i = 0; i < 8; i++)
{
flag1 = ((uN2[i] & 0xE0) + (((uN2[i] & 0xE0 )>> 24) & 0x1F)) >> 5;
flag2 = (uN2[i] & 0x1C) + (((uN2[i] & 0x1C) >> 24) & 3) >> 2;
flag3 = uN2[i] & 0x3; int flag4 = i / 3;
int id = i >> 24;
if (id == 2)
{
// .......
} if (id == 1)
{
// .......
} if (id == 0)
{
// .......
}
}

继续单步跟踪,在这块代码中,我们发现几张 table 和新的 flag ,翻翻汇编代码看看 flag 有什么意义。

联系上文可以发现,它存储的就是正确的注册码 Serial。

将 table dump 出来将这个基本块还原成 C 代码。


unsigned char t3_834[0x0040] =
{
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x40, 0xed, 0x9f, 0xf5,
0x26, 0x3a, 0x6f, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}; unsigned char t4_87c[0x00B0] =
{
0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78,
0x79, 0x7a, 0x6e, 0x69, 0xc9, 0x1b, 0x40, 0x00, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x18, 0x00, 0xa0, 0xf0, 0x18, 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5a, 0x00, 0x00, 0x40, 0xed, 0x9f, 0xf5, 0x26, 0x3a, 0x6f, 0x8f, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}; unsigned char t5_864[0x0090] =
{
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x18, 0x00, 0xa0, 0xf0, 0x18, 0x00,
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50,
0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x00, 0x00, 0x40, 0xed, 0x9f, 0xf5,
0x26, 0x3a, 0x6f, 0x8f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
}; if (id == 0)
{
serial[i] = t3_834[flag2];
serial[i+1] = t4_87c[flag3];
serial[i+2] = t5_864[flag1];
}

好了,接下来我们继续还原其他分支,这是 id == 1 的情况。

还原代码如下。


if (id == 1)
{
serial[i] = t3_834[flag1];
serial[i + 1] = t4_87c[flag2];
serial[i + 2] = t5_864[flag3];
}

这是 id == 2 的情况,发现了俩张新的 table:t6、t7 。

将新的 table dump 下来,还原 C 代码如下。


unsigned char t6_83c[0x001E] =
{
0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58,
0x59, 0x5A, 0x7F, 0x00, 0xF6, 0x9A, 0xE2, 0xF7, 0x9B, 0x50, 0x09, 0x80, 0x00, 0x00
}; unsigned char t7_874[0x003C] =
{
0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x19, 0x00, 0xC9, 0x1B, 0x40, 0x00,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x7F, 0x00, 0x00, 0x00, 0x00, 0x00,
0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C
}; if (id == 2)
{
serial[i] = t5_864[flag3];
serial[i+ 1] = t6_83c[flag1];
serial[i + 2] = t7_874[flag2];
}

在测试的时候,发现 serial 对不上,原因是因为我忘记还原这条汇编指令了、flag4 和 id 的关系搞错了 、constV 提取错了。

IMUL    EAX, EAX, 0x3

//int constV = 0xCF238EE8;
int constV = 0xE0698E5C; int flag4 = i % 3;
int id = flag4;

最终输出结果如下。

XXXXXDbg
Vi7Vk21OvWk6Um21LxRj0Ui0

show time

3. tvm?

看样子这个程序被巧妙的绕过了未知的 vm 保护,实际上经过我的测试,这个程序并无 vm 保护,有基于堆栈的花指令混淆。

这在学习第三章也就是下一章代码混淆时,会将其清除~

4. 还原程序

日后再补。

5. 文件下载

CrackMeDemo.tvmp.1.exe

KeyGen

(2023.7.24)软件加密与解密-2-1-程序分析方法[XDbg].md的更多相关文章

  1. RSA加密、解密、公钥私钥生成

    有时项目中需要用到一些加密和解密工具,这里之前整理了一个demo,记录一下,方便查询 package com.test; import java.security.KeyFactory; import ...

  2. Flash型单片机的加密与解密

    厂商利用单片机进行产品开发时,都会关心其代码和数据的保密性.考虑到用户在编写和调试代码时所付出的时间和精力,代码的成本是不言而喻的.    早期的单片机,代码是交给芯片制造商制成掩膜ROM.有两种加密 ...

  3. RSA js加密 java解密

    1. 首先你要拥有一对公钥.私钥: ``` pubKeyStr = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC1gr+rIfYlaNUNLiFsK/Kn ...

  4. C#.NET中对称和非对称加密、解密方法汇总--亲测可用

    C#.NET中对称和非对称加密.解密方法汇总--亲测可用   在安全性要求比较高的系统中都会涉及到数据的加密.解密..NET为我们封装了常用的加密算法,例如:MD5,DES,RSA等.有可逆加密,也有 ...

  5. Security基础(二):SELinux安全防护、加密与解密应用、扫描与抓包分析

    一.SELinux安全防护 目标: 本案例要求熟悉SELinux防护机制的开关及策略配置,完成以下任务: 将Linux服务器的SELinux设为enforcing强制模式 在SELinux启用状态下, ...

  6. C#加密与解密(DES\RSA)学习笔记

    本笔记摘抄自:https://www.cnblogs.com/skylaugh/archive/2011/07/12/2103572.html,记录一下学习过程以备后续查用. 数据加密技术是网络中最基 ...

  7. Java_DES 加密和解密源码

    Java密码学结构设计遵循两个原则: 1) 算法的独立性和可靠性. 2) 实现的独立性和相互作用性. 算法的独立性是通过定义密码服务类来获得.用户只需了解密码算法的概念,而不用去关心如何实现这些概念. ...

  8. Java实现文件的加密与解密

    最近在做一个项目,需要将资源文件(包括图片.动画等类型)进行简单的加密后再上传至云上的服务器,而在应用程序中对该资源使用前先将读取到的文件数据进行解密以得到真正的文件信息.此策略的原因与好处是将准备好 ...

  9. Java Base64加密、解密原理Java代码

    Java Base64加密.解密原理Java代码 转自:http://blog.csdn.net/songylwq/article/details/7578905 Base64是什么: Base64是 ...

  10. JS URL 使用base64加密与解密

    JS编码方式: <script type="text/javascript"> document.write(encodeURI("http://www.w3 ...

随机推荐

  1. [SWPUCTF 2021 新生赛]no_wakeup

    [SWPUCTF 2021 新生赛]no_wakeup 考点 反序列化 一.题目 打开题目发现如下代码 <?php header("Content-type:text/html;cha ...

  2. 什么是 Spring?为什么学它?

    前言 欢迎来到本篇文章!在这里,我将带领大家快速学习 Spring 的基本概念,并解答两个关键问题:什么是 Spring,以及为什么学习 Spring. 废话少说,下面,我们开始吧! Spring 官 ...

  3. 使用android studio发布android与flutter混合开发项目

    一.生成androd签名证书 Android studio的Build > Grenerate Signed Bundle 选择apk 点击Create New 录入对应的签名信息:点击确定 对 ...

  4. ggplot2 调整绘图区域大小

    熟悉 R 绘图的朋友肯定知道,在普通绘图中,图片的大小可以直接在 png() 和 pdf() 中指定,而绘图区大小则可以用 par() 中的 mar 或 mai 来指定.但是在 ggplot2 中,图 ...

  5. P8933 [JRKSJ R7] 技巧性的块速递推 题解

    题目传送门 题意: 简单来说就是一个涂色游戏. 有一个 n×m 的棋盘需要涂色. 每格只能涂黑色或白色两种颜色. 横.竖.斜连续 3 格颜色不能相同. 横.竖.斜连续 4 格颜色不能有 3 个相同颜色 ...

  6. 洛谷 P7579 「RdOI R2」称重(weigh) 题解

    题意: 题目 一道交互题. 有 n 个球,里面有两个假球,假球比普通球的要轻,每次可以询问任意两组球的轻重关系,第一组轻为 < ,第二组轻为 > ,一样重量为 = . 思路: 先考虑在一个 ...

  7. 尚医通day09-【用户平台搭建详细步骤】(内附源码)

    页面预览 首页 医院详情 第01章-服务器端渲染和客户端渲染 1.搜索引擎优化 1.1.什么是搜索引擎优化 SEO 是网站为了获得更多的流量,对网站的结构及内容进行调整和优化,以便搜索引擎 (百度,g ...

  8. VSCode中打开NodeJS项目自动切换对应版本的配置

    这几年搞了不少静态站点,有的是Hexo的,有的是VuePress的.由于不同的主题对于NodeJS的版本要求不同,所以本机上不少NodeJS的版本. 关于如何管理多个NodeJS版本,很早之前就写过用 ...

  9. Mysql基础6-常用数据库函数

    一.字符串函数 1.常见Mysql内置字符串函数 concat(s1,s2,s3,...):字符串拼接,将s1,s2,s3...等拼接成一个字符串 lower(str):将字符串str全部转为小写 u ...

  10. .net 6 winform启动器:调用CMD命令行执行dotnet命令启动.net core程序并将控制台输出日志输出到winform textbox实现实时日志显示

    背景 历史遗留问题,生产车间运行的一个.net core signalr程序使用命令行程序启动,经常由于生产人员误操作将光标停留在控制台上导致程序假死,丢失部分测试数据,车间随便找了一台win10系统 ...