C++ 标准库中缺少的字符串函数:`starts_with` 和 `ends_with`

C++ 标准模板库的 std::string 很好很强大,但是并没有提供判断一个字符串是否以另一个字符串开始/结束的接口。这里为 std::basic_string<charT> 提供这两个接口。

//string_predicate.hpp

#include <string>
namespace std {
template <typename charT>
inline bool starts_with(const basic_string<charT>& big, const basic_string<charT>& small) {
    if (&big == &small) return true;
    const typename basic_string<charT>::size_type big_size = big.size();
    const typename basic_string<charT>::size_type small_size = small.size();
    const bool valid_ = (big_size >= small_size);
    const bool starts_with_ = (big.compare(0, small_size, small) == 0);
    return valid_ and starts_with_;
}

template <typename charT>
inline bool ends_with(const basic_string<charT>& big, const basic_string<charT>& small) {
    if (&big == &small) return true;
    const typename basic_string<charT>::size_type big_size = big.size();
    const typename basic_string<charT>::size_type small_size = small.size();
    const bool valid_ = (big_size >= small_size);
    const bool ends_with_ = (big.compare(big_size - small_size, small_size, small) == 0);
    return valid_ and ends_with_;
}
}  // namespace std

用法:

#include <iostream>
#include <string>

#include "string_predicate.hpp"

int main() {
    std::string compared = "Hello world!";
    std::string start    = "Hello";
    std::string end      = "world!";

    std::cout << std::starts_with(compared, start) << std::endl;
    std::cout << std::ends_with(compared, end) << std::endl;

    std::wstring wcompared = L"你好世界";
    std::wstring wstart    = L"你好";
    std::wstring wend      = L"世界";

    std::cout << std::starts_with(wcompared, wstart) << std::endl;
    std::cout << std::ends_with(wcompared, wend) << std::endl;

    return 0;
}

结果:

$ g++ test.cc
$ ./a.out
1
1
1
1

转载自: https://liam0205.me/2017/12/14/the-missing-starts-with-and-ends-with-in-Cpp-for-std-string/

Linux统计文件夹下的文件数目

Linux下有三个命令:lsgrepwc。通过这三个命令的组合可以统计目录下文件及文件夹的个数。

  • 统计当前目录下文件的个数(不包括目录)
$ ls -l | grep "^-" | wc -l
  • 统计当前目录下文件的个数(包括子目录)
$ ls -lR| grep "^-" | wc -l
  • 查看某目录下文件夹(目录)的个数(包括子目录)
ls -lR | grep "^d" | wc -l

命令解析:

  • ls -l

长列表输出该目录下文件信息(注意这里的文件是指目录、链接、设备文件等),每一行对应一个文件或目录,ls -lR是列出所有文件,包括子目录。

  • grep "^-"
    过滤ls的输出信息,只保留一般文件,只保留目录是grep "^d"
  • wc -l
    统计输出信息的行数,统计结果就是输出信息的行数,一行信息对应一个文件,所以就是文件的个数。

Linux 命令行下下载 Google Drive/Docs

转载自 https://onebox.site/archives/250.html

说到如何在Linux命令行下下载Google网盘(云端硬盘)的文件,第一个想到的应该是gdriveprasmussen/gdrive),这个脚本可以下载、上传、同步等功能,当然需要事先命令gdrive about关联网盘,显然适合自己使用。

如果说要下载别人分享的文件呢?直接wget命令会多次跳转,可能会导致下载失败。找到个脚本(circulosmeos/gdown.pl)可以实现正常下载。

获取 gdown.pl 脚本

项目地址在GitHub:https://github.com/circulosmeos/gdown.pl

环境需求:

  • wget
  • perl

获取脚本:

wget https://raw.githubusercontent.com/circulosmeos/gdown.pl/master/gdown.pl
chmod +x gdown.pl

注意事项:

获取脚本后根据perlPATH可能需要修改gdown.pl文件的第一行,脚本第一行以下:

#!/usr/local/bin/perl

而我系统环境使用命令whereis perl显示:

perl: /usr/bin/perl /etc/perl /usr/share/perl /usr/share/man/man1/perl.1.gz

gdown.pl文件的第一行,我修改为:

#!/usr/bin/perl

我这里通过修改脚本,或者也可以通过修改系统的环境变量。

如何使用 gdown.pl 脚本下载

如何使用:

./gdown.pl 'Gdrive 文件地址' ['保存的文件名']

例如我要下载https://docs.google.com/uc?id=0B3X9GlR6EmbnQ0FtZmJJUXEyRTA&export=download,文件存为gdrive,可以使用如下命令:

./gdown.pl 'https://docs.google.com/uc?id=0B3X9GlR6EmbnQ0FtZmJJUXEyRTA&export=download' gdrive

gdown.pl

完整的脚本(v1.1 by circulosmeos 01-2017):

#!/usr/local/bin/perl
#
# Google Drive direct download of big files
# ./gdown.pl 'gdrive file url' ['desired file name']
#
# v1.0 by circulosmeos 04-2014.
# v1.1 by circulosmeos 01-2017.
# http://circulosmeos.wordpress.com/2014/04/12/google-drive-direct-download-of-big-files
# Distributed under GPL 3 (http://www.gnu.org/licenses/gpl-3.0.html)
#
use strict;

my $TEMP='gdown.cookie.temp';
my $COMMAND;
my $confirm;
my $check;
sub execute_command();

my $URL=shift;
die "\n./gdown.pl 'gdrive file url' [desired file name]\n\n" if $URL eq '';

my $FILENAME=shift;
$FILENAME='gdown' if $FILENAME eq '';

if ($URL=~m#^https?://drive.google.com/file/d/([^/]+)#) {
    $URL="https://docs.google.com/uc?id=$1&export=download";
}

execute_command();

while (-s $FILENAME < 100000) { # only if the file isn't the download yet
    open fFILENAME, '<', $FILENAME;
    $check=0;
    foreach (<fFILENAME>) {
        if (/href="(\/uc\?export=download[^"]+)/) {
            $URL='https://docs.google.com'.$1;
            $URL=~s/&/&/g;
            $confirm='';
            $check=1;
            last;
        }
        if (/confirm=([^;&]+)/) {
            $confirm=$1;
            $check=1;
            last;
        }
        if (/"downloadUrl":"([^"]+)/) {
            $URL=$1;
            $URL=~s/\\u003d/=/g;
            $URL=~s/\\u0026/&/g;
            $confirm='';
            $check=1;
            last;
        }
    }
    close fFILENAME;
    die "Couldn't download the file :-(\n" if ($check==0);
    $URL=~s/confirm=([^;&]+)/confirm=$confirm/ if $confirm ne '';

    execute_command();
}

unlink $TEMP;

sub execute_command() {
    $COMMAND="wget --no-check-certificate --load-cookie $TEMP --save-cookie $TEMP \"$URL\"";
    $COMMAND.=" -O \"$FILENAME\"" if $FILENAME ne '';
    `$COMMAND`;
    return 1;
}

ubuntu 搭建S3服务 mino单机和集群集群

搭建mino 集群

单机版

# 新建用户
sudo useradd -r minio-user -s /sbin/nologin
sudo chown -R minio-user:minio-user /usr/local/bin/minio
sudo mkdir /usr/local/share/minio
sudo chown -R minio-user:minio-user /usr/local/share/minio
sudo mkdir /etc/minio
sudo chown -R minio-user:minio-user /etc/minio
# 记得替换 MINIO_VOLUMES
cat  /etc/default/minio
# Local export path.
MINIO_VOLUMES="/tmp/minio/"
# Use if you want to run Minio on a custom port.
MINIO_OPTS="--address :443"
EOT
# 替换key
cat  /etc/default/minio
# Access Key of the server.
MINIO_ACCESS_KEY=Server-Access-Key
# Secret key of the server.
MINIO_SECRET_KEY=Server-Secret-Key
EOT
# 下载安装service文件
( cd /etc/systemd/system/; curl -O https://raw.githubusercontent.com/minio/minio-service/master/linux-systemd/minio.service )
# 记得替换 /etc/systemd/system/minio.service
# 中user和group
# 重载配置
systemctl daemon-reload
# 开机启动
systemctl enable minio.service
systemctl disable minio.service

集群版本

# 新建用户
useradd minio-user -s /sbin/nologin
# 记得替换 MINIO_VOLUMES
$ cat  /etc/default/minio
# Remote node configuration.
MINIO_VOLUMES=http://node1/export1 http://node2/export2 http://node3/export3 http://node4/export4
# Use if you want to run Minio on a custom port.
MINIO_OPTS="--address :9199"
EOT
# 替换key
cat  /etc/default/minio
# Access Key of the server.
MINIO_ACCESS_KEY=Server-Access-Key
# Secret key of the server.
MINIO_SECRET_KEY=Server-Secret-Key
EOT
# 下载安装service文件
( cd /etc/systemd/system/; curl -O https://raw.githubusercontent.com/minio/minio-service/master/linux-systemd/distributed/minio.service )
# 记得替换 /etc/systemd/system/minio.service
# 中user和group
# 重载配置
systemctl daemon-reload
# 开机启动
systemctl enable minio.service
systemctl disable minio.service
systemctl restart minio.service
# 查看日志
journalctl -u nginx.service -f 

配置ssl证书

/home/minio/.minio/certs

# 安装acme.sh
 apt-get install libcurl4-openssl-dev
 curl https://get.acme.sh | sh
# 配置好dns
export CF_Email="xxxx"
export CF_Key="xxx”
# 申请ssl
acme.sh --issue --dns dns_cf -d xx.com -d *.xx.com
acme.sh  --installcert  -d  xxx.com   \
        --key-file   /home/minio-user/.minio/certs/private.key \
        --fullchain-file /home/minio-user/.minio/certs/public.crt \
        --reloadcmd  "systemctl restart minio.service"

最后切换到ssl模式

vim /etc/default/minio
# 修改端口
MINIO_OPTS="--address :443"
# 重启服务器
systemctl restart minio.service

简单的GDI操作

简单的GDI操作

窗口程序的本质 :GUI GDI

GDI: Graphics Device Interface,图形设备接口,这是Windows提供的一组用于绘制图像的API
GUI: Graphical User Interface,图形用户界面,是指用户操作软件的界面方式,以区别于字符方式

说白了 GDI是一套实打实的接口,真实存在 GUI只是一个概念

GDI 图像设备接口(Graphics Device Interface)

  1. 设备对象(HWND)
  2. DC(设备上下文,Device Contexts)
  3. 图形对象
图形对象 作用
画笔(Pen) 影响线条,包括颜色、粗细、虚实、箭头形状等
画刷(Brushes) 影响对形状、区域等操作,如使用的颜色、是否有阴影等
字体(Fonts) 影响文字输出的字体
位图(Bitmaps) 影响位图创建、位图操作和保存等。

相关API

  • 上下文相关:

    `GetDC`       =>  获取上下文
    `ReleaseDC`   =>  释放上下文
    
  • 线条相关:

    `MoveToEx/LineTo` => 绘制直线
    `SetPixel/Getpixel`
    
  • 绘制封闭图形

    `Rectangle`   => 绘制矩形
    `Ellipse`     => 绘制圆形
    `RoundRect`   => 绘制圆角矩形
    

demo

https://github.com/luodaoyi/cpp_code/tree/master/MemoryInjectTool/GDI

消息机制与模拟消息

消息机制与模拟消息

消息机制

  • 硬件产生事件
  • 被操作系统捕获,封装成消息
  • 操作系统发送到系统消息队列
  • 再由操作系统由系统消息队列发送到 对应的线程内核对象中的线程消息队列
  • 线程消息队列处理完毕后,将时间送回操作系统
  • 由操作系统调用窗口回调函数 => WindowProc()

线程消息队列 处理

  • GetMessage() => 取出消息
  • DispathMessage() => 把消息再送回系统中系统中

在线程拿到消息之前,消息经过了两个队列

  1. 全局唯一的系统消息队列
  2. 线程内核对象中的线程消息队列

新的消息类型

鼠标按键消息类型


//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//  WM_RBUTTONDOWN - 按下右键
//  WM_LBUTTONDOWN - 按下左键
//  WM_RBUTTONUP - 弹起右键
//  WM_LBUTTONUP - 弹起左键
//
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择:
            switch (wmId)
            {
            case IDM_ABOUT:
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_RBUTTONDOWN :
        MessageBox(hWnd, TEXT("按下右键"), NULL, MB_OK);
        break;
    case WM_LBUTTONDOWN:
        MessageBox(hWnd, TEXT("按下左键"), NULL, MB_OK);
        break;
    case WM_RBUTTONUP:
        MessageBox(hWnd, TEXT("弹起右键"), NULL, MB_OK);
        break;
    case WM_LBUTTONUP:
        MessageBox(hWnd, TEXT("弹起左键"), NULL, MB_OK);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

TranslateMessage API 翻译消息

WM_KEYDOWN 消息 -> 按键按下

https://msdn.microsoft.com/query/dev15.query?appId=Dev15IDEF1&l=ZH-CN&k=k(WINUSER%2FWM_KEYDOWN);k(WM_KEYDOWN);k(DevLang-C%2B%2B);k(TargetOS-Windows)&rd=true;k(WM_KEYDOWN);k(DevLang-C%2B%2B);k(TargetOS-Windows)&rd=true)


#include "stdafx.h"
#include "WindowsMessage.h"
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    TCHAR className[] = TEXT("MyFirstWnd");
    //1 创建窗口类型模板
    WNDCLASS wndclass = { 0 };
    wndclass.lpszClassName = className; //名字
    wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND; //背景画笔句柄
    wndclass.hInstance = hInstance; //窗口过程的实例句柄
    wndclass.lpfnWndProc = WndProc; //窗口过程函数
                                    //2 注册窗口
    RegisterClass(&wndclass);
    //3 创建窗口
    HWND hwindows = CreateWindow(className, TEXT("MYWINDOWS"), WS_OVERLAPPEDWINDOW, 500, 300, 300, 250, NULL, NULL, hInstance, NULL);
    //4 显示窗口
    ShowWindow(hwindows, SW_SHOW);
    //5 构建消息循环
    MSG msg = { NULL };
    while (GetMessage(&msg, NULL, NULL, NULL))
    {
        //调用消息转换 否则有的消息无法捕捉
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}
//
//  函数: WndProc(HWND, UINT, WPARAM, LPARAM)
//
//  目的:    处理主窗口的消息。
//
//  WM_COMMAND  - 处理应用程序菜单
//  WM_PAINT    - 绘制主窗口
//  WM_DESTROY  - 发送退出消息并返回
//  WM_RBUTTONDOWN - 按下右键
//  WM_LBUTTONDOWN - 按下左键
//  WM_RBUTTONUP - 弹起右键
//  WM_LBUTTONUP - 弹起左键
//
//  WM_KEYDOWN - 按键消息
//  WM_CHAR - 按键消息 直接判断anscii码
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_COMMAND:
        {
            int wmId = LOWORD(wParam);
            // 分析菜单选择:
            switch (wmId)
            {
            case IDM_ABOUT:
                break;
            case IDM_EXIT:
                DestroyWindow(hWnd);
                break;
            default:
                return DefWindowProc(hWnd, message, wParam, lParam);
            }
        }
        break;
    case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC hdc = BeginPaint(hWnd, &ps);
            // TODO: 在此处添加使用 hdc 的任何绘图代码...
            EndPaint(hWnd, &ps);
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    case WM_KEYDOWN:
        //要想捕捉到此消息,必须在创建窗口时的第5步构建消息循环的时候 调用TranslateMessage
        //WM_KEYDOWN 的msdn文档中有强调
        if (wParam == 0x42)
        {
            MessageBox(hWnd, TEXT("b"), NULL, MB_OK);
        }
        break;
    case WM_CHAR:
        //此消息也需要 TranslateMessage
        if (wParam == 'a')
        {
            MessageBox(hWnd, TEXT("a"), NULL, MB_OK);
        }
        break;
    case WM_RBUTTONDOWN :
        //MessageBox(hWnd, TEXT("按下右键"), NULL, MB_OK);
        break;
    case WM_LBUTTONDOWN:
        //MessageBox(hWnd, TEXT("按下左键"), NULL, MB_OK);
        break;
    case WM_RBUTTONUP:
        MessageBox(hWnd, TEXT("弹起右键"), NULL, MB_OK);
        break;
    case WM_LBUTTONUP:
        MessageBox(hWnd, TEXT("弹起左键"), NULL, MB_OK);
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

模拟消息api

LRESULT WINAPI SendMessage
(
    HWND hWnd.   //目标窗口句柄
    UINT Msg,    //要发送的消息
    WPARAM wParam, //消息内容
    LPARAM IParam  //消息内容
);
BOOL WINAPI PostMessage
(
    HWND hWnd.   //目标窗口句柄
    UINT Msg,    //要发送的消息
    WPARAM wParam, //消息内容
    LPARAM IParam  //消息内容
)

postMessage和sendMessage的区别

  • 返回时间不同

        `PostMessage`发送消息后就 `立即返回`
        `SendMessage`发送消息后,等待消息`处理函数处理完毕后才返回`
  • 发送的过程不同

    在同一个线程中:
        `PostMessage`发送消息时,消息要`先放入系统消息队列`总
        `SendMessage`发送消息时,由`User`模块调用目标窗口的处理函数处理消息,并将结果返回
    在不同的线程中:
        `PostThreadMessage`代替`PostMessage`指定消息发送给哪一个线程
        `SendMessage`发送消息到目标窗口所属的线程的消息队列中,然后发送消息的线程在`User`模块内监视和等待消息处理,直到目标窗口处理完返回
    

新建一个窗口程序

新建一个窗口程序

消息机制

创建窗口的几个步骤

  1. 创建窗口类
  2. 注册窗口类
  3. 创建窗口
  4. 显示窗口
  5. 构建消息循环

创建过程

demo : https://github.com/luodaoyi/cpp_code/tree/master/MemoryInjectTool/CreateWindows

//1 创建窗口类型模板
    WNDCLASS wndclass = { 0 };
    wndclass.lpszClassName = className; //名字
    wndclass.hbrBackground = (HBRUSH)COLOR_BACKGROUND; //背景画笔句柄
    wndclass.hInstance = hInstance; //窗口过程的实例句柄
    wndclass.lpfnWndProc = WndProc; //窗口过程函数
    //2 注册窗口
    RegisterClass(&wndclass);
    //3 创建窗口
    HWND hwindows = CreateWindow(className, TEXT("MYWINDOWS"), WS_OVERLAPPEDWINDOW, 500, 300, 300, 250, NULL, NULL, hInstance,NULL);
    //创建窗口
    HWND hsitc1 = CreateWindow(L"STATIC", TEXT("账号:"), WS_CHILD | WS_VISIBLE, 10, 30, 50, 20, hwindows, HMENU(IDC_STATIC1), hInstance, NULL);
    HWND hstic2 = CreateWindow(L"STATIC", TEXT("密码"), WS_CHILD | WS_VISIBLE, 10, 80, 50, 20, hwindows, HMENU(IDC_STATIC2), hInstance, NULL);
    HWND hedit1 = CreateWindow(L"EDIT", NULL, WS_CHILD | WS_VISIBLE, 80, 30, 100, 20, hwindows, HMENU(IDC_EDIT1), hInstance, NULL);
    HWND hedit2 = CreateWindow(L"EDIT", NULL, WS_CHILD | WS_VISIBLE, 80, 80, 100, 20, hwindows, HMENU(IDC_EDIT2), hInstance, NULL);
    HWND hbutt1 = CreateWindow(L"BUTTON", TEXT("登录"), WS_CHILD | WS_VISIBLE, 50, 120, 50, 25, hwindows, HMENU(IDC_BUTTM1), hInstance, NULL);
    HWND hbutt2 = CreateWindow(L"BUTTON", TEXT("取消"), WS_CHILD | WS_VISIBLE, 110, 120, 50, 25, hwindows, HMENU(IDC_BUTTM2), hInstance, NULL);
    //4 显示窗口
    ShowWindow(hwindows,SW_SHOW);
    //5 构建消息循环
    MSG msg = { NULL };
    while (GetMessage(&msg,NULL,NULL,NULL))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

office2016 visio2016 project2016零售版转换vol版bat

http://msdn.itellyou.cn/网站上下载的是office2016是零售版的,转换为vol,将下列文本另存为bat文件,运行即可

@ECHO OFF&PUSHD %~DP0
setlocal EnableDelayedExpansion&color 3e & cd /d "%~dp0"
title office2016 retail转换vol版
%1 %2
mshta vbscript:createobject("shell.application").shellexecute("%~s0","goto :runas","","runas",1)(window.close)&goto :eof
:runas
if exist "%ProgramFiles%\Microsoft Office\Office16\ospp.vbs" cd /d "%ProgramFiles%\Microsoft Office\Office16"
if exist "%ProgramFiles(x86)%\Microsoft Office\Office16\ospp.vbs" cd /d "%ProgramFiles(x86)%\Microsoft Office\Office16"
:WH
cls
echo.
echo                         选择需要转化的office版本序号
echo.
echo --------------------------------------------------------------------------------
echo                 1. 零售版 Office Pro Plus 2016 转化为VOL版
echo.
echo                 2. 零售版 Office Visio Pro 2016 转化为VOL版
echo.
echo                 3. 零售版 Office Project Pro 2016 转化为VOL版
echo.
echo. --------------------------------------------------------------------------------
set /p tsk="请输入需要转化的office版本序号【回车】确认(1-3): "
if not defined tsk goto:err
if %tsk%==1 goto:1
if %tsk%==2 goto:2
if %tsk%==3 goto:3
:err
goto:WH
:1
cls
echo 正在重置Office2016零售激活...
cscript ospp.vbs /rearm
echo 正在安装 KMS 许可证...
for /f %%x in ('dir /b ..\root\Licenses16\proplusvl_kms*.xrm-ms') do cscript ospp.vbs /inslic:"..\root\Licenses16\%%x" >nul
echo 正在安装 MAK 许可证...
for /f %%x in ('dir /b ..\root\Licenses16\proplusvl_mak*.xrm-ms') do cscript ospp.vbs /inslic:"..\root\Licenses16\%%x" >nul
echo 正在安装 KMS 密钥...
cscript ospp.vbs /inpkey:XQNVK-8JYDB-WJ9W3-YJ8YR-WFG99
goto :e
:2
cls
echo 正在重置Visio2016零售激活...
cscript ospp.vbs /rearm
echo 正在安装 KMS 许可证...
for /f %%x in ('dir /b ..\root\Licenses16\visio???vl_kms*.xrm-ms') do cscript ospp.vbs /inslic:"..\root\Licenses16\%%x" >nul
echo 正在安装 MAK 许可证...
for /f %%x in ('dir /b ..\root\Licenses16\visio???vl_mak*.xrm-ms') do cscript ospp.vbs /inslic:"..\root\Licenses16\%%x" >nul
echo 正在安装 KMS 密钥...
cscript ospp.vbs /inpkey:PD3PC-RHNGV-FXJ29-8JK7D-RJRJK
goto :e
:3
cls
echo 正在重置Project2016零售激活...
cscript ospp.vbs /rearm
echo 正在安装 KMS 许可证...
for /f %%x in ('dir /b ..\root\Licenses16\project???vl_kms*.xrm-ms') do cscript ospp.vbs /inslic:"..\root\Licenses16\%%x" >nul
echo 正在安装 MAK 许可证...
for /f %%x in ('dir /b ..\root\Licenses16\project???vl_mak*.xrm-ms') do cscript ospp.vbs /inslic:"..\root\Licenses16\%%x" >nul
echo 正在安装 KMS 密钥...
cscript ospp.vbs /inpkey:YG9NW-3K39V-2T3HJ-93F3Q-G83KT
goto :e
:e
echo.
echo 转化完成,按任意键退出!
pause >nul
exit

模块隐藏

模块隐藏

遍历模块

HANDLE WINAPI CreateToolhelp32Snapshot(
    DWORD dwFlags,
    DWORD th32ProcessID
)
//dwFlags:
TH32CS_SNAPMODULE   遍历模块

PEB

(Process Environment Block 进程环境块)
存放进程信息,每个进程都有自己的PEB信息

如何找到PEB

  1. EPROCESS进程内核对象结构体中 0x1b0偏移的位置
  2. TEB线程环境块的 0x30位置

(实际上32位程序中的PEB的位置是固定的)

TEB

(Thread EnvironmentBlock 线程环境块)
系统在此TEB种保存频繁使用的线程相关的数据

如何找到TEB:
段寄存器fs中记录了TEB的存储位置

TEB偏移0X30的位置记录了TEB的地址

可以通过以下汇编来获取

MOV EAX,FS:[0x30]
//获得PEB
PEB_LDR_DATA* pPebLdrData=NULL;
_asm
{
    mov eax,fs:[0x30]
    mov ecx,[eax+0x0c]
    mov pPebLdrData,ecx
}
//获得LDR_DATA
pPebLdrData = (PEB_LDR_DATA*)*(_NT_PEB + 0x0c)