最最最刺激的来啦|他来啦

CreateProcess(…)我一直想试试,今天终于遇到了,一定要试一个痛快。

official-website

不过还是得从基本的函数参数开始,然后有一些参数设计一些略微复杂的结构体(主要是有些参数基本用不到)。皆さんこんじわ

//CreateProcessW function (processthreadsapi.h)
CreateProcess也有ANSI版本的,这里以UNICODE版本简述

//先上函数的声明,稍微有点长,不过萌大奶
BOOL CreateProcessW(
  LPCWSTR               lpApplicationName,
  LPWSTR                lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL                  bInheritHandles,
  DWORD                 dwCreationFlags,
  LPVOID                lpEnvironment,
  LPCWSTR               lpCurrentDirectory,
  LPSTARTUPINFOW        lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);

①从前两个开始,其实也是因为这俩兄弟相互有影响的
第一个名字,第二个命令行;一言概之是这样子的。不过也可以传入NULL。因此会稍微有一些复杂。
1.第一个不为NULL,第二个为NULL:
此时直接以第一个为命令行,进行检索时只会在本地目录下找一个可执行文件,或者由于传入的是一个完整的目录加文件名加后缀,到那个位置打开文件执行。
2.第一个为NULL,第二个不为NULL:
第二个参数如果是一个文件名,那么会被填充出后缀(exe这种)然后再去各个目录下查找有无。顺序参考:

1.The directory from which the application loaded.
2.The current directory for the parent process.
3.The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of this directory.
4.The 16-bit Windows system directory. There is no function that obtains the path of this directory, but it is searched. The name of this directory is System.
5.The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
6.The directories that are listed in the PATH environment variable. Note that this function does not search the per-application path specified by the App Paths registry key. To include this per-application path in the search sequence, use the ShellExecute function.

有点长,不过摩的关系。
3.两个都不为NULL:
就名字和命令行了。然后名字参考还是像lpApplicationName为NULL一样的规范,而命令行则是后面的那一串

②第三个第四个参数主要考虑进程和线程的安全性,这个都取NULL可以继承父进程的。也可以取一个 SECURITY_ATTRIBUTES structure来设置安全的属性

③第五个bInheritHandles这个主要考虑继承,可以通过这个防止子进程越权控制父进程的内核对象

④第六个dwCreationFlags主要用于设置新进程的优先级(通过标志位)
这个Mark一下,下次再细看

⑤第七个lpEnvironment是一个指向environment block的指针,如果为NULL可以继承父进程的,environment block指针则可以通过上文得到(阿巴阿巴)

⑥第八个lpCurrentDirectory一看就是指向当前目录的一个指针呗,也可以通过API获得

⑦第九个lpStartupInfo是
A pointer to a STARTUPINFO or STARTUPINFOEX structure

⑧第十个lpProcessInformation
A pointer to a PROCESS_INFORMATION structure that receives identification information about the new process

接下来开始激动人心的测试啦(其实有一些结构体的参数我还没仔细琢磨,不过萌大奶)

//这个程序能够打开一个notepad
#include <Windows.h>
#include <tchar.h>
#include <strsafe.h>


#ifndef _UNICODE
#define _UNICODE
#endif


int _tmain(int argc, TCHAR* argv[], TCHAR* env[])
{
	LPCWSTR lpAppName = NULL;
	LPWSTR lpCmdLine = new TCHAR;
	LPWSTR lpCmdLine = (LPWSTR)malloc(18);
	StringCchCopy(lpCmdLine, wcslen(L"notepad")+1, L"notepad");

	LPSECURITY_ATTRIBUTES lpProcessAttri = NULL;
	LPSECURITY_ATTRIBUTES lpThreadAttri = NULL;
	BOOL bInheritH = FALSE;

	DWORD dwCreadtionFlags = 0;
	// dwCreadtionFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP;
        //用上面这条语句贼能报错,现在还么研究透。
	LPVOID lpEnvironment = NULL;
	LPCWSTR lpCurrentDir = NULL;

	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	ZeroMemory(&si, sizeof(si));
	ZeroMemory(&pi, sizeof(pi));
	si.cb = sizeof(si);


	CreateProcessW(
		lpAppName,
		lpCmdLine,
		lpProcessAttri, 
		lpThreadAttri, 
		bInheritH,
		dwCreadtionFlags,
		lpEnvironment, 
		lpCurrentDir,
		&si, &pi);
	WaitForSingleObject(pi.hProcess, INFINITE);
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	return 0;
}

看看微软人家写的,我哭了

我又一次写出了屎山,太难受了。

我改进了一下,让进程自己创建新的自己。

#include <Windows.h>
#include <tchar.h>
#include <strsafe.h>


#ifndef _UNICODE
#define _UNICODE
#endif


int _tmain(int argc, TCHAR* argv[], TCHAR* env[])
{
	LPCWSTR lpAppName = NULL;
	LPWSTR lpCmdLine = (LPWSTR)malloc(18);
	StringCchCopy(lpCmdLine, wcslen(L"notepad")+1, L"notepad");
	LPWSTR lpCmdLine2 = (LPWSTR)malloc(30);
	StringCchCopy(lpCmdLine2, wcslen(L"ERROR_WATCH.exe") + 1, L"ERROR_WATCH.exe");

	LPSECURITY_ATTRIBUTES lpProcessAttri = NULL;
	LPSECURITY_ATTRIBUTES lpThreadAttri = NULL;
	BOOL bInheritH = FALSE;

	DWORD dwCreadtionFlags = 0;
	// dwCreadtionFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP;
	LPVOID lpEnvironment = NULL;
	LPCWSTR lpCurrentDir = NULL;

	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	ZeroMemory(&si, sizeof(si));
	ZeroMemory(&pi, sizeof(pi));
	si.cb = sizeof(si);


	CreateProcessW(
		lpAppName,
		lpCmdLine,
		lpProcessAttri, 
		lpThreadAttri, 
		bInheritH,
		dwCreadtionFlags,
		lpEnvironment, 
		lpCurrentDir,
		&si,
		&pi);

	CreateProcessW(
		lpAppName,
		lpCmdLine2,
		lpProcessAttri,
		lpThreadAttri,
		bInheritH,
		dwCreadtionFlags,
		lpEnvironment,
		lpCurrentDir,
		&si,
		&pi);
	WaitForSingleObject(pi.hProcess, INFINITE);
	CloseHandle(pi.hProcess);
	CloseHandle(pi.hThread);
	return 0;
}
//跑这个很危险,建议开虚拟机跑

读取进程的环境变量

甘了半天各种出错,mark一下一丢丢成果

//涉及一些Win32 Api
ExpandEnvironmentStringsA(
  LPCSTR lpSrc,
  LPSTR  lpDst,
  DWORD  nSize
);
//用于拓展可替换字符

LPWCH GetEnvironmentStringsW();
//用于返回一个指向进程环境变量的指针

DWORD GetEnvironmentVariableA(
  LPCSTR lpName,
  LPSTR  lpBuffer,
  DWORD  nSize
);
//用于确定某个环境变量是否存在

UINT SetErrorMode(
  UINT uMode
);
//设置系统如何处理错误(未对齐,文件查找错误等)

DWORD GetCurrentDirectory(
  DWORD  nBufferLength,
  LPTSTR lpBuffer
);
//获取当前目录

BOOL SetCurrentDirectory(
  LPCTSTR lpPathName
);
//设置当前目录

HANDLE CreateFileTransactedW(
  LPCWSTR               lpFileName,
  DWORD                 dwDesiredAccess,
  DWORD                 dwShareMode,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  DWORD                 dwCreationDisposition,
  DWORD                 dwFlagsAndAttributes,
  HANDLE                hTemplateFile,
  HANDLE                hTransaction,
  PUSHORT               pusMiniVersion,
  PVOID                 lpExtendedParameter
);
//判断windows系统版本
//也可以用GetVersion这个函数来整,还有一个结构体与之相关,应该挺有历史的OSVERSIONINFOEXA structure
#include <Windows.h>
#include <tchar.h>

#ifndef _UNICODE
#define _UNICODE
#endif

void ReadEnvBlock(LPWCH ptToEnv);

int _tmain(int argc,TCHAR* argv[],TCHAR* env[])
{
	for (int i = 0; i < 10; ++i)
	{
		wprintf(L"%s\n",env[i]);
	}
	wprintf(L"*****************\n");
	LPWCH ptToEnv = GetEnvironmentStringsW();
	ReadEnvBlock(ptToEnv);
	system("pause");
	return 0;
}

void ReadEnvBlock(LPWCH ptToEnv)
{
	while (ptToEnv != NULL)
	{
		if (*ptToEnv != L'=')
		{
			wprintf(L"%s\n", ptToEnv);
		}
		ptToEnv += wcslen(ptToEnv) + 1;
		if (*ptToEnv == L'\0')
		{
			break;
		}
	}
}

第一种方法是直接用main函数中的env参数直接读取,很方便
第二种就是用GetEnvironmentStringsW()读取进程环境变量,麻烦就在返回的宽字节指针是一个一维(客观由于环境变量的每个字符串不等长)的一个指针。于是写了一个轮子,运行起来还可以。

想起来今天贼尴尬的TCHAR宏定义问题,这个在编译器中就已经处理了,当我再次宏定义_UNICODE就改变不了TCHAR。

#include <Windows.h>
#include <tchar.h>

#ifndef _UNICODE
#define _UNICODE
#endif


int _tmain(int argc,TCHAR* argv[],TCHAR* env[])
{
	DWORD chValue = ExpandEnvironmentStrings(L"PATH='%PATH%'", NULL, 0);
	PTSTR pszBuffer = new TCHAR[chValue];
	chValue = ExpandEnvironmentStrings(L"PATH='%PATH%'", pszBuffer, chValue);
	_tprintf(L"%s", pszBuffer);
	delete[] pszBuffer;
	return 0;
}
//这个通过<!-- wp:enlighter/codeblock {"language":"cpp"} -->
<pre class="EnlighterJSRAW" data-enlighter-language="cpp" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">#include <Windows.h>
#include <tchar.h>

#ifndef _UNICODE
#define _UNICODE
#endif


int _tmain(int argc,TCHAR* argv[],TCHAR* env[])
{
	DWORD chValue = ExpandEnvironmentStrings(L"PATH='%PATH%'", NULL, 0);
	PTSTR pszBuffer = new TCHAR[chValue];
	chValue = ExpandEnvironmentStrings(L"PATH='%PATH%'", pszBuffer, chValue);
	_tprintf(L"%s", pszBuffer);
	delete[] pszBuffer;
	return 0;
}
//这个通过ExpandEnvironmentStrings函数拓展“可替换字符串”(比如%PATH%这种)返回一个指向拓展完成之后的字符串的指针。
//观察了一番发现我调用时先查询了系统的环境变量,再查询了用户的环境变量。
//那和进程的环境变量有什么关系呢?存疑嗷。
//可能有人会好奇为什么EpEnS执行了两次,第一次其实是为了返回拓展之后的长度,再分配内存给他。

进程“传递”内核对象的东东

怕忘记,就mark一下

第一种方法是在创建的时候直接创建可以继承的内核对象

//CreateProcessA function (processthreadsapi.h)
BOOL CreateProcessA(
  LPCSTR                lpApplicationName,
  LPSTR                 lpCommandLine,
  LPSECURITY_ATTRIBUTES lpProcessAttributes,
  LPSECURITY_ATTRIBUTES lpThreadAttributes,
  BOOL                  bInheritHandles,
 
  DWORD                 dwCreationFlags,
  LPVOID                lpEnvironment,
  LPCSTR                lpCurrentDirectory,
  LPSTARTUPINFOA        lpStartupInfo,
  LPPROCESS_INFORMATION lpProcessInformation
);
//其中bInheritHandles是用于标志继承属性的,最后会在进程句柄表中标志位1之类
//对象句柄的继承只会在生成子进程的时候发生(也就是先删对象,就继承不到了;后来父继进程创建了新的对象,子也继承不到了)

②使用对象命名(能否传递也和内核对象是否全局有关(记不清了))

/*举一个创建的例子表明在哪可以写入对象名字(不过很多内核对象没名字,其实也就不用担心被别的调用或者“架空”(俺自己造的词),虽然不很清楚为什么)*/
HANDLE OpenMutex(
    DWORD dwDesireAccess,
    BOOL bInheritHandle,
    PCTSTR pszName);
//其中pszName就是可以写入名字的
//CreateMutexA function (synchapi.h)
HANDLE CreateMutexA(
  LPSECURITY_ATTRIBUTES lpMutexAttributes,
  BOOL                  bInitialOwner,
  LPCSTR                lpName
);
//lpName就可以传名字,如果有就忽略前两项了。
//坏蛋也可以通过名字的方法废掉一些内核对象(跃跃欲试),因为内核对象创建的时候不会检查是否覆盖。

③复制对象句柄(这个看了好久)

//DuplicateHandle function (handleapi.h)
BOOL DuplicateHandle(
  HANDLE   hSourceProcessHandle,
  HANDLE   hSourceHandle,
  HANDLE   hTargetProcessHandle,
  LPHANDLE lpTargetHandle,
  DWORD    dwDesiredAccess,
  BOOL     bInheritHandle,
  DWORD    dwOptions
);
//这个老折磨人了
第一个参数是进程S(Sourse)的handle,可以使用GetCurrentProcess()获得
第二个参数是进程要复制内核对象的句柄
第三个参数是目标进程的句柄
第四个参数记录着目标进程的句柄表中写入的索引位置(handle)
//来一个例子
HANDLE hObjInProcessS = CreateMutex(NULL,FALSE,NULL);
HANDLE hProcessT = OpenProcess(PROCESS_ALL_ACCESS,FALSE,dwProcessIdT);
HANDLE hObjInProcessT;
DuplicateHandle(GetCurrentProcess(),hObjInProcessS,hProcessT,&hObjInProcessT,0,FALSE,DUPLICATE_SAME_ACCESS)
//后面主要和传递过去内核对象的标志控制有关

这是什么妖怪

今天在逆向时进入一个_strlen函数。。

这个循环非常和善,用来确定eax的返回大小,表示字符串长度。

不过问题不在这,似乎我时被迷惑了

这个圈起来的给我看蒙了。这里改变了edx有什么用嘛,在这个函数结束之后,edx又被从栈中返回了,改变了eax,但是下面循环的时候eax也被重新赋值了,这就很迷惑=。=(太菜了)

C语言数字、指针、布尔值灵活利用的典范

先思考这个问题,假设我有一组数,我需要在每两个数字中间加入一个空格然后再输出出来,末尾和头部都没有空格,应该如何实现。

实现方法很简单,加个if就可以实现了,比如我想输出1 2 3 4 5 6,就可以这样:

#include<stdio.h>

int main()
{
	int arr[6] = { 1, 2, 3, 4, 5, 6 };
	for (int i = 0; i < 6; i++)
	{
		if (i == 5) //输出到最后一个数字了
			printf("%d", arr[i]);
		else
			printf("%d ", arr[i]);
	}
	return 0;
}

我们利用一个if语句来判断是否输出到最后一个数字,来选择是否输出占位的空格。事实上,也可以对第一个数字特殊对待,然后剩余数据输出时附带一个空格。

但是如果我们这么做呢:

#include<stdio.h>

int main()
{
	int arr[6] = { 1, 2, 3, 4, 5, 6 };
	for (int i = 0; i < 6; i++)
		printf(" %d" + !i, arr[i]);
	return 0;
}

极其简单的几句话完成了我们之前要的操作,一气呵成。

我详细叙述下它的过程:

玄机在于printf语句的那个加法上。C语言的字符串常量作参数,实际上是一个指向常量池中该字符串的指针,也就是可以进行运算。然后就是一个!i,这是把C语言中的整型数据当布尔值来看待,对他取非。当循环第一趟时,i=0,0在C语言中是false,非i得到true,默认为1,当那个指针+1,指针就从原来的指向空格,变成指向“%d”,那么就不会输出空格。而当之后几轮时,i均为非0数,C语言中为true,那么取非则得到false,即0,那么原指针+0,就是它本身,也就会正常输出空格,最后达到我们的要求。

这种写法说实话有很大局限性,可读性也极差,但是却很好的体现了C语言的灵活之处。C语言对数据和内存的操作极为灵活,这就要求使用者有很高的水平。这种写法不推荐在正式的程序设计中使用,但是对于开拓思维、优化程序是很有帮助的。

kali子系统启用root用户骚操作

kali平时使用大家都是直接用root用户,省(tou)事(lan)。

安装Windows的子系统的时候,kali会默认要求你创建新管理员用户,到时候再去删掉这个用户再启用root用户太麻烦了。

其实只要在第一次安装提示创建新用户的时候,直接关掉wsl再重新打开,你就会发现你直接使用默认的root用户登录了,非常偷懒。

LinkStart

到学校得路上贼坎坷,办好了麻烦事就可以开始啦*-*。

菱形继承虚表 看着好晕(等我看一遍C++再看吗。。)

买了本Windows程序设计,没想到是C#和XMHL相关,虽然似乎对学win32 API很有帮助就是,还有C++的面向对象的东西要看看。一下子就多了好多任务的亚子ToT。

question

计算机编码的奇迹 补码

给两个条件
当前补码+1等于下一个补码(比如【1】+1=【2】)
互为相反数的补码相加为【0】(比如【1】+【-1】=【0】)

产生的编码是唯一的嘛(充分必要嘛)