dll注入与代码注入
学习《逆向工程核心原理》,在x64下dll注入与代码注入。
dll注入主要用到CreateRemoteThread,
代码注入类似,主要用到VirtualAllocEx,WriteProcessMemory,在需注入的进程中开辟空间,写入代码,变量。
- 1 #include "pch.h"
- 2 #include <windows.h>
- 3 #include <tchar.h>
- 4 #include <tlhelp32.h>
- 5 #include <stdio.h>
- 6 #include <shlobj.h>
- 7 extern void checkAdmin();
- 8
- 9 //遍历输出进程pid
- 10 int TraversalProcess() {
- 11 HANDLE hProceessnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
- 12 if (hProceessnap == INVALID_HANDLE_VALUE)
- 13 {
- 14 printf_s("创建进行快照失败\n");
- 15 return -1;
- 16 }
- 17 else
- 18 {
- 19 PROCESSENTRY32 pe32;
- 20 pe32.dwSize = sizeof(pe32);
- 21 BOOL hProcess = Process32First(hProceessnap, &pe32);
- 22 while (hProcess)
- 23 {
- 24 /*WCHAR * ProcessName =(WCHAR *)L"ProcessID.exe";
- 25 if (!wcscmp(pe32.szExeFile, ProcessName))
- 26 {*/
- 27 printf("进程名:%-10ls ----------------进程ID:%6d\n", pe32.szExeFile, pe32.th32ProcessID);
- 28 /* break;
- 29 }*/
- 30 hProcess = Process32Next(hProceessnap, &pe32);
- 31 }
- 32 }
- 33 CloseHandle(hProceessnap);
- 34 }
- 35
- 36 //设置权限
- 37 BOOL SetPrivilege(LPCTSTR lpszPrivilege, BOOL bEnablePrivilege)
- 38 {
- 39 TOKEN_PRIVILEGES tp;
- 40 HANDLE hToken;
- 41 LUID luid;
- 42
- 43 if (!OpenProcessToken(GetCurrentProcess(),
- 44 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
- 45 &hToken))
- 46 {
- 47 _tprintf(L"OpenProcessToken error: %u\n", GetLastError());
- 48 return FALSE;
- 49 }
- 50
- 51 if (!LookupPrivilegeValue(NULL, // lookup privilege on local system
- 52 lpszPrivilege, // privilege to lookup
- 53 &luid)) // receives LUID of privilege
- 54 {
- 55 _tprintf(L"LookupPrivilegeValue error: %u\n", GetLastError());
- 56 return FALSE;
- 57 }
- 58
- 59 tp.PrivilegeCount = 1;
- 60 tp.Privileges[0].Luid = luid;
- 61 if (bEnablePrivilege)
- 62 tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
- 63 else
- 64 tp.Privileges[0].Attributes = 0;
- 65
- 66 // Enable the privilege or disable all privileges.
- 67 if (!AdjustTokenPrivileges(hToken,
- 68 FALSE,
- 69 &tp,
- 70 sizeof(TOKEN_PRIVILEGES),
- 71 (PTOKEN_PRIVILEGES)NULL,
- 72 (PDWORD)NULL))
- 73 {
- 74 _tprintf(L"AdjustTokenPrivileges error: %u\n", GetLastError());
- 75 return FALSE;
- 76 }
- 77
- 78 if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
- 79 {
- 80 _tprintf(L"The token does not have the specified privilege. \n");
- 81 return FALSE;
- 82 }
- 83
- 84 return TRUE;
- 85 }
- 86
- 87 //dll注入
- 88 BOOL InjectDll(DWORD dwPID, LPCTSTR szDllPath)
- 89 {
- 90 HANDLE hProcess = NULL, hThread = NULL;
- 91 HMODULE hMod = NULL;
- 92 LPVOID pRemoteBuf = NULL;
- 93 DWORD dwBufSize = (DWORD)(_tcslen(szDllPath) + 1) * sizeof(TCHAR);
- 94 LPTHREAD_START_ROUTINE pThreadProc;
- 95
- 96 // #1. 使用dwPID请求对象进程(notepad.exe)的HANDLE。
- 97 if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
- 98 {
- 99 _tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
- 100 return FALSE;
- 101 }
- 102
- 103 // #2. 在目标进程(notepad.exe)内存中分配与szDllName大小相同的内存。
- 104 pRemoteBuf = VirtualAllocEx(hProcess, NULL, dwBufSize, MEM_COMMIT, PAGE_READWRITE);
- 105
- 106 // #3. 在分配的内存中使用dll路径
- 107 WriteProcessMemory(hProcess, pRemoteBuf, (LPVOID)szDllPath, dwBufSize, NULL);
- 108
- 109 // #4. 请求到LoadLibraryA() API地址。
- 110 hMod = GetModuleHandle(L"kernel32.dll");
- 111 pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hMod, "LoadLibraryW");
- 112
- 113 // #5. 在notepad.exe进程中运行线程
- 114 hThread = CreateRemoteThread(hProcess, NULL, 0, pThreadProc, pRemoteBuf, 0, NULL);
- 115 //通过CreateRemoteThread,调用pThreadProc,即LoadLibraryW,参数为在需注入进程中开辟的内存(存储了dll路径)
- 116
- 117 WaitForSingleObject(hThread, INFINITE);
- 118
- 119 CloseHandle(hThread);
- 120 CloseHandle(hProcess);
- 121
- 122 return TRUE;
- 123 }
- 124 //卸载dll
- 125 BOOL EjectDll(DWORD dwPID, LPCTSTR szDllName)
- 126 {
- 127 BOOL bMore = FALSE, bFound = FALSE;
- 128 HANDLE hSnapshot, hProcess, hThread;
- 129 HMODULE hModule = NULL;
- 130 MODULEENTRY32 me = { sizeof(me) };
- 131 LPTHREAD_START_ROUTINE pThreadProc;
- 132 // dwPID = notepad 进程 ID
- 133 // 使用TH32CS_SNAPMODULE参数获得载入notepad程序的DLL名称
- 134 hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, dwPID);
- 135
- 136 bMore = Module32First(hSnapshot, &me);
- 137 for (; bMore; bMore = Module32Next(hSnapshot, &me))
- 138 {
- 139 if (!_tcsicmp((LPCTSTR)me.szModule, szDllName) ||
- 140 !_tcsicmp((LPCTSTR)me.szExePath, szDllName))
- 141 {
- 142 bFound = TRUE;
- 143 break;
- 144 }
- 145 }
- 146
- 147 if (!bFound)
- 148 {
- 149 CloseHandle(hSnapshot);
- 150 return FALSE;
- 151 }
- 152
- 153 if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, dwPID)))
- 154 {
- 155 _tprintf(L"OpenProcess(%d) failed!!! [%d]\n", dwPID, GetLastError());
- 156 return FALSE;
- 157 }
- 158
- 159 hModule = GetModuleHandle(L"kernel32.dll");
- 160 pThreadProc = (LPTHREAD_START_ROUTINE)GetProcAddress(hModule, "FreeLibrary");
- 161 hThread = CreateRemoteThread(hProcess, NULL, 0,
- 162 pThreadProc, me.modBaseAddr,
- 163 0, NULL);
- 164 WaitForSingleObject(hThread, INFINITE);
- 165
- 166 CloseHandle(hThread);
- 167 CloseHandle(hProcess);
- 168 CloseHandle(hSnapshot);
- 169
- 170 return TRUE;
- 171 }
- 172
- 173
- 174 //代码注入所需结构体
- 175 typedef struct _THREAD_PARAM
- 176 {
- 177 FARPROC pFunc[2]; // LoadLibraryA(), GetProcAddress()
- 178 char szBuf[4][128]; // "user32.dll", "MessageBoxA", "www.reversecore.com", "ReverseCore"
- 179 } THREAD_PARAM, *PTHREAD_PARAM;
- 180
- 181 typedef HMODULE(WINAPI *PFLOADLIBRARYA)
- 182 (
- 183 LPCSTR lpLibFileName
- 184 );
- 185
- 186 typedef FARPROC(WINAPI *PFGETPROCADDRESS)
- 187 (
- 188 HMODULE hModule,
- 189 LPCSTR lpProcName
- 190 );
- 191
- 192 typedef int (WINAPI *PFMESSAGEBOXA)
- 193 (
- 194 HWND hWnd,
- 195 LPCSTR lpText,
- 196 LPCSTR lpCaption,
- 197 UINT uType
- 198 );
- 199
- 200 //需注入的代码
- 201 DWORD WINAPI ThreadProc(LPVOID lParam)
- 202 {
- 203 PTHREAD_PARAM pParam = (PTHREAD_PARAM)lParam;
- 204 HMODULE hMod = NULL;
- 205 FARPROC pFunc = NULL;
- 206 //调用 LoadLibrary() 加载 "user32.dll"
- 207 hMod = ((PFLOADLIBRARYA)pParam->pFunc[0])(pParam->szBuf[0]); // "user32.dll"
- 208 if (!hMod)
- 209 return 1;
- 210
- 211 //调用 GetProcAddress() 获取"MessageBoxA"
- 212 pFunc = (FARPROC)((PFGETPROCADDRESS)pParam->pFunc[1])(hMod, pParam->szBuf[1]); // "MessageBoxA"
- 213 if (!pFunc)
- 214 return 1;
- 215
- 216 // 调用MessageBoxA()
- 217 ((PFMESSAGEBOXA)pFunc)(NULL, pParam->szBuf[2], pParam->szBuf[3], MB_OK);
- 218 return 0;
- 219 }
- 220
- 221 //实现代码注入
- 222 BOOL InjectCode(DWORD dwPID)
- 223 {
- 224 HMODULE hMod = NULL;
- 225 THREAD_PARAM param = { 0, };
- 226 HANDLE hProcess = NULL;
- 227 HANDLE hThread = NULL;
- 228 LPVOID pRemoteBuf[2] = { 0, };
- 229 DWORD dwSize = 0;
- 230
- 231 hMod = GetModuleHandleA("kernel32.dll");
- 232
- 233 // set THREAD_PARAM
- 234 param.pFunc[0] = GetProcAddress(hMod, "LoadLibraryA");
- 235 param.pFunc[1] = GetProcAddress(hMod, "GetProcAddress");
- 236 strcpy_s(param.szBuf[0], "user32.dll");
- 237 strcpy_s(param.szBuf[1], "MessageBoxA");
- 238 strcpy_s(param.szBuf[2], "这是code inject");
- 239 strcpy_s(param.szBuf[3], "好开心!");
- 240
- 241 // Open Process
- 242 if (!(hProcess = OpenProcess(PROCESS_ALL_ACCESS, // dwDesiredAccess
- 243 FALSE, // bInheritHandle
- 244 dwPID))) // dwProcessId
- 245 {
- 246 printf("OpenProcess() fail : err_code = %d\n", GetLastError());
- 247 return FALSE;
- 248 }
- 249
- 250 // Allocation for THREAD_PARAM 写入代码注入所需的data
- 251 dwSize = sizeof(THREAD_PARAM);
- 252 if (!(pRemoteBuf[0] = VirtualAllocEx(hProcess, // hProcess
- 253 NULL, // lpAddress
- 254 dwSize, // dwSize
- 255 MEM_COMMIT, // flAllocationType
- 256 PAGE_READWRITE))) // flProtect
- 257 {
- 258 printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
- 259 return FALSE;
- 260 }
- 261 SIZE_T sz = 0;
- 262 if (!WriteProcessMemory(hProcess, // hProcess
- 263 pRemoteBuf[0], // lpBaseAddress
- 264 (LPVOID)¶m, // lpBuffer
- 265 dwSize, // nSize
- 266 &sz)) // [out] lpNumberOfBytesWritten
- 267 {
- 268 printf("写入大小:%d\n", sz);
- 269 printf("THREAD_PARAM WriteProcessMemory() fail : err_code = %d\n", GetLastError());
- 270 return FALSE;
- 271 }
- 272
- 273 // Allocation for ThreadProc() 写入代码
- 274 dwSize = abs((int)((DWORD)InjectCode - (DWORD)ThreadProc));
- 275
- 276 printf("dwSize:%d\n", dwSize);
- 277 //dwSize = 1024;
- 278 if (!(pRemoteBuf[1] = VirtualAllocEx(hProcess, // hProcess
- 279 NULL, // lpAddress
- 280 dwSize, // dwSize
- 281 MEM_COMMIT, // flAllocationType
- 282 PAGE_EXECUTE_READWRITE))) // flProtect
- 283 {
- 284 printf("VirtualAllocEx() fail : err_code = %d\n", GetLastError());
- 285 return FALSE;
- 286 }
- 287 sz = 0;
- 288 if (!WriteProcessMemory(hProcess, // hProcess
- 289 pRemoteBuf[1], // lpBaseAddress
- 290 (LPVOID)ThreadProc, // lpBuffer
- 291 dwSize, // nSize
- 292 &sz)) // [out] lpNumberOfBytesWritten
- 293 {
- 294 printf("写入大小:%d\n", sz);
- 295 printf("ThreadProc() WriteProcessMemory() fail : err_code = %d\n", GetLastError());
- 296 return FALSE;
- 297 }
- 298 printf("ThreadProc()写入大小:%d\n", sz);
- 299 if (!(hThread = CreateRemoteThread(hProcess, // hProcess
- 300 NULL, // lpThreadAttributes
- 301 0, // dwStackSize
- 302 (LPTHREAD_START_ROUTINE)pRemoteBuf[1], // dwStackSize
- 303 pRemoteBuf[0], // lpParameter
- 304 0, // dwCreationFlags
- 305 NULL))) // lpThreadId
- 306 {
- 307 printf("CreateRemoteThread() fail : err_code = %d\n", GetLastError());
- 308 return FALSE;
- 309 }
- 310
- 311 WaitForSingleObject(hThread, INFINITE);
- 312 CloseHandle(hThread);
- 313 CloseHandle(hProcess);
- 314 printf("code inject end\n");
- 315 return TRUE;
- 316 }
- 317
- 318
- 319 int _tmain(int argc, TCHAR *argv[])
- 320 {
- 321 /*if (argc != 3)
- 322 {
- 323 _tprintf(L"USAGE : %s <pid> <dll_path>\n", argv[0]);
- 324 return 1;
- 325 }*/
- 326 // change privilege
- 327 // 判断当前进程是否以管理员身份运行
- 328 checkAdmin();
- 329 /*if (!SetPrivilege(SE_DEBUG_NAME, TRUE))
- 330 return 1;*/
- 331 //TraversalProcess();
- 332 TCHAR pid[10];
- 333 TCHAR path[MAX_PATH];
- 334 system("tasklist");
- 335 printf("ok\n");
- 336 printf("输入要注入的进程pid:\n");
- 337 scanf_s("%ls", pid, 10);
- 338 printf("请选择功能:1.dll注入 2.代码注入\n");
- 339 int flag = 0;
- 340 scanf_s("%d", &flag, 1);
- 341 if (flag == 1) {
- 342 printf("输入要注入的dll路径:");
- 343 scanf_s("%ls", path,MAX_PATH);
- 344 // inject dll
- 345 if (InjectDll((DWORD)_tstol(pid), path))
- 346 _tprintf(L"InjectDll(\"%s\") success!!!\n", path);
- 347 else
- 348 _tprintf(L"InjectDll(\"%s\") failed!!!\n", path);
- 349 printf("输入q 卸载dll\n");
- 350 while (getchar() != 'q');
- 351 TCHAR *p = _tcsrchr(path, '\\');
- 352 if(EjectDll((DWORD)_tstol(pid), p+1))
- 353 printf("卸载dll成功!\n");
- 354 else
- 355 {
- 356 printf("卸载失败!\n");
- 357 }
- 358
- 359 system("pause");
- 360 return 0;
- 361 }
- 362 else if (flag == 2)
- 363 {
- 364 InjectCode((DWORD)_tstol(pid));
- 365 system("pause");
- 366 return 0;
- 367 }
- 368 else
- 369 {
- 370 system("pause");
- 371 return 0;
- 372 }
- 373 }
判断当前运行时的权限,以管理员身份运行。
- 1 BOOL IsAdmin(HANDLE hProcess)
- 2 {
- 3 HANDLE hToken = NULL;
- 4 OpenProcessToken(hProcess, TOKEN_QUERY, &hToken);
- 5
- 6 TOKEN_ELEVATION_TYPE tokenType = TokenElevationTypeDefault; // 用于接收令牌类型
- 7
- 8 DWORD dwRetSize = 0; // 用于接收函数输出信息的字节数
- 9
- 10 // 2. 查询进程令牌中的权限提升值.( 这个值会记录当前的令牌是何种类型( 细节在17_权限管理_令牌的获取.cpp ) )
- 11 GetTokenInformation(hToken,
- 12 TokenElevationType,// 获取令牌的当前提升等级
- 13 &tokenType,
- 14 sizeof(tokenType),
- 15 &dwRetSize // 所需缓冲区的字节数
- 16 );
- 17
- 18
- 19 // 根据令牌的类型来输出相应的信息
- 20 if (TokenElevationTypeFull == tokenType) {
- 21 // 3. 如果令牌是TokenElevationTypeFull , 则拥有至高无上的能力,可以给令牌添加任何特权
- 22 printf("管理员账户,并拥有全部的权限,可以给令牌添加任何特权\n");
- 23 return TRUE;
- 24 }
- 25 // 4. 如果是其他的, 则需要以管理员身份重新运行本进程. 这样就能以第三步的方法解决剩下的问题.
- 26 else if (TokenElevationTypeDefault == tokenType) {
- 27 printf("默认用户, 可能是一个普通用户, 可能是关闭UAC时登录的管理员用户\n");
- 28
- 29 // 调用系统函数IsUserAnAdmin, 进一步确定是普通用户还是管理员用户
- 30 return IsUserAnAdmin();
- 31 }
- 32 else if (TokenElevationTypeLimited == tokenType) {
- 33
- 34 // 判断受限制的用户是管理员
- 35 // 如果是管理员, 则这个令牌中会保存有管理员的SID
- 36
- 37 // 1. 获取系统内键管理员用户的SID
- 38 SID adminSid;
- 39 DWORD dwSize = sizeof(adminSid);
- 40 CreateWellKnownSid(WinBuiltinAdministratorsSid, // 获取SID的类型,这里是系统内键管理员
- 41 NULL, // 传NULL,获取本地计算机的管理员
- 42 &adminSid,// 函数输出的管理员SID
- 43 &dwSize // 输入结构的大小,也作为输出
- 44 );
- 45
- 46 // 获取本令牌的连接令牌(受限制的令牌都会有一个连接的令牌,受限制的令牌正式由主令牌所创建的. )
- 47 TOKEN_LINKED_TOKEN linkToken;
- 48 GetTokenInformation(hToken,
- 49 TokenLinkedToken, // 获取连接的令牌句柄
- 50 &linkToken,
- 51 sizeof(linkToken),
- 52 &dwSize
- 53 );
- 54
- 55 // 在连接的令牌中查找是否具有管理员的SID
- 56 BOOL bIsContain = FALSE; // 用于保存是否包含.
- 57 CheckTokenMembership(linkToken.LinkedToken, // 在这个令牌中检查
- 58 &adminSid, // 检查令牌中是否包含此SID
- 59 &bIsContain); // 输出TRUE则包含,反之不包含
- 60
- 61
- 62
- 63 if (bIsContain) {
- 64 printf("权限被阉割的受限制管理员账户, 部分权限被移处理\n");
- 65 }
- 66
- 67
- 68 return bIsContain; // 不是以管理员权限运行
- 69 }
- 70
- 71 return FALSE;
- 72 }
- 73
- 74 void checkAdmin() {
- 75 if (!IsAdmin(GetCurrentProcess())) {
- 76
- 77 // 以管理员身份运行本进程
- 78 // 1 获取本进程的文件路径.
- 79 TCHAR path[MAX_PATH] = { 0 }; // 需要初始化
- 80 DWORD dwPathSize = MAX_PATH;
- 81 QueryFullProcessImageName(GetCurrentProcess(), 0,
- 82 path,
- 83 &dwPathSize);
- 84
- 85 // 2 调用创建进程的API运行本进程.
- 86 ShellExecute(NULL, // 窗口句柄,没有则填NULL
- 87 _T("runas"), // 以管理员身份运行的重要参数
- 88 path, // 所有运行的程序的路径(这里是本进程)
- 89 NULL, // 命令行参数
- 90 NULL, // 新进程的工作目录的路径
- 91 SW_SHOW // 创建后的显示标志(最小化,最大化, 显示,隐藏等)
- 92 );
- 93
- 94 // 退出本进程
- 95 ExitProcess(0);
- 96 }
- 97 }
dll注入与代码注入的更多相关文章
- 代码注入——c++代码注入
代码注入之——c++代码注入 0x00 代码注入和DLL注入的区别 DLL注入后DLL会通过线程常驻在某个process中,而代码注入完成之后立即消失. 代码注入体积小,不占内存 0x01 通过c ...
- 注入攻击-SQL注入和代码注入
注入攻击 OWASP将注入攻击和跨站脚本攻击(XSS)列入网络应用程序十大常见安全风险.实际上,它们会一起出现,因为 XSS 攻击依赖于注入攻击的成功.虽然这是最明显的组合关系,但是注入攻击带来的不仅 ...
- C++代码注入
一.C++代码注入原则: 在注入代码中不允许使用API. 在注入代码中不允许使用全局变量. 在注入代码中不允许使用字符串(编译时也被当做全局变量). 在注入代码中不允许使用函数嵌套. 二.注入代码编写 ...
- 通过注入DLL修改API代码实现钩取(一)
通过注入DLL修改API代码实现钩取(一) Ox00 大致思路 通过CreateRemoteThread函数开辟新线程,并将DLL注入进去 通过GetProcessAddress函数找到需钩取的API ...
- 转:EasyHook远程代码注入
EasyHook远程代码注入 最近一段时间由于使用MinHook的API挂钩不稳定,经常因为挂钩地址错误而导致宿主进程崩溃.听同事介绍了一款智能强大的挂钩引擎EasyHook.它比微软的detours ...
- 32位汇编第三讲,RadAsm,IDE的配置和使用,以及汇编代码注入方式
32位汇编第三讲,RadAsm,IDE的配置和使用,以及汇编代码注入方式 一丶RadAsm的配置和使用 用了怎么长时间的命令行方式,我们发现了几个问题 1.没有代码提醒功能 2.编写代码很慢,记不住各 ...
- 安全之路 —— 借助DLL进行远程线程注入实现穿墙与隐藏进程
简介 大多数后门或病毒要想初步实现隐藏进程,即不被像任务管理器这样典型的RING3级进程管理器找到过于明显的不明进程,其中比较著名的方法就是通过远程线程注入的方法注入将恶意进程的DLL文 ...
- 详解C#泛型(二) 获取C#中方法的执行时间及其代码注入 详解C#泛型(一) 详解C#委托和事件(二) 详解C#特性和反射(四) 记一次.net core调用SOAP接口遇到的问题 C# WebRequest.Create 锚点“#”字符问题 根据内容来产生一个二维码
详解C#泛型(二) 一.自定义泛型方法(Generic Method),将类型参数用作参数列表或返回值的类型: void MyFunc<T>() //声明具有一个类型参数的泛型方法 { ...
- Windows下的代码注入
木马和病毒的好坏很大程度上取决于它的隐蔽性,木马和病毒本质上也是在执行程序代码,如果采用独立进程的方式需要考虑隐藏进程否则很容易被发现,在编写这类程序的时候可以考虑将代码注入到其他进程中,借用其他进程 ...
随机推荐
- Emacs和Vim:神的编辑器和编辑器之神, 到底哪个更好?
Emacs和Vim:神的编辑器和编辑器之神, 到底哪个更好? 在这个蔚蓝色的星球上,流传着两大神器的传说:据说Emacs是神的编辑器,而Vim是编辑器之神. 一些人勇敢地拾起了Vim或Emacs,却发 ...
- HTML5 Animation Creator | Hype 官方文档
HTML5 Animation Creator | Hype 官方文档 HTML5 Animation Build 7个 专业术语 场景,元素,属性,关键帧,动画,时间线,操作 Hype 官方文档 h ...
- Git Best Practice All In One
Git Best Practice All In One git workflow 本地开发环境: 开发人员自测的,可以是自己本地部署的静态服务器,当然也可类似是运行 npm server类似的环境, ...
- 为什么要抢挖Baccarat流动性挖矿的头矿?头矿的价值是什么?
今年下半年,DeFi流动性挖矿非常受投资者的欢迎,究其原因,其超高的挖矿回报率着实足够吸引无数投资者的眼球.而即将上线的Baccarat流动性挖矿,也未上线先火了一把.Baccarat是由NGK公链推 ...
- BGV暴涨千倍,未来或将超越YFI领跑DeFi全场!
毫无疑问,YFI在2020年上半年以一己之力掀翻了DeFi市场的热潮.迄今为止,YFI的新鲜资讯从不缺席,最近也是频频登上各大知名媒体热搜.其币价远远超过比特币价格,也让资本市场注意到DeFi市场原来 ...
- Recycle 只显示一行BUG
学习Recycle 两天了,照着网上的Adapter写了2个Demo,结果测试的时候发现,第一个Demo 显示.点击都正常,第二个Demo的Adapter合第一个一模一样,仅仅是类名不同,结果显示的时 ...
- C++ 多线程使用future传递异常
如果 std::async 调用的函数抛出异常,那么这个异常会被存储在值的位置,同时 future 变为 ready ,如果调用 get() 会重新抛出存储的异常. Note: 标准并没有指定原来的异 ...
- 字节码增强技术-Byte Buddy
本文转载自字节码增强技术-Byte Buddy 为什么需要在运行时生成代码? Java 是一个强类型语言系统,要求变量和对象都有一个确定的类型,不兼容类型赋值都会造成转换异常,通常情况下这种错误都会被 ...
- 最常用SQL joins:内连接(交集)、左外连接、右外连接、左连接、右连接、全连接(并集),全外连接
1.内连接.两个表的公共部分用Inner join,Inner join是交集的部分. Select * from TableA A inner join TableB B on A.key=B.ke ...
- 微信的两种access_token总结,不能混淆
大家需要弄清楚微信的网页授权token和公众号api调用授权token. 1.网页授权access_token 1.有效期:7200ms 2.微信网页授权是通过OAuth2.0机制实现的,在用户授权给 ...