反调试技术

news2024/9/30 7:15:46

文章目录

  • 前言
  • 系统API实现方式
    • IsDebuggerPresent (+0x2)
    • NtGlobalFlag(+0x68)
    • Heap flags(0x18)
    • CheckRemoteDebuggerPresent
    • NtQueryInformationProcess
    • ZwSetInformationThread
  • 示例
    • 示例1
      • 比较明文字符串和输入字符串
      • NtGlobalFlag
      • 时间差检测
      • ProcessMonitor
      • 检测进程名
      • 检测 VMware
      • SEH
      • 获取flag
    • 示例2

前言

思来想去还是先写反反调试,壳的话打算在PE文件内容里写
反调试顾名思义就是用尽一切手段防止运行时对程序的非法篡改和窥视,加大代码的复杂度和分析等

以下只是遇到的一些反调试,其中有一些知识都没有学习到,不过慢慢来也慢慢补坑

系统API实现方式

IsDebuggerPresent (+0x2)

微软给出的解释是此函数允许应用程序确定自己是否正在被调试,并依此改变行为。例如通过OutputDebugString函数提供更多调试信息

IsDebuggerPresent 这个 API 的实现方式是从 PEB 读取 BeingDebugged 字段来判断进程是否被调试状态

实现代码:

mov eax,dword ptr fs:[0x30]
movzx eax,byte ptr ds:[rax+0x2]
ret

fs:[0]是TEB结构的地址,其中fs:[0x30] 这个偏移是 PEB 指针,第一行的意思是将 PEB 指针赋值给 eax 寄存器,fs:[0x18]则是TEB指针

第二行就是从 PEB 结构的 0x2 偏移处,也就是 BeingDebugged 字段,取 1 字节,赋值到 eax

第三行就是返回了,没有参数和局部变量所以也没平栈,无论 __cdecl 调用还是 __stdcall 调用都是在 eax 寄存器保存返回值

想要 bypass 这种检查就非常容易,修改 PEB 结构中的 BeingDebugged 字段值为 0 就OK了。

NtGlobalFlag(+0x68)

在 32 位机器上, NtGlobalFlag字段位于PEB(进程环境块)0x68的偏移处, 64 位机器则是在偏移0xBC位置. 该字段的默认值为 0. 当调试器正在运行时, 该字段会被设置为一个特定的值. 尽管该值并不能十分可信地表明某个调试器真的有在运行, 但该字段常出于该目的而被使用.

该字段包含有一系列的标志位. 由调试器创建的进程会设置以下标志位:

FLG_HEAP_ENABLE_TAIL_CHECK (0x10)
FLG_HEAP_ENABLE_FREE_CHECK (0x20)
FLG_HEAP_VALIDATE_PARAMETERS (0x40)

调试程序时,PEB.NtGlobalFlag的值会被设置为0x70,所以,检测该成员的值即可判断进程是否处于被调试状态。

bypass 这个检查也很容易,因为标志位都在被调试进程的地址空间里,直接改掉就行了。

Heap flags(0x18)

这里引用ctfwiki给的解释:

Heap flags包含有两个与NtGlobalFlag一起初始化的标志: Flags和ForceFlags. 这两个字段的值不仅会受调试器的影响, 还会由 windows 版本而不同, 字段的位置也取决于 windows 的版本.

Flags 字段:
在 32 位 Windows NT, Windows 2000 和 Windows XP 中, Flags位于堆的0x0C偏移处. 在 32 位 Windows Vista 及更新的系统中, 它位于0x40偏移处.
在 64 位 Windows XP 中, Flags字段位于堆的0x14偏移处, 而在 64 位 Windows Vista 及更新的系统中, 它则是位于0x70偏移处.

ForceFlags 字段:
在 32 位 Windows NT, Windows 2000 和 Windows XP 中, ForceFlags位于堆的0x10偏移处. 在 32 位 Windows Vista 及更新的系统中, 它位于0x44偏移处.
在 64 位 Windows XP 中, ForceFlags字段位于堆的0x18偏移处, 而在 64 位 Windows Vista 及更新的系统中, 它则是位于0x74偏移处.。

在所有版本的 Windows 中, Flags字段的值正常情况都设为HEAP_GROWABLE(2), 而ForceFlags字段正常情况都设为0.
然而对于一个 32 位进程 (64 位程序不会有此困扰), 这两个默认值, 都取决于它的宿主进程(host process) 的 subsystem版本 (这里不是指所说的比如 win10 的 linux 子系统).
只有当subsystem在3.51及更高的版本, 字段的默认值才如前所述. 如果是在3.10-3.50版本之间, 则两个字段的HEAP_CREATE_ALIGN_16 (0x10000)都会被设置. 如果版本低于3.10, 那么这个程序文件就根本不会被运行.
如果某操作将Flags和ForgeFlags字段的值分别设为2和0, 但是却未对subsystem版本进行检查, 那么就可以表明该动作是为了隐藏调试器而进行的.

CheckRemoteDebuggerPresent

kernel32的CheckRemoteDebuggerPresent()函数用于检测指定进程是否正在被调试. Remote在单词里是指同一个机器中的不同进程.
两个参数:一个是进程的 HANDLE,一个是 PBOOL。

BOOL WINAPI CheckRemoteDebuggerPresent(
  _In_    HANDLE hProcess,
  _Inout_ PBOOL  pbDebuggerPresent
);

如果调试器存在 (通常是检测自己是否正在被调试), 该函数会将pbDebuggerPresent指向的值设为0xffffffff.

NtQueryInformationProcess

NtQueryInformationProcess 是一个查询信息的接口,输入参数包括查询的信息类型、进程HANDLE、结果指针等。
kernel32的CheckRemoteDebuggerPresent()函数内部通过调用NtQueryInformationProcess()来检测调试, 而NtQueryInformationProcess内部则是查询EPROCESS结构体的DebugPort字段, 当进程正在被调试时, 返回值为0xffffffff.

内核接口,还不会hook先放置一下。

ZwSetInformationThread

ZwSetInformationThread 等同于 NtSetInformationThread,通过为线程设置 ThreadHideFromDebugger,可以禁止线程产生调试事件,代码如下:

#include <Windows.h>
#include <stdio.h>

typedef DWORD(WINAPI* ZW_SET_INFORMATION_THREAD) (HANDLE, DWORD, PVOID, ULONG);
#define ThreadHideFromDebugger 0x11
VOID DisableDebugEvent(VOID)
{
    HINSTANCE hModule;
    ZW_SET_INFORMATION_THREAD ZwSetInformationThread;
    hModule = GetModuleHandleA("Ntdll");
    ZwSetInformationThread = (ZW_SET_INFORMATION_THREAD)GetProcAddress(hModule, "ZwSetInformationThread");
    ZwSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, 0, 0);
}

int main()
{
    printf("Begin\n");
    DisableDebugEvent();
    printf("End\n");
    return 0;
}

关键代码为ZwSetInformationThread(GetCurrentThread(), ThreadHideFromDebugger, 0, 0),如果处于调试状态,执行完该行代码,程序就会退出。
想要bypass也比较简单,可以看到ZwSetInformationThread()函数的第二个参数为ThreadHideFromDebugger,其值为0x11。当执行到该函数的时候,若发现第 2 个参数值为 0x11,跳过或者将 0x11 修改为其他值即可。

示例

示例1

还是以ctfwiki给的示例来做,直接运行,要求输入密码,输入出错则提示
在这里插入图片描述

无壳32位,丢ida看看,查找字符串

在这里插入图片描述

显然,字符串表明程序中可能有各种检测,比如检测进程名ollydbg.exe, ImmunityDebugger.exe, idaq.exe和Wireshark.exe,然后也有其他的检测,可以看到字符串password is wrong和You password is correct的字样,同时还有疑是待解密的flag字符串

先从提示处开始入手,也就是主函数,果然存在大量的反调试

int __cdecl main(int argc, const char **argv, const char **envp)
{
  FILE *v3; // eax
  HANDLE v4; // eax
  int v11; // [esp+C4h] [ebp-A8h]
  DWORD v12; // [esp+D4h] [ebp-98h]
  LPCSTR lpFileName; // [esp+D8h] [ebp-94h]
  BOOL pbDebuggerPresent; // [esp+DCh] [ebp-90h]
  int v15; // [esp+E0h] [ebp-8Ch]
  int v16; // [esp+E4h] [ebp-88h]
  int i; // [esp+E8h] [ebp-84h]
  int v18; // [esp+ECh] [ebp-80h]
  int v19; // [esp+F0h] [ebp-7Ch]
  char v20[4]; // [esp+F8h] [ebp-74h]
  int v21; // [esp+108h] [ebp-64h]
  char v22; // [esp+10Ch] [ebp-60h]
  char v23; // [esp+10Dh] [ebp-5Fh]
  CPPEH_RECORD ms_exc; // [esp+154h] [ebp-18h]

  v22 = 0;
  memset(&v23, 0, 0x3Fu);
  v21 = 1;
  printf("Input password >");
  v3 = (FILE *)sub_40223D();
  fgets(&v22, 64, v3);
  strcpy(v20, "I have a pen.");
  v21 = strncmp(&v22, v20, 0xDu);               // 直接比较明文和输入字符串
  if ( !v21 )
  {
    puts("Your password is correct.");
    if ( IsDebuggerPresent() == 1 )             // API: IsDebuggerPresent() 静态反调
    {
      puts("But detected debugger!");
      exit(1);
    }
    if ( sub_401120() == 0x70 )                 // API:NtGlobalFlag 静态反调
    {
      puts("But detected NtGlobalFlag!");
      exit(1);
    }
    v4 = GetCurrentProcess();
    CheckRemoteDebuggerPresent(v4, &pbDebuggerPresent);// API:CheckRemoteDebuggerPresent() 静态反调
    if ( pbDebuggerPresent )
    {
      printf("But detected remotedebug.\n");
      exit(1);
    }
    v12 = GetTickCount();                       // 时间差检测 动态反调
    for ( i = 0; i == 100; ++i )
      Sleep(1u);
    v15 = 1000;
    if ( GetTickCount() - v12 > 0x3E8 )
    {
      printf("But detected debug.\n");
      exit(1);
    }
    lpFileName = "\\\\.\\Global\\ProcmonDebugLogger";// 通过检测设备文件来检测ProcessMonitor
    if ( CreateFileA("\\\\.\\Global\\ProcmonDebugLogger", 0x80000000, 7u, 0, 3u, 0x80u, 0) != (HANDLE)-1 )
    {
      printf("But detect %s.\n", &lpFileName);
      exit(1);
    }
    v11 = sub_401130();                         //  API: CreateToolhelp32Snapshot()检测进程
    if ( v11 == 1 )
    {
      printf("But detected Ollydbg.\n");
      exit(1);
    }
    if ( v11 == 2 )
    {
      printf("But detected ImmunityDebugger.\n");
      exit(1);
    }
    if ( v11 == 3 )
    {
      printf("But detected IDA.\n");
      exit(1);
    }
    if ( v11 == 4 )
    {
      printf("But detected WireShark.\n");
      exit(1);
    }
    if ( sub_401240() == 1 )                    // 检测 VMware
    {
      printf("But detected VMware.\n");
      exit(1);
    }
    v16 = 1;                                    // 异常 动态反调

    v19 = 1;
    v18 = 1 / 0;
    ms_exc.registration.TryLevel = -2;
    printf("But detected Debugged.\n");
    exit(1);
  }
  printf("password is wrong.\n");
  return 0;
}

 

进行一些反调的简单分析:

比较明文字符串和输入字符串

  v22 = 0;
  memset(&v23, 0, 0x3Fu);
  v21 = 1;
  printf("Input password >");
  v3 = (FILE *)sub_40223D();
  fgets(&v22, 64, v3);
  strcpy(v20, "I have a pen.");
  v21 = strncmp(&v22, v20, 0xDu);               

先输出Input password >,然后用fgets()获取用户输入的字符串, 将I have a pen,复制到v20的缓冲区中,然后用strncmp比对用户输入与I have a pen.的内容, 并将比较结果返回给v21. 以下会根据v21, 也就是根据输入的password是否正确而进行跳转

NtGlobalFlag

    if ( sub_401120() == 0x70 )                 // API:NtGlobalFlag
    {
      puts("But detected NtGlobalFlag!");
      exit(1);
    }
//sub_401120
int sub_401120()
{
  return *(_DWORD *)(__readfsdword(0x30u) + 104) & 0x70;
}

0x68是 PEB 的NtGlobalFlag字段对应偏移值
0x70是FLG_HEAP_ENABLE_TAIL_CHECK (0x10),FLG_HEAP_ENABLE_FREE_CHECK (0x20) 和FLG_HEAP_VALIDATE_PARAMETERS (0x40)这三个标志

由于是静态反调,所以在xdbg中无效

时间差检测

v13 = GetTickCount();
for ( i = 0; i == 100; ++i )    // 睡眠
    Sleep(1u);
v16 = 1000;
if ( GetTickCount() - v13 > 1000 )  //  检测时间差
{
    printf("But detected debug.\n");
    exit(1);
}

GetTickCount会返回启动到现在的毫秒数,循环里光是sleep(1)就进行了 100 次,也就是 100 毫秒。 两次得到的时间作差如果大于 1000 毫秒,时差明显大于所耗的时间, 也就间接检测到了调试。
反动调经常使用的时间检测法,其余的时间检测法还有 时钟检测,其他的时间API函数,如QueryPerformanceCounter、GetTickCount、GetSystemTime、GetLocalTime等
bypass也很简单,只需修改标志位让其跳转即可

ProcessMonitor

lpFileName = "\\\\.\\Global\\ProcmonDebugLogger";
if ( CreateFileA("\\\\.\\Global\\ProcmonDebugLogger", 0x80000000, 7u, 0, 3u, 0x80u, 0) != (HANDLE)-1 )
{
    printf("But detect %s.\n", &lpFileName);     
    exit(1);
}

这里通过检测设备文件\\.\Global\ProcmonDebugLogger来检测ProcessMonitor

检测进程名

v11 = sub_401130();    
if ( v11 == 1 )
{
    printf("But detected Ollydbg.\n");
    exit(1);
}
if ( v11 == 2 )
{
    printf("But detected ImmunityDebugger.\n");
    exit(1);
}
if ( v11 == 3 )
{
    printf("But detected IDA.\n");
    exit(1);
}
if ( v11 == 4 )
{
    printf("But detected WireShark.\n");
    exit(1);
}

//sub_401130()
signed int sub_401130()
{
  PROCESSENTRY32 pe; // [esp+0h] [ebp-138h]
  HANDLE hSnapshot; // [esp+130h] [ebp-8h]
  BOOL i; // [esp+134h] [ebp-4h]

  pe.dwSize = 296;
  memset(&pe.cntUsage, 0, 0x124u);
  hSnapshot = CreateToolhelp32Snapshot(2u, 0);
  for ( i = Process32First(hSnapshot, &pe); i == 1; i = Process32Next(hSnapshot, &pe) )
  {
    if ( !_stricmp(pe.szExeFile, "ollydbg.exe") )
      return 1;
    if ( !_stricmp(pe.szExeFile, "ImmunityDebugger.exe") )
      return 2;
    if ( !_stricmp(pe.szExeFile, "idaq.exe") )
      return 3;
    if ( !_stricmp(pe.szExeFile, "Wireshark.exe") )
      return 4;
  }
  return 0;
}

通过执行sub_401130()函数来检测进程,并根据检测到的不同进程,返回相应的值。

sub_401120()中使用了 API: CreateToolhelp32Snapshot来获取当前的进程信息, 并在 for 循环里依次比对。如果找到指定的进程名, 就直接返回相应的值,然后根据返回值跳转到不同的分支里。

检测 VMware

if ( sub_401240() == 1 )    // 8. 通过vmware的I/O端口进行检测
{
    printf("But detected VMware.\n");
    exit(1);
}
//sub_401240()
signed int sub_401240()
{
  unsigned __int32 v0; // eax

  v0 = __indword(0x5658u);
  return 1;
}

这是 VMware 的一个 "后门"I/O 端口, 0x5658 = “VX”.
如果程序在 VMware 内运行, 程序使用In指令通过0x5658端口读取数据时, EBX寄存器的值就会变为0x564D5868(0x564D5868 == “VMXh”)

在这里插入图片描述

SEH

v17 = 1;
v20 = 1;
v12 = 0;
v19 = 1 / 0;    // SEH
ms_exc.registration.TryLevel = -2;
printf("But detected Debugged.\n");
exit(1);

接下来这一段,这里v19 = 1 / 0明显是不合常理的,会产生一个除零异常. 而后面的ms_exc.registration.TryLevel = -2,解除异常,TryLevel=TRYLEVEL_NONE (-2) . 来看汇编代码

在这里插入图片描述

这里的idiv [ebp+var_9C]触发异常后就由程序注册的异常处理函数接管,如果没有在异常处理程序入口设下断点的话, 程序就容易跑飞,将异常交给SEH处理下好断点即可。

获取flag

前面分析到有一串类似待解密的flag字符串,怎么都没有看到相关的代码流,实际上由于 IDA 反编译的限制,使得反编译出的伪 C 代码并不正确,来看最后一个Debugged提示的汇编代码,有些代码流没有被实际反编译出来

.text:00401627 loc_401627:                             
.text:00401627                 call    sub_4012E0
.text:0040162C                 movzx   eax, ax
.text:0040162F                 mov     [ebp+var_A8], eax
.text:00401635                 cmp     [ebp+var_A8], 0
.text:0040163C                 jz      short loc_401652  //该函数也没有被反编译出来
.text:0040163E                 push    offset aButDetectedDeb_2 ,"But detected Debugged.\n"
.text:00401643                 call    _printf
.text:00401648                 add     esp, 4
.text:0040164B                 push    1               ; int
.text:0040164D                 call    _exit

以上代码并没有被实际反编译出来,而loc_401652()也是同样没有被反编译出来的代码

.text:00401652 loc_401652:                           
.text:00401652                 mov     [ebp+var_78], 0
.text:00401659                 cmp     [ebp+var_78], 1
.text:0040165D                 jnz     loc_40174D
.text:00401663                 mov     ecx, 7
.text:00401668                 mov     esi, offset aAjJq7hbotHU8ac ; ";aj&@:JQ7HBOt[h?U8aCBk]OaI38"
.text:0040166D                 lea     edi, [ebp+var_CC]
.text:00401673                 rep movsd
.text:00401675                 movsb
.text:00401676                 xor     ecx, ecx
.text:00401678                 mov     [ebp+var_AF], ecx
.text:0040167E                 lea     edx, [ebp+var_CC]
.text:00401684                 mov     [ebp+var_D8], edx
.text:0040168A                 mov     [ebp+Text], 0
.text:00401691                 push    7Fh             ; size_t
.text:00401693                 push    0               ; int
.text:00401695                 lea     eax, [ebp+var_157]
.text:0040169B                 push    eax             ; void *
.text:0040169C                 call    _memset
.text:004016A1                 add     esp, 0Ch
.text:004016A4                 lea     ecx, [ebp+Text]
.text:004016AA                 mov     [ebp+var_D4], ecx
.text:004016B0                 mov     edx, [ebp+var_D8]
.text:004016B6                 push    edx             ; char *
.text:004016B7                 call    _strlen
.text:004016BC                 add     esp, 4
.text:004016BF                 mov     [ebp+var_D0], eax
.text:004016C5                 mov     [ebp+var_15C], 0
.text:004016CF                 jmp     short loc_4016FE

loc_4016FE():
.text:004016FE loc_4016FE:                           
.text:004016FE                 mov     eax, [ebp+var_15C]
.text:00401704                 cmp     eax, [ebp+var_D0]
.text:0040170A                 jnb     short loc_401737
.text:0040170C                 mov     ecx, [ebp+var_D0]
.text:00401712                 sub     ecx, [ebp+var_15C]
.text:00401718                 push    ecx
.text:00401719                 mov     edx, [ebp+var_D4]
.text:0040171F                 push    edx
.text:00401720                 mov     eax, [ebp+var_D8]
.text:00401726                 push    eax
.text:00401727                 call    sub_401000
.text:0040172C                 add     esp, 0Ch
.text:0040172F                 test    eax, eax
.text:00401731                 jz      short loc_401735
.text:00401733                 jmp     short loc_401737

loc_401737():
.text:00401737 loc_401737:                            
.text:00401737                                         ; 
.text:00401737                 push    0               ; uType
.text:00401739                 push    offset Caption  ; "check!"
.text:0040173E                 lea     ecx, [ebp+Text]
.text:00401744                 push    ecx             ; lpText
.text:00401745                 push    0               ; hWnd
.text:00401747                 call    ds:MessageBoxA

可以看到后续的代码中调用了MessageboxA()函数,其中第三个参数是解密后的flag

在这里插入图片描述

再向上查找的话发现在进入flag的解密前会有一个永假条件跳转,在调试的时候雄修改标志位跳过即可,之后让程序解密flag,调用窗体

在这里插入图片描述

弹出窗体,得到flag

在这里插入图片描述

示例2

改天再补吧

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

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

相关文章

支持向量机SVM的原理和python实现

文章目录 1 SVM概述1.1 概念1.2 SVM的优缺点1.2.1 优点1.2.2 缺点 2 在python中使用SVM2.1 scikit-learn库2.2 SVM在scikit-learn库中的使用2.2.1 安装依赖库2.2.2 svm.SVC2.2.3 应用实例 总结 1 SVM概述 1.1 概念 支持向量机&#xff08;SVM&#xff09;是一类按监督学习方式…

CRM系统如何选择?哪些是必备功能?

CRM系统可以收集、整理并分析客户数据、优化企业销售流程、实现团队协作和共享&#xff0c;提高客户转化率&#xff0c;实现业绩增长。那么&#xff0c;如何选择CRM系统&#xff1f;CRM系统哪家好&#xff1f; 一、明确自己的业务需求 不同行业和规模的企业有不同的业务需求&…

JMU 软件工程经济学 复习总结

文章目录 碎碎念0. 基准收益率 i1. 现金流量图2. 净现值 NPV&#xff0c;内部收益率 IRR3. 单利&#xff0c;复利计算4. 等额年金NAV5. 动态回收期 P t ′ P_t Pt′​6. 固定资产折旧 [书P44]7. 增值税8. 软件行业增值税的即征即退9. 利息备付率 ICR&#xff0c;偿债备付率 DSC…

这6种最佳移动自动化测试工具你知道吗?

最好的移动自动化测试工具 在本文章关于移动应用程序测试的这一部分中&#xff0c;我们将研究 2023 年 6 种最佳移动自动化测试工具。 1、Appium Appium 是一个非常流行的开源自动化测试框架&#xff0c;支持各种操作系统的自动化。它可以与本机、混合和移动 Web 应用程序一…

微机原理基础知识

前言 微机原理期末复习的一些概念性的基础知识总结。 内容 &#xff08;1&#xff09;微处理器、微机与微机系统三者之间有什么异同&#xff1f; &#xff08;1&#xff09;把CPU&#xff08;运算器和控制器&#xff09;用大规模集成电路技术做在一个芯片上&#xff0c;即为微…

Vue实现Base64转png、jpg

method中写两个方法&#xff1a; 根据base64转图片的方法 根据转换出blob格式的文件导出的方法 //base64转pngbase64ImgtoFile(dataurl, filename file) {const arr dataurl.split(,)const mime arr[0].match(/:(.*?);/)[1]const suffix mime.split(/)[1]const bstr a…

Windows安装postgresql数据库图文教程

数据库使用排行榜&#xff1a;https://db-engines.com/en/ranking 目录 一、软件简介 二、软件下载 三、安装教程 四、启动教程 一、软件简介 PostgreSQL是一种特性非常齐全的自由软件的对象-关系型数据库管理系统&#xff08;ORDBMS&#xff09;&#xff0c;是以加州大学计…

Python采集二手房源数据信息并做可视化展示

目录标题 前言环境使用:模块使用:python技术实现: <基本流程步骤>代码展示尾语 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 环境使用: Python 3.8 jupyter --> pip install jupyter notebook pycharm 也可以 模块使用: requests >>> pip instal…

【MySQL 数据查询】:提高查询的效率

前言 ✨欢迎来到小K的MySQL专栏&#xff0c;本节将为大家带来MySQL数据查询的讲解&#xff08;基本、分组、排序、聚合、分页、条件查询&#xff09; 目录 前言一、基本查询二、条件查询三、聚合函数(统计函数)四、分组查询五、排序查询五、分页查询六、总结 一、基本查询 MySQ…

2020年CSP-J认证 CCF非专业级别软件能力认证第一轮真题--阅读程序题

2020 CCF认证第一轮&#xff08;CSP-J&#xff09;真题 二、阅读程序题 (程序输入不超过数组或字符串定义的范围&#xff0c;判断题正确填√错误填X;除特殊说明外&#xff0c;判断题 1.5分&#xff0c;选择题3分&#xff0c;共计4 分) 第一题 01 #include <cstdlib> …

Java调优

Java调优 Java 性能调优不像是学一门编程语言&#xff0c;无法通过直线式的思维来掌握和应用&#xff0c;它对于工程师的技术广度和深度都有着较高的要求。 互联网时代&#xff0c;一个简单的系统就囊括了应用程序、数据库、容器、操作系统、网络等技术&#xff0c;线上一旦出…

【深度学习】GPT-1

GPT-1是OpenAI在《Improving Language Understanding by Generative Pre-Training》中于2018年提出的生成式预训练语言模型。 1.GPT-1 简介 在自然语言处理任务中&#xff0c;存在大量无标签的语料数据&#xff0c;而有标签的语料数据相对较少&#xff0c;因此基于有监督训练的…

[建议收藏] Mysql+ETLCloud CDC+Doris实时数仓同步实战

一、业务需求及其痛点 随着数字化转型&#xff0c;企业需要对各种销售及营销数据进行实时同步分析&#xff0c;例如销售订单信息&#xff0c;库存信息&#xff0c;会员信息&#xff0c;设备状态信息等等&#xff0c;这些统计分析信息可以实时同步到 Doris中进行分析和统计&…

搜索跳出率:了解并优化用户体验

&#x1f482; 个人网站:【海拥】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 目录 前言什么是搜索跳出率&…

【Linux编辑器-vim使用】

目录 Linux编辑器-vim使用1.vim的基本概念2.vim的基本操作3.vim正常模式命令集4.vim末行模式命令集 Linux编辑器-vim使用 1.vim的基本概念 目前了解的vim有三种模式&#xff08;其实有好多模式&#xff09;&#xff0c;分别是命令模式、插入模式和底行模式&#xff0c;各模式…

Midjourney如何用参考图/垫图来绘画图

大家都知道AI绘画工具每次生成的效果都是随机的&#xff0c;但是现在很多AI绘图工具都提供了利用参考图/垫图的方式出图&#xff0c;这样就可以让让AI画作生成自己想要的布局、场景、色彩等等。 国内的AI绘图工具一般都好操作&#xff0c;国外主流的Midjourney也可以添加参考图…

ChatGPT/GPT-4 或将从根本上改变软件工程

文章目录 一、前言二、主要内容 &#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 一、前言 编程也可能是最容易被 AI 技术取代的工作之一&#xff0c;软件的构建方式将产生根本性的转变。 今年以来&#xff0c;相信大家都听说过 ChatGPT、New Bing 和…

8.5 字节序及IP地址转换

目录 主机字节序和网络字节序 什么是字节序&#xff1f; 字节序转换函数 IP地址字节序转换函数 主机字节序和网络字节序 什么是字节序&#xff1f; 字节序是指多字节数据在计算机内存中存储或者网络传输时各字节的存储顺序&#xff0c;分为&#xff1a; 大端字节序 (Big …

药物 3D 打印新突破:圣地亚哥大学用机器学习筛选喷墨打印生物墨水,准确率高达 97.22%

内容一览&#xff1a;药物喷墨打印是一种高度灵活和智能化的制药方式。据相关报告统计&#xff0c;该领域市场规模将在不久的未来呈现指数级增长。过往&#xff0c;筛选合适生物墨水的方法费时且费力&#xff0c;因此也成为药物喷墨打印领域面临的主要挑战之一。为解决这一问题…

开启你的时间序列分析之旅:一步步教你学会HyperTS

目录 前言一、HyperTS介绍二、HyperTS安装、使用2-1、安装2-2、HyperTS使用 三、案例3-0、通用工作流程3-1、时间序列预测3-2、时间序列分类3-3、时间序列异常检测 四、高级应用4-1、模型的保存和加载 总结 前言 HyperTS是一个开源的时间序列分析库&#xff0c;主要用于处理和分…