c++ - 动态载入DLL接口,可以给IDA静态分析增加一点麻烦

文章目录

    • c++ - 动态载入DLL接口,可以给IDA静态分析增加一点麻烦
    • 概述
    • 笔记
    • 测试工程
    • test_load_dll_then_call_api.cpp
    • CMyUser32Dll.h
    • CMyUser32Dll.cpp
    • LateLoad.h
    • IDA静态分析
    • 引入表中没有PostMessageW
    • 字符串查找能找到PostMessageW
    • 备注
    • 看看CMyUser32Dll.h编译完的样子
    • 备注 - debug版不生成调试信息
    • debug版即使不生成调试信息,也可能带入一些和工程目录相关的字符串
    • 解决方法1
    • 解决方法2
    • 备注
    • END

c++ - 动态载入DLL接口,可以给IDA静态分析增加一点麻烦

概述

工程中用到了PostMessage来压入任务,太扎眼了。在IDA静态分析时,看API引用,很容易看到压入任务的实现。
想动态载入API, 看看静态分析效果如何?

笔记

网上有大神开源了一个动态载入DLL的封装类(http://www.codeproject.com/KB/DLL/LateLoad.aspx)

以前也用过,只是用来方便动态调用DLL接口。
这次用,主要是看看用IDA静态分析时,动态载入的API接口是否容易识别?

测试工程

写个demo(vs2019 vc++ console), 转圈打印字符串. 如果圈数>10, 向控制台程序的窗口post消息,关闭控制台程序的窗口,从而达到关闭控制台程序的目的。
在这里插入图片描述

test_load_dll_then_call_api.cpp

//! \file test_load_dll_then_call_api.cpp#include <windows.h>
#include <stdlib.h>
#include <stdio.h>#include "CMyUser32Dll.h"int main()
{int i = 0;CMyUser32Dll myUser32Dll;do {Sleep(500);printf("[%d] runing\n", ++i);if (i > 10){// 发送消息,关掉控制台程序的窗口myUser32Dll.PostMessageW(GetConsoleWindow(), WM_CLOSE, 0, 0);}} while (true);return EXIT_SUCCESS;
}

CMyUser32Dll.h

//! \file CMyLoadDll.h#ifndef __C_MY_LOAD_DLL_H__
#define __C_MY_LOAD_DLL_H__#include "LateLoad.h"// User32.dll
// https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-postmessagea?redirectedfrom=MSDN&devlangs=cpp&f1url=%3FappId%3DDev16IDEF1%26l%3DZH-CN%26k%3Dk(WINUSER%252FPostMessage)%3Bk(PostMessage)%3Bk(DevLang-C%252B%252B)%3Bk(TargetOS-Windows)%26rd%3Dtrue
/*
BOOL PostMessageA([in, optional] HWND   hWnd,[in]           UINT   Msg,[in]           WPARAM wParam,[in]           LPARAM lParam
);
*/
// https://learn.microsoft.com/zh-cn/windows/win32/api/winuser/nf-winuser-postmessagew
/*
BOOL PostMessageW([in, optional] HWND   hWnd,[in]           UINT   Msg,[in]           WPARAM wParam,[in]           LPARAM lParam
);
*/LATELOAD_BEGIN_CLASS(CMyUser32Dll, TEXT("User32.dll"), FALSE, FALSE)
LATELOAD_FUNC_4(FALSE, BOOL, STDAPICALLTYPE, PostMessageW, HWND, UINT, WPARAM, LPARAM)
LATELOAD_END_CLASS()#endif // #ifndef __C_MY_LOAD_DLL_H__

CMyUser32Dll.cpp

//! \file CMyLoadDll.cpp#include "pch.h"
// #include "memOpt/my_debug_new_define.h"#include "CMyUser32Dll.h"

LateLoad.h

#ifndef __LATE_LOAD__H__
#define __LATE_LOAD__H__#if _MSC_VER > 1000
#pragma once
#endif#include <Windows.h>
#include <assert.h>/*Module : LateLoad.hPurpose: I was tired of constantly re-writing LoadLibrary & GetProcAddress code, over & over & over again.  So I created a series of macros that will define & create a wrapper class to do it all for me.This is NOT an end-all-be-all solution for loading DLLs.  This is just ahandy lightweight helper when you would use LoadLibrary/GetProcAddress.For more information on DLLs, check out MSDN.This was inspired by my own need, as well as a nice little MSDN article titled "DLLs the Dynamic Way" by "MicHael Galkovsky"http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dndllpro/html/dlldynamic.aspUnfortunatly, there was no source to go with the article. So, I wrote this version quite a bit differently.Author : Copyright (c) 2004 by Jason De Arte, All Rights Reserved.Site   : http://1001010.com/code/	         License: Use it however you like as a compiled portion of your product(s).  Don't be a jerk by claiming the it as your own.Usage  : The following will declare the class "CUser32Wrapper" that will loadfunction pointer(s) from USER32.DLL.  Those function names will become member function(s) of the newly created class.LATELOAD_BEGIN_CLASS(CUser32Wrapper,user32,FALSE)LATELOAD_FUNC_0(NULL,HCURSOR,WINAPI,GetCursor)LATELOAD_FUNC_1(NULL,HANDLE,WINAPI,NonExistantFunction,BOOL)LATELOAD_END_CLASS()// The above statements will auto-generate a class that functionaly equivalent to...class CUser32Wrapper : public CLateLoadBase{public:BOOL Is_GetCursor();HCURSOR WINAPI GetCursor();BOOL Is_NonExistantFunction();HANDLE WINAPI NonExistantFunction();};// And usage is even easierCUser32Wrapper uw;HCURSOR h = uw.GetCursor();// And for DLLs missing a function export, you can still use the member function!uw.NonExistantFunction(true);// It will just return the default non-existant value in the definition// To determine if the funtion was imported, use the "BOOL Is_<FunctionName>();" memberIs_NonExistantFunction();// If it exists, it will return TRUE.  False if it does not exist-------------------------------------------Let's look at the various defines that are used...LATELOAD_BEGIN_CLASS(CUser32Wrapper,user32,FALSE)CUser32Wrapper - the class that will be createduser32 - the library that will be loaded FALSE - determines if "FreeLibrary()" will be called in the destructor.LATELOAD_FUNC_0(NULL,HCURSOR,WINAPI,GetCursor)NULL - if the ProcAddress was not loaded, this value is returned in the call to that funcHCURSOR - the return type for the functionWINAPI - function calling conventionGetCursor - the exported functionLATELOAD_FUNC_1 (up to _9)Identical to LATELOAD_FUNC_0, except it allows 1-9 parameters to the functionLATELOAD_FUNC_0_ (up to _9)Same as before, but for use with NULL return typesLATELOAD_END_CLASS()finalizes the class declaration-------------------------------------------History:2004.Feb.29.JED - Created
*///
// Used for keeping track
//
enum ImportedProcState
{ipsUnknown = 0,  // No attempts to load the Proc have been made yetipsMissing,      // GetProcAddress failed to find the exported functionipsAvailable,    // the Proc is Ready & Available for useips_MAX
};#ifndef NO_VTABLE
# if _MSC_VER > 1000
#  define NO_VTABLE __declspec(novtable)
# else
// 2004.Feb.29.JED - Note to self, find out what is appropriate under non-msvc compilers
//#  define NO_VTABLE
# endif //_MSC_VER
#endif//
// Declaration for the utility base class, not intended for non-inherited use.
//
class NO_VTABLE CLateLoadBase
{//// Why no v-table?// 1) not needed// 2) reduces code size// 3) LATELOAD_BEGIN_CLASS calls ZeroMemory to blank out all the derived function //    pointers & ImportedProcState memeber variables - in the process the vtable//    will get blanked out.// 4) This class is not intended to be instantiated on its own.// 5) Makes it so that using this class on its own will cause an Access Violation//
protected:HMODULE  m_module;          // Handle to the DLLDWORD    m_dwLoadLibError;  // If there was an error from LoadLibraryBOOL     m_bManaged;        // FreeLibrary in the destructor?LPTSTR   m_pszModule;       // The name of the module, handy for first-use loading/****************************************************************************** NAME: *  CLateLoadBase::dll_LoadLibrary* * DESCRIPTION: *  Loads the dll.  * *******************************************************************************/
protected:BOOL dll_LoadLibrary(LPCTSTR pszLibrary, BOOL bDoItNow){//// Make a record of the DLL name//if( !m_pszModule ){if( pszLibrary && *pszLibrary){int nLen = lstrlen(pszLibrary); m_pszModule = new TCHAR[nLen+2]; if( m_pszModule ) { ZeroMemory( m_pszModule, sizeof(TCHAR)*(nLen+2) ); lstrcpy(m_pszModule,pszLibrary); } else { assert(!"CLateLoadBase::dll_LoadLibrary - Unable to allocate memory for a string!"); }} else { assert(!"CLateLoadBase::dll_LoadLibrary - We need a valid pszLibrary string"); } }// Nothing to do?if( m_module )return TRUE;// Should we do this later?if( !bDoItNow )return FALSE;// Load the DLLm_dwLoadLibError = 0;m_module = ::LoadLibrary(pszLibrary);if( m_module )return TRUE;// Unable to load it, find out whym_dwLoadLibError = GetLastError();// We rely on the last error being not equal to zero on LoadLibrary failure.// So, in the off chance it IS zero, we set it to a non-zero value.  // Tip: According to MSDN, Bit 29 in Get/SetLastError logic is reserved for Application //      defined error codes.  No system level error codes should have this bit set.if( !m_dwLoadLibError ){assert(!"Wow, that should NOT have happened!  Could you tell JED how you did it?");m_dwLoadLibError = 0x20000000;}return FALSE;}/****************************************************************************** NAME: *  CLateLoadBase::dll_GetProcAddress* * DESCRIPTION: *  Loads the function pointer from the DLL* *******************************************************************************/
protected:FARPROC dll_GetProcAddress(LPCSTR pszFunctionName, ImportedProcState& ips){FARPROC pfn = NULL;ips = ipsUnknown;// Does the DLL still need to be loaded?if( !m_module && m_pszModule && *m_pszModule && 0 == m_dwLoadLibError   // There is no reason to attempt to load the DLL more than once){dll_LoadLibrary(m_pszModule,TRUE);}if( m_module ){		pfn = ::GetProcAddress(m_module,pszFunctionName);if( pfn )ips = ipsAvailable;elseips = ipsMissing;}return pfn; }/****************************************************************************** NAME: *  CLateLoadBase::~CLateLoadBase* * DESCRIPTION: *  Description goes here...* *******************************************************************************/
public:~CLateLoadBase(){if( m_bManaged && m_module )::FreeLibrary(m_module);m_module  = NULL;m_bManaged = FALSE;if( m_pszModule )delete m_pszModule;m_pszModule = NULL;}/****************************************************************************** NAME: *  CLateLoadBase::dll_GetLoadError* * DESCRIPTION: *  If LoadLibrary failed, returns the error code.*  If there was no error, returns zero* *******************************************************************************/
public:DWORD dll_GetLoadError()  {return m_dwLoadLibError;     }/****************************************************************************** NAME: *  CLateLoadBase::dll_IsLoaded* * DESCRIPTION: *  Is the DLL loaded?* *******************************************************************************/
public:BOOL dll_IsLoaded()      { return NULL!=m_module;       }/****************************************************************************** NAME: *  CLateLoadBase::dll_GetModuleName* * DESCRIPTION: *  return the name of the module loaded* *******************************************************************************/
public:LPCTSTR dll_GetModuleName() { return (LPCTSTR)m_pszModule; }
};//--------------------------------------------------------------------------------------
//
// Start of macros
//
//--------------------------------------------------------------------------------------//
// Start, Declares the name of the class
//
// ClassName   = the name of the class
// ModuleName  = the DLL name
// bLoadDllNow = if true, the DLL will be loaded in the constructor.  
//               if false, it will ONLY be loaded when any bound function is first used
// bManaged    = if true, FreeLibrary will be called in the destructor
//
#define LATELOAD_BEGIN_CLASS(ClassName,ModuleName,bLoadDllNow,bManaged) \
class ClassName : public CLateLoadBase \
{ \
public:\ClassName()\{\/*Automagicaly blank out all the function pointers and ImportedProcState member vars*/ \/*that will be declared in following LATELOAD_FUNC_* declarations, very handy. */ \ZeroMemory(static_cast<ClassName*>(this),sizeof(ClassName)); \m_bManaged = bManaged; \/*and load the DLL*/ \dll_LoadLibrary(ModuleName,bLoadDllNow); \}//
// Function Declaration, Zero Parameters, returns a value 
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType,  type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
//
// A function prototype that looked like...
//   typedef BOOL (CALLBACK* SOMETHING)();
//  or
//   BOOL CALLBACK Something();
//
// Would be changed to...
//   LATELOAD_FUNC_0(0,BOOL,CALLBACK,Something)
//
// If "Something" could not be loaded, and it was called - it would return 0
//
#define LATELOAD_FUNC_0(ErrorResult,ReturnType,CallingConv,FuncName) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName() \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(); \}//
// Function Declaration, One Parameter, returns a value 
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType,  type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
// A function prototype that looked like...
//   typedef BOOL (CALLBACK* SOMETHING)(BOOL);
//  or
//   BOOL CALLBACK Something(BOOL bEnable);
//
// Would be changed to...
//   LATELOAD_FUNC_1(0,BOOL,CALLBACK,Something,BOOL)
//
// If "Something" could not be loaded, and it was called - it would return 0
//
#define LATELOAD_FUNC_1(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1); \}//
// Function Declaration, Two Parameters, returns a value 
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType,  type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
// A function prototype that looked like...
//   typedef BOOL (CALLBACK* SOMETHING)(BOOL,INT);
//  or
//   BOOL CALLBACK Something(BOOL bEnable, INT nNum);
//
// Would be changed to...
//   LATELOAD_FUNC_2(0,BOOL,CALLBACK,Something,BOOL,INT)
//
// If "Something" could not be loaded, and it was called - it would return 0
//
#define LATELOAD_FUNC_2(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2); \}//
// Function Declaration, Three Parameters, returns a value 
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType,  type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_3(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3); \}//
// Function Declaration, Four Parameters, returns a value 
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType,  type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_4(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3,p4); \}//
// Function Declaration, Five Parameters, returns a value 
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType,  type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_5(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3,p4,p5); \}//
// Function Declaration, Six Parameters, returns a value 
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType,  type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_6(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3,p4,p5,p6); \}//
// Function Declaration, Seven Parameters, returns a value 
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType,  type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_7(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6,ParamType7 p7) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3,p4,p5,p6,p7); \}//
// Function Declaration, Eight Parameters, returns a value 
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType,  type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_8(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6,ParamType7 p7,ParamType8 p8) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3,p4,p5,p6,p7,p8); \}//
// Function Declaration, Nine Parameters, returns a value 
//
// ErrorResult, Default return value if the function could not be loaded & it is called anyways
// ReturnType,  type of value that the function returns
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_9(ErrorResult,ReturnType,CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8,ParamType9) \
protected: \typedef ReturnType(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8,ParamType9); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \ReturnType FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6,ParamType7 p7,ParamType8 p8,ParamType9 p9) \{\if( !Is_##FuncName() ) \return ErrorResult; \return m_pf##FuncName(p1,p2,p3,p4,p5,p6,p7,p8,p9); \}//
// Function Declaration, Zero Parameters, returns nothing 
//
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
//
// A function prototype that looked like...
//   typedef VOID (CALLBACK* SOMETHING)();
//  or
//   VOID CALLBACK Something();
//
// Would be changed to...
//   LATELOAD_FUNC_0_VOID(CALLBACK,Something)
//
#define LATELOAD_FUNC_0_VOID(CallingConv,FuncName) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName() \{\if( Is_##FuncName() ) \m_pf##FuncName(); \}//
// Function Declaration, One Parameters, returns nothing 
//
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
// A function prototype that looked like...
//   typedef VOID (CALLBACK* SOMETHING)(BOOL);
//  or
//   VOID CALLBACK Something(BOOL bEnable);
//
// Would be changed to...
//   LATELOAD_FUNC_1_VOID(CALLBACK,Something,BOOL)
//
#define LATELOAD_FUNC_1_VOID(CallingConv,FuncName,ParamType1) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1); \}//
// Function Declaration, Two Parameters, returns nothing 
//
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
// A function prototype that looked like...
//   typedef VOID (CALLBACK* SOMETHING)(BOOL,INT);
//  or
//   VOID CALLBACK Something(BOOL bEnable, INT nNumber);
//
// Would be changed to...
//   LATELOAD_FUNC_2_VOID(CALLBACK,Something,BOOL,INT)
//
#define LATELOAD_FUNC_2_VOID(CallingConv,FuncName,ParamType1,ParamType2) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2); \}//
// Function Declaration, Three Parameters, returns nothing 
//
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_3_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3); \}//
// Function Declaration, Four Parameters, returns nothing 
//
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_4_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3,p4); \}//
// Function Declaration, Five Parameters, returns nothing 
//
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_5_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3,p4,p5); \}//
// Function Declaration, Six Parameters, returns nothing 
//
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_6_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3,p4,p5,p6); \}//
// Function Declaration, Seven Parameters, returns nothing 
//
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_7_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6,ParamType7 p7) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3,p4,p5,p6,p7); \}//
// Function Declaration, Eight Parameters, returns nothing 
//
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_8_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6,ParamType7 p7,ParamType8 p8) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3,p4,p5,p6,p7,p8); \}//
// Function Declaration, Nine Parameters, returns nothing 
//
// CallingConv, Calling convention of the function
// FuncName,    Name of the function
// ParamN       types of the function parameters
//
#define LATELOAD_FUNC_9_VOID(CallingConv,FuncName,ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8,ParamType9) \
protected: \typedef void(CallingConv * TYPE_##FuncName)(ParamType1,ParamType2,ParamType3,ParamType4,ParamType5,ParamType6,ParamType7,ParamType8,ParamType9); \TYPE_##FuncName m_pf##FuncName; \ImportedProcState m_ips##FuncName;\
public: \BOOL Is_##FuncName() \{ \if(ipsUnknown == m_ips##FuncName) \m_pf##FuncName = (TYPE_##FuncName)dll_GetProcAddress(#FuncName, m_ips##FuncName); \return(ipsAvailable == m_ips##FuncName); \} \void FuncName(ParamType1 p1,ParamType2 p2,ParamType3 p3,ParamType4 p4,ParamType5 p5,ParamType6 p6,ParamType7 p7,ParamType8 p8,ParamType9) \{\if( Is_##FuncName() ) \m_pf##FuncName(p1,p2,p3,p4,p5,p6,p7,p8,p9); \}//
// End of the class
//
#define LATELOAD_END_CLASS()  };#endif //__LATE_LOAD__H__

IDA静态分析

引入表中没有PostMessageW

在这里插入图片描述

字符串查找能找到PostMessageW

在这里插入图片描述
在这里插入图片描述
可以顺着字符串,找到动态载入API的实现。
在这里插入图片描述
rcx是类指针this
[rcx + 0x18]就是保存PostMessageW函数地址的指针变量
在这里插入图片描述

在IDA中搜索文本18h, 就能找到动态调用postMessageW的调用点。
在这里插入图片描述
如果程序大了,想知道API的动态调用点,应该是挺不好找的。
如果将字符串编码后,需要用到的时候,再解码来用,那么调试者就只能动态调试才能找到思路了。
如果这样的话,唯一不能隐藏的就是LoadLibary和GetProcAddress。剩下2这个API无所谓,因为壳程序人家也是留下这2个API的,如果人家愿意调试你的程序,也挡不住人家。

备注

如果在程序中动态载入API, 可以给IDA静态分析增加一点麻烦。
虽然麻烦增加的不多,但是有点用。

看看CMyUser32Dll.h编译完的样子

CMyUser32Dll.h是包含在main.cpp中,进行宏展的。
编译main.cpp, 就能看到CMyUser32Dll.h中宏展开的样子。
在这里插入图片描述
在这里插入图片描述
整理 test_load_dll_then_call_api.i

// test_load_dll_then_call_api.cpp的宏展开的整理enum ImportedProcState
{ipsUnknown = 0,  ipsMissing,      ipsAvailable,    ips_MAX
};class __declspec(novtable) CLateLoadBase
{
protected:HMODULE  m_module;          DWORD    m_dwLoadLibError;  BOOL     m_bManaged;        LPTSTR   m_pszModule;       protected:BOOL dll_LoadLibrary(LPCTSTR pszLibrary, BOOL bDoItNow){if( !m_pszModule ){if( pszLibrary && *pszLibrary){int nLen = lstrlenW(pszLibrary); m_pszModule = new TCHAR[nLen+2]; if( m_pszModule ) { memset((m_pszModule),0,(sizeof(TCHAR)*(nLen+2))); lstrcpyW(m_pszModule,pszLibrary); } else { assert(false);}} else { assert(false);} }if( m_module )return 1;if( !bDoItNow )return 0;m_dwLoadLibError = 0;m_module = ::LoadLibraryW(pszLibrary);if( m_module )return 1;m_dwLoadLibError = GetLastError();if( !m_dwLoadLibError ){assert(false);m_dwLoadLibError = 0x20000000;}return 0;}protected:FARPROC dll_GetProcAddress(LPCSTR pszFunctionName, ImportedProcState& ips){FARPROC pfn = 0;ips = ipsUnknown;if( !m_module && m_pszModule && *m_pszModule && 0 == m_dwLoadLibError   ){dll_LoadLibrary(m_pszModule,1);}if( m_module ){		pfn = ::GetProcAddress(m_module,pszFunctionName);if( pfn )ips = ipsAvailable;elseips = ipsMissing;}return pfn; }
public:~CLateLoadBase(){if( m_bManaged && m_module )::FreeLibrary(m_module);m_module  = 0;m_bManaged = 0;if( m_pszModule )delete m_pszModule;m_pszModule = 0;}public:DWORD dll_GetLoadError()  {return m_dwLoadLibError;     }public:BOOL dll_IsLoaded()      { return 0!=m_module;       }public:LPCTSTR dll_GetModuleName() { return (LPCTSTR)m_pszModule; }
};class CMyUser32Dll : public CLateLoadBase 
{ 
public: CMyUser32Dll() { memset((static_cast<CMyUser32Dll*>(this)),0,(sizeof(CMyUser32Dll))); m_bManaged = 0; dll_LoadLibrary(L"User32.dll",0); }protected: typedef BOOL(__stdcall * TYPE_PostMessageW)(HWND,UINT,WPARAM,LPARAM); TYPE_PostMessageW m_pfPostMessageW; ImportedProcState m_ipsPostMessageW;
public: BOOL Is_PostMessageW(){ if(ipsUnknown == m_ipsPostMessageW) m_pfPostMessageW = (TYPE_PostMessageW)dll_GetProcAddress("PostMessageW", m_ipsPostMessageW); return(ipsAvailable == m_ipsPostMessageW); } BOOL PostMessageW(HWND p1,UINT p2,WPARAM p3,LPARAM p4) { if( !Is_PostMessageW() )return 0; return m_pfPostMessageW(p1,p2,p3,p4); }
};int main()
{int i = 0;CMyUser32Dll myUser32Dll;do {Sleep(500);printf("[%d] runing\n", ++i);if (i > 10){myUser32Dll.PostMessageW(GetConsoleWindow(), 0x0010, 0, 0);}} while (true);return 0;
}

备注 - debug版不生成调试信息

VS2019工程的debug版默认是生成.pdb的, 如果到了发行阶段,又不想生成release版(以前听说过,有的软件发行时,如果生成release版就会报错,厂家又解决不了,只能以debug版来发行),那么可以在编译选项中去掉调试信息,这样就不会生成pdb了。
在这里插入图片描述

debug版即使不生成调试信息,也可能带入一些和工程目录相关的字符串

在这里插入图片描述
这些和工程目录相关的字符串的引入,看IDA的字符串引用,是和debug版的new, assert等用到了__FILE__, __LINE__有关系。

解决方法1

将工程目录模拟成磁盘,在磁盘根目录下生成发布版(即使是debug), 这样,在工程中留下的字符串,就比较短。也不那么敏感。只和工程本身相关。如果在客户那里出了问题,就能看到断言,用户截个图就大概知道问题在哪里。
在这里插入图片描述

解决方法2

release版,但是不优化。
如果真的是程序有问题,只能在debug版下运行不报错,只要release版不优化,这样大概也是不会出问题的。
如果程序真的有问题,还是将bug从根上搞掉才是解决问题的态度。
在这里插入图片描述
在这里插入图片描述
release版默认虽然没有DEBUG预处理宏,但是还是会生成调试信息,所有也要去掉调试信息才靠谱。
在这里插入图片描述
在这里插入图片描述

看release版不优化(也不带调试信息)的EXE, 已经看不到和工程相关的路径和文件信息了。
看到有个和工程所在盘符相关的x:字符串,去看了一下,并没有引用关系。

备注

用动态载入DLL接口的方法,将IsDebuggerPresent动态来用,能隐蔽一些。

END

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/815788.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CSS快速入门

目录 一、CSS介绍 1、什么是CSS&#xff1f; ​编辑2、基本语法规范 3、引入方式 4、规范 二、CSS选择器 1、标签选择器 2、类&#xff08;class&#xff09;选择器 3、id选择器 4、通配符选择器 5、复合选择器 三、常用CSS 1、color 2、font-size 3、border 4…

【300套】基于Springboot+Vue的Java毕业设计项目(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f9e1;今天给大家分享300的Java毕业设计&#xff0c;基于Springbootvue框架&#xff0c;这些项目都经过精心挑选&#xff0c;涵盖了不同的实战主题和用例&#xff0c;可做毕业…

电影《你想活出怎样的人生》观后感

上周去看了宫崎骏电影《你想活出怎样的人生》&#xff0c;就像作为导演问观众的一个问题一样&#xff0c;宫崎骏老爷子&#xff0c;在电影中&#xff0c;给出了他的一些开放式答案。自己可是说是宫崎骏的粉丝&#xff0c;宫崎骏老爷子的大部分电影&#xff0c;自己基本都看过了…

clion最新安装教程

还在用Dev-C吗&#xff1f;也尝试了很多C编辑器&#xff0c;不是太老&#xff0c;就是太复杂。对于c开发者来说clion真的好用&#xff0c;CLion是一款专为开发C及C所设计的跨平台IDE。难受的是cion并不免费&#xff0c;仿佛是在证明好货不贵的道理&#xff0c;只能免费用30天。…

2024年少儿编程赛事时间表整理

01 信息学奥赛路线比赛 来源:https://www.noi.cn/ 首先,信息学奥赛是升学最有帮助的一个赛事了,从CSP-J/P开始到NOIP再到最后的NOI,对科技特长生、高考加分都有较大裨益,但相对难度也是最大的。 小学可以参加的是CSP-J比赛,但是和初中生一起考试,不占优势,胜在早入门…

4.14学习总结

java网络编程 一.网络编程的概念和原理 概念: 网络编程是指通过计算机网络进行数据传输和通信的编程技术。在网络编程中&#xff0c;可以实现不同计算机之间的数据交互和通信&#xff0c;从而实现分布式系统、客户端-服务器应用等。 Java网络编程基于TCP/IP协议栈进行通信&…

离谱!奇安信人事总监透露:Web安全不会岗位这些就别投简历了

有人的地方就有江湖&#xff0c;有互联网安全的地方&#xff0c;就必然有Web安全工程师的身影。但其实Web安全是近几年才备受关注的&#xff0c;从事这方面的专业人员并不多&#xff0c;这就导致整个市场Web安全研究员的供求严重不平衡。 这种供求不平衡直接反映在Web安全研究…

软考之零碎片段记录(十五)+复习巩固(十)

一、学习 1. 多对多关系模式 举例&#xff1a;学生和课程。顾客和商品等。 多对多关系的确立需要有中间表&#xff0c;需要使用两个外键确认表中的唯一数据。 2. 数据库范式 1nf 表中每个字段都是原子性不可查分的。在关系&#xff08;或表&#xff09;中&#xff0c;每一行…

使用Python的Pillow库进行图像处理书法参赛作品

介绍&#xff1a; 在计算机视觉和图像处理领域&#xff0c;Python是一种强大而流行的编程语言。它提供了许多优秀的库和工具&#xff0c;使得图像处理任务变得轻松和高效。本文将介绍如何使用Python的wxPython和Pillow库来选择JPEG图像文件&#xff0c;并对选中的图像进行调整和…

SpringBoot自动装配

清明前夕&#xff0c;我发表了一篇与Spring Cloud有关的文章&#xff0c;原计划在这篇文章之后&#xff0c;就梳理Eureka注册中心的相关知识。然而在跟踪之后&#xff0c;我才发现上来就谈Eureka组件的实现原理是不现实的&#xff0c;因为我根本不清楚SpringBoot是如何集成Eure…

【笔记】mysql版本6以上时区问题

前言 最近在项目中发现数据库某个表的createTime字段的时间比中国时间少了13个小时&#xff0c;只是在数据库中查看显示时间不对&#xff0c;但是在页面&#xff0c;又是正常显示中国时区的时间。 排查 项目中数据库的驱动使用的是8.0.19&#xff0c;驱动类使用的是com.mysq…

Open CASCADE学习|实现Extrude功能

首先定义了一些基本的几何元素&#xff0c;如线、圆和平面&#xff0c;然后使用makeExtrudebydir函数来对一个面进行挤出操作。下面是详细过程&#xff1a; 定义Extrude函数&#xff1a;makeExtrudebydir函数接受一个TopoDS_Shape对象和一个gp_Vec对象作为参数。TopoDS_Shape是…

HashMap与HashSet的不安全问题

HashSet的不安全问题 HashSet与ArrayList一样也存在不安全的问题&#xff0c;当多线程时也会出现ConcurrentModificationException&#xff0c;并发修改异常需要提出解决方案 问题 public static void main(String[] args) {Set<Integer> set new HashSet<>();…

1.MMD模型动作场景镜头的导入及视频导出

界面介绍 MIKUMIKUDANCE926版本 MMD的工具栏模型骨骼帧的窗口&#xff0c;在不同时间做不同动作&#xff0c;可以在这里打帧操作时间曲线操作窗口&#xff0c;控制模型两个动作之间的过渡模型操作窗口&#xff0c;导入模型选择模型相机操作&#xff0c;控制相机远近&#xf…

Vol.44 一个分享网站的网站,每个月8.7万访问量

哈咯&#xff0c;各位朋友好啊&#xff0c;我是欧维&#xff0c;今天要给大家分享的网址是Fuun.fun&#xff0c;奇趣网站收藏家&#xff1b; 它的网址是&#xff1a;FUUN.FUN 这是一个我经常逛的网站&#xff0c;为什么我经常逛呢&#xff1f;因为可以从中发现一些有意思的网站…

上位机图像处理和嵌入式模块部署(智能硬件的开发)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 目前&#xff0c;用上位机软件虽然可以部署项目&#xff0c;但是它本身有自己的缺点&#xff0c;那就是稳定性差、价格贵。稳定性这部分&#xff0…

Linux 快问快答

如果对于找 Java 后端开发的话&#xff0c;我感觉会这几个差不多了&#xff0c;面试官应该不会问的这么详细吧。一般就问问 Linux 的几个常用的命令&#xff0c;然后做一些简单的性能排查就好了。如果面试被问到另外的问题&#xff0c;那我再补充进来&#xff0c;现在先掌握这么…

CMC学习系列 (8):动态力输出期间的伽马范围皮质相干性

CMC学习系列:动态力输出期间的伽马范围皮质相干性 0. 引言1. 主要贡献2. 方法3. 结果4. 讨论5. 总结欢迎来稿 论文地址&#xff1a;https://www.sciencedirect.com/science/article/abs/pii/S1053811906010238 论文题目&#xff1a;Gamma-range corticomuscular coherence duri…

OSPF动态路由实验(思科)

华为设备参考&#xff1a; 一&#xff0c;技术简介 OSPF&#xff08;Open Shortest Path First&#xff09;是一种内部网关协议&#xff0c;主要用于在单一自治系统内决策路由。它是一种基于链路状态的路由协议&#xff0c;通过链路状态路由算法来实现动态路由选择。 OSPF的…

D. Nene and the Mex Operator

解题思路 若选定一个区间&#xff0c;则可以构造成值全为构造方如下&#xff1a;先将区间全变为0&#xff08;若区间有0且不全为0两次&#xff08;全变为一个值后再全变为0&#xff09;&#xff0c;若没有0则一次&#xff0c;若已经全为0则0次&#xff09;保留r为0&#xff0c…