TLS学习总结
我们有知道
调试器,在调试程序时会设断在OEP(修改第一个字节0xcc)。我在想,使用什么编程技术,代码可以在OEP前被执行。在网上找了些资料,在论坛上看到许多大牛,使用静态TLS做了好多有趣的事,今天自己也来终结下,,呵呵
1. 什么是TLS?
TLS是Thread Local
Storage(线程局部存储)的简称,是一项解决多线程内部变量使用问题的技术。用于将某些数据和一特定线程关联起来,即,这些数据为关联线程所独有
(私有)。在多线程编程中, 同一个变量, 如果要让多个线程共享访问, 那么这个变量可以使用关键字volatile进行声明;
而如果一个变量不想被多个线程共享访问, 那么就应该使用TLS。
2. 如何使用TLS编程?
TLS使用非常简单, 只要对变量声明时使用__declspec(thread)修饰就可以了。例如:
__declspec(thread) int g_nData = 0;
3.多线程中使用TLS的例子(动态)
相关API
Windows TLS的API: TlsAlloc, TlsFree, TlsSetValue, TlsGetValue。(动态TLS)
● DWORD TlsAlloc(); //(若要使用动态T L S,首先必须调用TlsAlloc函数)
这个函数命令系统对进程中的位标志进行扫描,并找出一个FREE标志。然后系统将该标志从FREE改为INUSE,并且TlsAlloc返回位数组中的标志的索引。DLL(或APP)通常将该索引保存在一个全局变量中,因为它的值是每个进程而不是每个线程使用的值。
● BOOL TlsSetValue( //将一个值放入线程的数组中
DWORD dwTlsIndex,
PVOID pvTlsValue);
● PVOID TlsGetValue(DWORD dwTlsIndex); //要从线程的数组中检索一个值
● BOOL TlsFree(DWORD dwTlsIndex); //当在所有线程中不再需要保留TLS槽时
例子:
#include <windows.h>
#include "stdio.h"
#define ThreadCound 4 //创建线程个数
DWORD dwTlsIndex;
//全局变量
int iNUM_OF_CALL_COMMON=0;
int iNUM_OF_CALL_THREAD=0;
VOID CommonFunc(VOID)
{
LPVOID lpvData;
// Retrieve a data pointer for the current thread.
iNUM_OF_CALL_COMMON++;
lpvData = TlsGetValue(dwTlsIndex);
if ((lpvData == 0) && (GetLastError() != ERROR_SUCCESS))
exit(0);
// Use the data stored for the current thread.
printf("common: thread %d: lpvData=%lx\n",
GetCurrentThreadId(), lpvData);
Sleep(5000);
}
DWORD WINAPI ThreadFunc(VOID)
{
LPVOID lpvData;
// Initialize the TLS index for this thread.
iNUM_OF_CALL_THREAD++;
lpvData = (LPVOID) LocalAlloc(LPTR, 256);
if (! TlsSetValue(dwTlsIndex, lpvData))
exit(0);
printf("thread %d: lpvData=%lx\n", GetCurrentThreadId(), lpvData);
CommonFunc();
// Release the dynamic memory before the thread returns.
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != 0)
LocalFree((HLOCAL) lpvData);
return 0;
}
void main(void)
{
DWORD dwThreadId;
HANDLE hThread[ThreadCound];
int i;
//Allocate a TLS index
if ((dwTlsIndex =TlsAlloc()) == TLS_OUT_OF_INDEXES)
{
exit(0);
}
for (i = 0; i<ThreadCound; i++)
{
hThread[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadFunc, NULL, 0, &dwThreadId);
if(hThread == NULL)
{
exit(0);
}
}
for (i = 0; i < ThreadCound; i++)
{
WaitForSingleObject(hThread, INFINITE);
}
TlsFree(dwTlsIndex);
printf("The nums of thread is: %d\n",iNUM_OF_CALL_THREAD);
printf("The nums of call is: %d\n",iNUM_OF_CALL_COMMON);
}
以上是动态TLS在多线程中的使用.
而利用静态线程局部存储可以玩弄调试器甚至可以感染一些pe.
静态线程局部存储是在Windows的PE/COFF可执行文件格式中所支持的静态线程局部存储。即在PE头中数据目录表中的第10项,这一项指向的是Tls目录,Tls目录的结构如下:
IMAGE_TLS_DIRECTORY32 STRUCTStartAddressOfRawData dd ?EndAddressOfRawData dd ?AddressOfIndex
dd ?AddressOfCallBacks dd ?SizeOfZeroFill
dd ?Characteristics
dd ?IMAGE_TLS_DIRECTORY32 ENDS比较重要的是该结构的第四项,是指向Tls
CallBack的数组,该数组里面存放了一个或多个TLS回调函数.而TLS回调函数要执行大概经历以下三个步骤:1.程序在链接时,连接器要在PE文
件的目录表创建TLS目录2.在创建线程时,PE加载器会从TEB(当前线程环境块,通过FS寄存器获取)中获取指向TLS回调函数数值的指针(TEB偏
移2Ch).3.查看TLS 回调函数数组是否为空,不为空时加载器会依次执行回调函数。
静态TLS代码;
#include "stdio.h"#include <windows.h>//告诉编译器在PE文件中要创建TLS目录#pragma
comment(linker,
"/INCLUDE:__tls_used")//TLS_CallBack()函数,第二个参数决定了函数在那种情况下(DllMain函数一样)
/* DLL_PROCESS_ATTACH:是指新进程创建时,初始化主线程时执行
DLL_PROCESS_DETACH: 是指在进程终止时执行
DLL_THREAD_ATTACH: 是指创建新线程时,但是不包括主线程
DLL_THREAD_DETACH: 是指在所有线程终止时执行,但是同样不包括主线程*/void go_anit(){
ExitProcess(0);}void __stdcall my_tls_callback(PVOID h, DWORD reason,
PVOID pv){ //仅在进程初始化创建主线程执行 if (reason ==
DLL_PROCESS_ATTACH) { //检查OEP
IMAGE_DOS_HEADER *DosHeader = (IMAGE_DOS_HEADER*)GetModuleHandle(NULL);
IMAGE_NT_HEADERS *NtHeader =
(IMAGE_NT_HEADERS*)(((DWORD)DosHeader)+ (DWORD)DosHeader->e_lfanew);
BYTE *ope =
(PBYTE)(NtHeader->OptionalHeader.AddressOfEntryPoint +
(DWORD)DosHeader); if (*ope == 0xcc) {
go_anit(); } }}/*
下面创建一个TLS段 ".CRT$XLB"的含义是:".CRT"表明是使用C RunTime机制 $后面的XLB中,
X表示随机的标识 L表示TLS Callback section B可以被换成B到Y的任意一个字母,.
$是给连接器的。*/#pragma data_seg(".CRT$XLB")//定义多个TLS_CallBack
PIMAGE_TLS_CALLBACK p_Tls_CallBack[] = {my_Tls_CallBack1,
my_Tls_CallBack2,my_Tls_CallBack3, 0};PIMAGE_TLS_CALLBACK p_Tls_CallBack
= my_tls_callback;#pragma data_seg()void main(){ printf("Hello
TLS\n"); getchar();}
这种方法就能真真的玩弄调试器吗?显然不是的,首先我们来看下创建一个进程的过程1.打开将要在该进程中执行的映像文件。
首先操作系统找到执行的Windows映像然后创建一个内存区对象,以便后面将它映射到新的进程地址空间中。
2.创建Windows执行体进程对象。
接下来操作系统调用内部的系统函数NtCreateProcess来创建一个Windwos执行体进程对象。具体步骤是:
(1)建立EPROCESS
(2)创建初始的进程地址空间(3)初始化内核进程块KPROCESS
(4)结束进程地址空间的创建过程
(5)建立PEB
(6)完成执行体进程对象的创建过程
3.创建初始线程(栈、堆执行环境初始化及执行线程体对象)。
这时候Windows执行体进程对象已经完全建立完成,但它还没有线程所以无法执行,接下来系统调用NtCreateThread来创建一个挂起的新线程它就是进程的主线程体。
4.通知Windows子系统新进程创建了(子系统是操作系统的一部分它是一个协助操作系统内核管理用户态/客户方的一个子系统具体的进程为
Csrss.exe)。
接下来操作系统通过客户态(Kernel32.dll)给Windows子系统(Csrss)发送一个新进程线程创建的数据消息,让子系统建立自己的进程线程管理块。当
Csrss接收到该消息时候执行下面的处理:
*复制一份该进程和线程句柄
*设置进程优先级
*分配Csrss进程块
*把新进程的异常处理端口绑定到Csrss中,这样当该进程发生异常时,Csrss将会接收到异常消息
*分配和初始化Csrss线程块
*把线程插入到进程的线程列表中
*把进程插入到Csrss的线程列表中
*显示进程启动光标
5.开始执行初始线程(如果创建时候指定了线程的CREATE_SUSPENDED状态则线程暂时挂起不执行)。
到这里进程环境已经建立完毕进程中开始创建的主线程到这里获得执行权开始执行线程
6.在新进程和线程环境中完成地址空间的初始化(比如加载必须的DLL和库),然后开始到进程入口执行。
到这步实质是调用ldrInitializeThunk来初始化加载器,堆管理器NLS表TLS数组以及临界区结构,并且加载任何必须要的DLL并且用
DLL_PROCESS_ATTACH功能代码来调用各DLL入口点,最后当加载器初始化例程返回到用户模式APC分发器时进程映像开始在用户模式下执行,然后它调用线程启动函数开始执行。
操作系统会抛出一个CreateProcessEvent事件,早于Tls_CallBack函数,同时ExceptionEvent事件也是早于
Tls_callback的所以我们可以设置调试器第一次断点“系统断点” 本实验我用的Immunity Debugger调试器,
OD相似选项->调试选项->事件->设置第一次暂停于 “系统断点” 单步跟中会发现 对当前进程信息设置
file:///C:/Users/asus/AppData/Local/youdao/ynote/images
/C357E1C9D1184437819E2C6A7919ADB8/clipboard.png到此跟进,,,查了好多资料也不知道这个函数是啥
子,,求大牛指导
file:///C:/Users/asus/AppData/Local/youdao/ynote/images
/DCE533091FD64FE48C52145B1484E6F6/clipboard.png之后见call跟进,,,到ZwContiune函
数, 继续执行线程,他的一个参数是CONTEXT指针,找到Context->EIP
file:///C:/Users/asus/AppData/Local/youdao/ynote/images/2D7F6F8AC69F46B293E3ED5CC47F167E/clipboard.png出现OPE地址
file:///C:/Users/asus/AppData/Local/youdao/ynote/images/000F6F5D807640FB92E5BFC4E562AF28/clipboard.png
跟进call eax
file:///C:/Users/asus/AppData/Local/youdao/ynote/images/98213D4D5ACD48A3A6C4BF957917E3B1/clipboard.pngcall edx 则进入OEP
file:///C:/Users/asus/AppData/Local/youdao/ynote/images/6A1D98036EB64140BAEF6A1F7B67F792/clipboard.png
file:///C:/Users/asus/AppData/Local/youdao/ynote/images
/05EAE8B3357D45CCA440A3082C120B11/clipboard.png这个过程真的还很模糊,很多疑问,不知道进入OEP之
前的那些函数具体的干了啥,,,,求指导
TLS学习总结的更多相关文章
- 在 ASP.NET MVC 中使用 HTTPS (SSL/TLS) -- 学习
在 ASP.NET MVC 中使用 HTTPS (SSL/TLS) IS 7如何实现http重定向https HTTPS 升级指南
- Python Web学习笔记之SSL,TLS,HTTPS
一. SSL 1. SSL简介 SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持.SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可 ...
- https、ssl、tls协议学习
一.知识准备 1.ssl协议:通过认证.数字签名确保完整性:使用加密确保私密性:确保客户端和服务器之间的通讯安全 2.tls协议:在SSL的基础上新增了诸多的功能,它们之间协议工作方式一样 3.htt ...
- 常用加密算法学习总结之数字证书与TLS/SSL
数字证书 对于一个安全的通信,应该有以下特征: 完整性:消息在传输过程中未被篡改 身份验证:确认消息发送者的身份 不可否认:消息的发送者无法否认自己发送了信息 显然,数字签名和消息认证码是不符合要求的 ...
- NodeJS学习笔记 (32)安全加密-tls
https://github.com/chyingp/nodejs-learning-guide
- 《Django By Example》第二章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:翻译完第一章后,发现翻译第二章的速 ...
- tomcat集群学习记录1--初识apache http server
起因 平时开发的时候我都是用eclipse把代码部署到本地tomcat的,当然只会去跑1台tomcat啦... 偶尔有时候解决问题需要去公司测试环境找日志.连上公司测试环境以后发现竟然有2台weblo ...
- SIP学习笔记(转)
本文转发自:http://blog.chinaunix.net/uid-20655530-id-1589483.html 学习 SIP 协议最快捷的方法是通过范例来学习, 找到了一个完整的呼叫流程,l ...
- Docker学习笔记
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化.容器是完全使用沙箱机制,相互之间不会有任何 ...
随机推荐
- linux中PHP dirname(__FILE__)路径问题解决
近期在给wordpress开发模板功能时发现,直接使用include(“文件名”)的形式调用其他php代码片段时会出现路径错误.之前服务器环境一直都是iis,未曾出现过类似的BUG,但换成linux服 ...
- ajax返回值中有回车换行、空格解决方法
最近在写一个页面,用jquery ajax来实现判断,刚写好测试完全没有问题,过了两天发现出现问题,判断不成了.后来发现所有alert出来的返回值前面都会加若干换行和空格.(至今不明白,同一台电脑,同 ...
- MySql中把一个表的数据插入到另一个表中的实现代码
web开发中,我们经常需要将一个表的数据插入到另外一个表,有时还需要指定导入字段,设置只需要导入目标表中不存在的记录,虽然这些都可以在程序中拆分成简单sql来实现,但是用一个sql的话,会节省大量代码 ...
- Delphi XE5 for android 调用Java类库必看的文件
C:\Program Files\Embarcadero\RAD Studio\12.0\source\rtl\android 的目录 Androidapi.AppGlue.pasAndroidapi ...
- IT新人论成长
说自己是新人,其实自己也不新了,2012年毕业,辗转3个城市,现在在上海一家公司,工资不到5K. 在来现在公司之前,我从事web后台开发,采用MVC模式,虽然做了不少的网站,但感觉自己的水平还是在底层 ...
- Maven学习随记
慕课网视频教程:http://www.imooc.com/learn/443 ====Maven是什么 Maven是基于项目对象模型(POM),可以通过一小段描述信息来管理项目的构建.报告和文档的软件 ...
- iPhone开发中的技巧整理
1.NSCalendar用法 -(NSString *) getWeek:(NSDate *)d { NSCalendar *calendar = [[NSCalendar alloc] initWi ...
- Oracle DB 分区特性概述 Overview of Partitions
概述:在Oracle数据库中,分区(partitioning)可以使非常大的表(table)或索引(index)分解为小的易管理的块(pieces),这些块被称作分区(partitions).每个分区 ...
- C 关于二叉查找树的回顾,并简述结构接口设计
前言 最经想改写C用的配置读取接口, 准备采用hash或二叉树提到原先用的链表,提高查找效率.就回顾一下二叉树,这里分享一下二叉查找树,代码很精简的, 适合学习运用二叉树查找. 需要基础 1.具备C ...
- iframe 父子窗口相互之间调用语法
一.父窗口调用iframe子窗口方法 1.HTML语法:<iframe name="myFrame" src="child.html"></i ...