typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];

本文介绍了一种利用checked_delete和checked_array_delete实现的安全删除方法,通过在编译阶段捕获潜在错误来增强程序的健壮性。

看智能指针的时候遇到一组函数蛮有意思的,即checked_delete(T* x)和checked_array_delete(T* x),这两个函数的作用是安全删除参数所指向的变量或数组。

[cpp]  view plain  copy
  1. template<class T> inline void checked_delete(T* x)  
  2. {  
  3.   typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];  
  4.   (voidsizeof(type_must_be_complete);  
  5.   delete x;  
  6. }  
  7.   
  8. template<class T> inline void checked_array_delete(T* x)  
  9. {  
  10.   typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];  
  11.   (voidsizeof(type_must_be_complete);  
  12.   delete[] x;  
  13. }  

函数只有最后一行不同,分别调用了delete和delete[]操作符,顺便说一句,在C/C++中指针既可以指向一个变量,也可以指向一个数组,因此这两个函数仅仅从参数无法区别出来待删除的是单个变量还是数组,只能由调用者自行保证调用了正确的函数。

函数总共三行语句,第三行是根本目的,很容易理解,前两行的目的就是为了所谓的安全性了。怎么个安全法呢?这两个函数是函数模版,在编译时无法确定参数的类型,而动态运行时的错误是比较棘手的,所以用这行代码:

[cpp]  view plain  copy
  1. typedef char type_must_be_complete[ sizeof(T)? 1: -1 ];  

来将运行时错误转变为编译期错误。这句话其实就是定义了一个固定大小的char型数组,数组名为type_must_be_complete,数组大小是多少呢?是sizeof(T)?1:-1, ?:这个三元操作符大家都很熟悉了,若sizeof(T)非0,这个表达式的值为1,即typedef了一个大小为1的char型数组,否则定义一个大小为-1的数组。数组大小还能为负数?当然不能,于是就会报错,而且是编译期错误,于是就将一个动态运行时错误在编译时就发现了。

接下来解释sizeof什么时候返回0. C/C++语言本身似乎没有这种情况,但有些编译器会作一些扩展,比如GCC对于incomplete type使用sizeof时,会返回0.那什么又叫做incomplete type呢,就是那些声明了,但没有定义的类型,例如:

[cpp]  view plain  copy
  1. class A;  
  2.   
  3. extern A a;  

C++标准允许通过一个 delete 表达式删除指向不完全类的指针。如果该类有一个非平凡的析构函数,或者有一个类相关的 delete 操作符,那么其行为就是无定义的。因此编译器作了这种扩展,以将这种未定义的行为转为编译期错误,帮助程序员们及早发现。

函数的第二行语句的作用据说是为了防止编译器优化,因为编译器检测到typedef的类型未被使用到的话可能就会将其优化掉,因而第二行语句使用了这个类型,告诉编译器这个typedef是有用的,不能优化掉。至于(void)强制类型转换,问了同学,说是为了消除编译器对未使用sizeof返回值的警告。

仅仅几行代码都有这么多讲究,要学的东西还很多啊。错误和疏漏的地方欢迎批评指正!

#include <string.h> #include <stdlib.h> #include "event2/buffer.h" #include "udp_punch.h" #include "udt_c.h" #include "utils.h" #include "session.h" #ifdef HAVE_VS #include "../include/video_share.h" #endif #define PUNCH_SYN_SENT 1 #define PUNCH_SYN_RCVD 2 #define PUNCH_SYNACK_RCVD 3 #define PUNCH_UDT_CONNECTED 4 #define PUNCH_OPT_ACK 0x00000001 #ifndef PUNCH_PREPARING_TTL #define PUNCH_PREPARING_TTL 5 #endif /* private IP range -- RFC1918 * 10.0.0.0/8 * 172.16.0.0/12 * 192.168.0.0/16 */ #define PRIVATE_NIPV4(__nip) (((__nip)&0x000000FF) == 0x0000000A || \ ((__nip)&0x0000F0FF) == 0x000010AC || \ ((__nip)&0x0000FFFF) == 0x0000A8C0) #define UDT_SNDBUF_SIZE (256*1024) #define UDT_RCVBUF_SIZE (16*1024) #define UDT_MAX_SEG_SIZE 1420 #ifndef UDT_SNDLOWAT #define UDT_SNDLOWAT 1 #endif typedef struct punch_inner_data_s { struct event* evstun; /* event for UDP */ struct sockaddr_in sin_peer; /* peer address */ int status; int punch_prepared; int punch_try_cnt; int fd_udt; unsigned int udt_sndlowat; struct evbuffer* evbuf_out; } punch_inner_data_t; static const char* _sockaddr_ntoa(const struct sockaddr_in* sin) { static char s[32]; snprintf(s, sizeof(s), "%s:%d", inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); return s; } static int stun_udtsyn(void* data, int size) { /* TODO: udt v4 */ static const char* udt4_syn = "\x80\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x00" "\x00\x00\x00\x04"; if ( size >= 20 && !memcmp(data, udt4_syn, 20) ) { return 1; } return 0; } static int udt_get_state(int fd_udt) { int state = BROKEN, len; len = sizeof(state); if( udt_getsockopt(fd_udt, 0, UDT_STATE, &state, &len) ) { return BROKEN; } return state; } static int udt_get_canrecv(int fd_udt) { int canrecv = -1, len; len = sizeof(canrecv); if( udt_getsockopt(fd_udt, 0, UDT_RCVDATA, &canrecv, &len) ) { return -1; } return canrecv; } static int udt_get_cansend(int fd_udt) { int sendbufused, len; len = sizeof(sendbufused); if( udt_getsockopt(fd_udt, 0, UDT_SNDDATA, &sendbufused, &len) ) { return -1; } return UDT_SNDBUF_SIZE - sendbufused*UDT_MAX_SEG_SIZE; } static int build_udt_socket(int fd_udp, int rendezvous, int without_linger) { UDTSOCKET fd_udt; int opt, ret = 0; fd_udt = udt_socket(PF_INET, SOCK_STREAM, 0); if ( -1 == fd_udt ) { DBG_LOG(DBG_ERR, "udt_socket failed, errcode %d\n", udt_getlasterror_code()); return -1; } /* set socket options: RENDEZVOUS, MSS, NONBLOCK, SND/RCV BUFFER size. */ opt = 1; if ( rendezvous ) { ret = udt_setsockopt(fd_udt, 0, UDT_RENDEZVOUS, &opt, sizeof(opt)); } if ( without_linger ) { struct linger lg; lg.l_onoff = 0; lg.l_linger = 0; DBG_LOG(DBG_ERR, "udt socket without linger\n"); ret = ret ? ret : udt_setsockopt(fd_udt, 0, UDT_LINGER, &lg, sizeof(lg)); } opt = UDT_MAX_SEG_SIZE; /* strips headers (PPPOE, PPP, ...) off */ ret = ret ? ret : udt_setsockopt(fd_udt, 0, UDT_MSS, &opt, sizeof(opt)); opt = 0; ret = ret ? ret : udt_setsockopt(fd_udt, 0, UDT_RCVSYN, &opt, sizeof(opt)); if ( ret != 0 ) { DBG_LOG(DBG_ERR, "udt_setsockopt failed, errcode %d\n", udt_getlasterror_code()); udt_close(fd_udt); return -1; } /* set socket buffer size */ opt = UDT_SNDBUF_SIZE; udt_setsockopt(fd_udt, 0, UDT_SNDBUF, &opt, sizeof(opt)); opt >>= 1; udt_setsockopt(fd_udt, 0, UDP_SNDBUF, &opt, sizeof(opt)); opt = UDT_RCVBUF_SIZE; udt_setsockopt(fd_udt, 0, UDT_RCVBUF, &opt, sizeof(opt)); opt >>= 1; udt_setsockopt(fd_udt, 0, UDP_RCVBUF, &opt, sizeof(opt)); /* bind to local udp address */ if ( fd_udp != -1 ) { /* FIXME: non-block fd(UDP) makes udt_bind2 crazy!!! */ evutil_make_socket_blocking(fd_udp); if ( udt_bind2(fd_udt, fd_udp) != 0 ) { DBG_LOG(DBG_ERR, "udt_bind2 failed, errcode %d\n", udt_getlasterror_code()); udt_close(fd_udt); return -1; } } return fd_udt; } static int udt_do_send_stream(punch_inner_data_t* inner_ctx) { int ret, sent = 0; int size; char buf[4096] = {0}; do { memset(buf, 0, sizeof(buf)); size = evbuffer_copyout(inner_ctx->evbuf_out, buf, sizeof(buf)); if ( size <= 0 ) { break; } ret = udt_send(inner_ctx->fd_udt, buf, size, 0); if ( -1 == ret ) { if (udt_getlasterror_code() == EASYNCSND ) { return 0; } else { DBG_LOG(DBG_ERR, "udt_send failed, errcode %d\n", udt_getlasterror_code()); return -1; } } evbuffer_drain(inner_ctx->evbuf_out, ret); sent += ret; /* send not complete, or buffer empty */ if ( ret < size || size < sizeof(buf) ) { break; } } while (1); return sent; } #ifdef HAVE_VS #define HTTP_RESPONSE_503_6 \ "HTTP/1.0 503.6 Service Unavailable\r\n"\ "Server: p2pd\r\n"\ "Content-Length: 0\r\n"\ "Content-Type: text/html\r\n"\ "Connection: close\r\n"\ "\r\n" #endif static int read_local_data(punch_ctx_t *ctx) { punch_inner_data_t* inner_ctx = ctx->inner_data; char buf[8192]; int buf_size = sizeof(buf); int ret = 0; int avail_buf_size = 0; int iter_size = 0; int recv_size_from_local = 0; int send_size = 0; if (!ctx->test_local_server(ctx->env)) { return 0; } while (1) { avail_buf_size = udt_get_cansend(inner_ctx->fd_udt); if (avail_buf_size <= 0) { break; } iter_size = (avail_buf_size > buf_size)?buf_size:avail_buf_size; recv_size_from_local = ctx->read_from_local_server(ctx->env, (void *)buf, iter_size); if (recv_size_from_local < 0) { DBG_LOG(DBG_ERR, "recv data from local server failed.\n"); return -1; } if (recv_size_from_local == 0) { break; } ret = udt_send(inner_ctx->fd_udt, buf, recv_size_from_local, 0); if (ret < 0) { return -1; } } return 0; } #ifdef ADD_REMOTE_ADDR static int add_header_to_evbuf(session_t *sess, char *buf, int len) { if (sess->evbuf_in == NULL) { sess->evbuf_in = evbuffer_new(); if (NULL == sess->evbuf_in) { return -1; } } if (HTTP_HEADER_MAX_LEN <= evbuffer_get_length(sess->evbuf_in)) { DBG_LOG(DBG_ERR, "http header is too long.\n"); return -1; } evbuffer_add(sess->evbuf_in, buf, len); return 0; } static int evbuf_contain_complete_header(struct evbuffer *evbuf) { char *header_end = NULL; int buf_len = 0; if (NULL == evbuf) { /* false */ return FALSE; } header_end = evbuffer_find(evbuf, "\r\n\r\n", 4); if (NULL == header_end) { DBG_LOG(DBG_ERR, "http header is too long\n"); return FALSE; } return TRUE; } static int send_new_header(session_t *sess) { int ret = 0; char *header_end = NULL; int header_len = 0; char buf[HTTP_HEADER_MAX_LEN + 1] = {0}; punch_ctx_t *ctx = &sess->punch_ctx; ret = evbuffer_copyout(sess->evbuf_in, buf, sizeof(buf) - 1); if (ret <= 0) { return -1; } header_end = strstr(buf, "\r\n\r\n"); if (NULL == header_end) { return -1; } /* add \r\n */ header_end += 2; header_len = header_end - buf; int left_len = sizeof(buf) - header_len; /* �ⲿIP����ȷ���Զ˵ĵ�����ַ */ snprintf(header_end, left_len, "X-Client-Addr: %s\r\n\r\n", sess->prepare_req.eip); if ( ctx->write_to_local_server(ctx->env, buf, strlen(buf)) < 0 ) { return -1; } /* there is a "\r\n" left */ header_len += 2; evbuffer_drain(sess->evbuf_in, header_len); while (0 != evbuffer_get_length(sess->evbuf_in)) { ret = evbuffer_remove(sess->evbuf_in, buf, sizeof(buf) - 1); if (ret <= 0) { return -1; } if (ctx->write_to_local_server(ctx->env, buf, ret) < 0) { return -1; } } evbuffer_free(sess->evbuf_in); sess->evbuf_in = NULL; sess->header_recv = 1; return 0; } #endif static int read_peer_data(punch_ctx_t *ctx) { int ret = 0; punch_inner_data_t* inner_ctx = ctx->inner_data; char buf[2048]; int buf_size = sizeof(buf); session_t* sess = ctx->env; #ifdef HAVE_VS char sndbuf[256 + 1] = {0}; int size = 0; time_t t; #endif #ifdef ADD_REMOTE_ADDR char *new_header = NULL; #endif if(udt_get_canrecv(inner_ctx->fd_udt) <= 0 ) { return 0; } ret = udt_recv(inner_ctx->fd_udt, buf, sizeof(buf), 0); if ( -1 == ret && udt_getlasterror_code() == EASYNCRCV ) { return 0; } if ( ret <= 0 ) { return -1; } #ifdef HAVE_VS /* need to check permission when it is video share */ if(sess->prepare_req.is_video_share) { DBG_LOG(DBG_DBG, "it is video share, need to check permission\n"); t = time(NULL); if(strstr(buf, "/stream") != NULL) { DBG_LOG(DBG_DBG, "this request is POST /stream, now check permission\n"); if(0 == vs_share_rules_time_valid(&t, &sess->share_info->rules)) { DBG_LOG(DBG_ERR, "permission denied, send 503.6 to peer\n"); size = snprintf(sndbuf, 200, HTTP_RESPONSE_503_6); udt_send(inner_ctx->fd_udt, sndbuf, size, 0); return 0; } } else { DBG_LOG(DBG_DBG, "request type is not GET\n"); } DBG_LOG(DBG_DBG, "permission check passed\n"); } #endif #ifdef ADD_REMOTE_ADDR //printf("buf: %s\n", buf); if (!sess->header_recv) { if (0 > add_header_to_evbuf(sess, buf, ret)) { DBG_LOG(DBG_ERR, "failed to add header to evbuf\n"); return -1; } if (!evbuf_contain_complete_header(sess->evbuf_in)) { return 0; } if (0 > send_new_header(sess)) { DBG_LOG(DBG_ERR, "failed to send new header\n"); return -1; } return 0; } #endif if ( ctx->write_to_local_server(ctx->env, buf, ret) < 0 ) { return -1; } return 0; } static void udt_event_cb(evutil_socket_t fd, short events, void* arg) { int ret; punch_ctx_t* ctx = arg; punch_inner_data_t* inner_ctx = ctx->inner_data; int udt_stat = NONEXIST; udt_stat = udt_get_state(inner_ctx->fd_udt); if (udt_stat != CONNECTED) { if ( !(inner_ctx->status & PUNCH_UDT_CONNECTED) ) { return ; } else { DBG_LOG(DBG_ERR, "udt io-error, errcode %d\n", udt_getlasterror_code()); goto FAILED; } } else { if (!(inner_ctx->status & PUNCH_UDT_CONNECTED)) { DBG_LOG(DBG_DBG, "udt_connect OK, peer address: %s\n", _sockaddr_ntoa(&inner_ctx->sin_peer)); inner_ctx->status = PUNCH_UDT_CONNECTED; if ( ctx->succeed_cb(ctx->env, &inner_ctx->sin_peer) < 0 ) { DBG_LOG(DBG_ERR, "udt succeed_cb failed.\n"); goto FAILED; } } } ret = read_peer_data(ctx); if (ret < 0) { DBG_LOG(DBG_ERR, "read_peer_data failed.\n"); goto FAILED; } ret = read_local_data(ctx); if (ret < 0) { DBG_LOG(DBG_ERR, "read_local_data failed.\n"); goto FAILED; } return ; FAILED: ctx->error_cb(ctx->env); } static int udt_async_connect(punch_ctx_t* ctx, int rendezvous, int without_linger) { punch_inner_data_t* inner_ctx = ctx->inner_data; struct timeval tv = {0, 100*1000}; event_del(inner_ctx->evstun); event_assign(inner_ctx->evstun, ctx->base, -1, EV_TIMEOUT|EV_PERSIST, udt_event_cb, ctx); if ( event_add(inner_ctx->evstun, &tv) < 0 ) { return -1; } /* rendezvous */ inner_ctx->fd_udt = build_udt_socket(ctx->fd, rendezvous, without_linger); if ( inner_ctx->fd_udt < 0 ) { return -1; } DBG_LOG(DBG_DBG, "udt connect to %s...\n", _sockaddr_ntoa(&inner_ctx->sin_peer)); return udt_connect(inner_ctx->fd_udt, (void*)&inner_ctx->sin_peer, sizeof(inner_ctx->sin_peer)); } static int stun_send_syn(punch_ctx_t* ctx, int opt) { punch_inner_data_t* inner_ctx = ctx->inner_data; struct sockaddr_in sin, sin2; char buf[512]; int ret = 0, len, i; snprintf(buf, sizeof(buf), "%s", ctx->stun_sid); if ( (opt & PUNCH_OPT_ACK) ) { buf[0]++; } len = (int)strlen(buf) + 1; sin = (opt & PUNCH_OPT_ACK) ? inner_ctx->sin_peer : ctx->sin_peer_wan; DBG_LOG(DBG_DBG, "punch buffer is %s\n", buf); // port = ntohs(sin.sin_port); //TODO: port prediction //PRINTF("sending %s...\n", (opt & PUNCH_OPT_ACK) ? "SYN/ACK" : "SYN" ); if ( !(opt & PUNCH_OPT_ACK) && ctx->sin_peer_lan.sin_addr.s_addr != ctx->sin_peer_wan.sin_addr.s_addr ) { /* LAN punching... */ ret = sendto(ctx->fd, buf, len, 0, (struct sockaddr*)&ctx->sin_peer_lan, sizeof(struct sockaddr_in)); } /* WAN punching... */ if(!inner_ctx->punch_prepared) { /* the first SYN has low TTL, * this low-TTL SYN is used to punch holes on the NATs of the PC's/IPC's own side. * * next we will send normal(whose TTL has not been set) SYNs, * and these normal SYNs will be sent every 1 second. * why 1 second, see the function udp_punch. */ int original_ttl, new_ttl, ttl_len; new_ttl = PUNCH_PREPARING_TTL; ttl_len = sizeof(original_ttl); if(getsockopt(ctx->fd, IPPROTO_IP, IP_TTL, (char *) &original_ttl, &ttl_len) || setsockopt(ctx->fd, IPPROTO_IP, IP_TTL, (char *) &new_ttl, sizeof(new_ttl)) ) { inner_ctx->punch_prepared = 1; return 0; } /* send multiple times, avoid packet loss */ for(i = 0; i < 3; ++i) { sendto(ctx->fd, buf, len, 0, (struct sockaddr *) &sin, sizeof(struct sockaddr_in)); } setsockopt(ctx->fd, IPPROTO_IP, IP_TTL, (char *) &original_ttl, ttl_len); inner_ctx->punch_prepared = 1; return 0; } ret = sendto(ctx->fd, buf, len, 0, (struct sockaddr*)&sin, sizeof(struct sockaddr_in)); if ( ret < 0 ) { if ( !sock_func_will_block(ret) ) { DBG_LOG(DBG_ERR, "sendto failed, fd:%d, sock_errno:%d\n", ctx->fd, sock_errno); goto FAILED; } return 0; } inner_ctx->punch_try_cnt ++; if(!(opt & PUNCH_OPT_ACK) && inner_ctx->punch_try_cnt > 3) { /* if we have sent the low-TTL SYN, as well as 3 normal SYNs(this takes 4 seconds approximately), * but hole punching still not done, we start to do port prediction. * * NOTE: port prediction strategy will be changed if we have better one. */ sin2 = sin; for(i = 1; i < 5; i++) { sin2.sin_port = htons(ntohs(sin.sin_port) + i); sendto(ctx->fd, buf, len, 0, (struct sockaddr *)&sin2, sizeof(struct sockaddr_in)); } } return 0; FAILED: return -1; } static void punch_event_cb(evutil_socket_t fd, short events, void* arg) { punch_ctx_t* ctx = arg; punch_inner_data_t* inner_ctx = ctx->inner_data; int opt_ack = 0; if (events & EV_TIMEOUT) { opt_ack = (PUNCH_SYN_SENT != inner_ctx->status) ? PUNCH_OPT_ACK : 0; stun_send_syn(ctx, opt_ack); return ; } if (events & EV_READ) { char buf[512] = {0}; struct sockaddr_in sin; socklen_t socklen = sizeof(sin); int size; size = recvfrom(fd, buf, sizeof(buf)-1, 0, (struct sockaddr*)&sin, &socklen); if ( size < 0 ) { /* for udp socket, WSAENETRESET means TTL expired. * this is an ICMP error, called asynchronous error, * and only winSocket will tell udp socket this error, * others like bsd/linux will not. */ DBG_LOG(DBG_ERR, "recvfrom failed, fd:%d, sock_errno:%d\n", fd, sock_errno); if ( !sock_func_will_block(size) ) { goto FAILED; } return; } /* SYN received */ int syn_recv = !strcmp(buf, ctx->stun_sid); if (syn_recv) { if ( PRIVATE_NIPV4(sin.sin_addr.s_addr) || /* LAN */ PUNCH_SYN_SENT == inner_ctx->status ) { /* ����ΪLAN�� */ inner_ctx->sin_peer = sin; inner_ctx->status = PUNCH_SYN_RCVD; } stun_send_syn(ctx, PUNCH_OPT_ACK); DBG_LOG(DBG_DBG, "recvd SYN from %s\n", _sockaddr_ntoa(&sin)); return; } else { buf[0]--; int ack_recv = !strcmp(buf, ctx->stun_sid); if (ack_recv) { DBG_LOG(DBG_DBG, "recvd SYN, ACK from %s\n", _sockaddr_ntoa(&sin)); } else { DBG_LOG(DBG_DBG, "recvd trash data(%d bytes) from %s\n", size, _sockaddr_ntoa(&sin)); return; } } if ( PUNCH_SYN_SENT == inner_ctx->status ) { /* û���յ��Է���SYN����ֱ���յ�SYN+ACK */ /* ����Գ���+IP���� */ inner_ctx->sin_peer = sin; stun_send_syn(ctx, PUNCH_OPT_ACK); } else if (PUNCH_SYN_RCVD == inner_ctx->status) { /* prefer local to remote/harpin connections */ int is_local_connection = PRIVATE_NIPV4(inner_ctx->sin_peer.sin_addr.s_addr); int synack_from_remote_ip = sin.sin_addr.s_addr != inner_ctx->sin_peer.sin_addr.s_addr; if (is_local_connection && synack_from_remote_ip) { return; } /* received SYN, SYN/ACK from different remote address */ int different_addr = (inner_ctx->sin_peer.sin_addr.s_addr != sin.sin_addr.s_addr) || (inner_ctx->sin_peer.sin_port != sin.sin_port); if (different_addr) { inner_ctx->sin_peer = sin; } } else { DBG_LOG(DBG_ERR, "punching status error: %d\n", inner_ctx->status); return; } /* punching succeed */ DBG_LOG(DBG_DBG, "UDP hole punching succeed, peer address: %s\n", _sockaddr_ntoa(&inner_ctx->sin_peer)); stun_send_syn(ctx, PUNCH_OPT_ACK); inner_ctx->status = PUNCH_SYNACK_RCVD; int ret = udt_async_connect(ctx, 1, 0); if ( ret < 0 ) { goto FAILED; } return; } return; FAILED: DBG_LOG(DBG_ERR, "punching failed\n"); ctx->error_cb(ctx->env); } int udp_punch_send_stream(punch_ctx_t* ctx, const void* data, int size) { punch_inner_data_t* inner_ctx; int sent = 0; if (!ctx || !ctx->inner_data || !data || size < 0) { return -1; } inner_ctx = ctx->inner_data; if ( !(inner_ctx->status & PUNCH_UDT_CONNECTED) ) { return 0; } if ( !size ) { return (int)evbuffer_get_length(inner_ctx->evbuf_out); } /* data enqueue in evbuffer, and sent when timer expires */ if ( evbuffer_add(inner_ctx->evbuf_out, (char*)data+sent, size-sent) < 0 ) { return -1; } return (int)evbuffer_get_length(inner_ctx->evbuf_out); } int udp_punch(punch_ctx_t* ctx) { punch_inner_data_t* inner_ctx; struct timeval tv = {1,1}; if (!ctx || !ctx->inner_data ) { return -1; } inner_ctx = ctx->inner_data; inner_ctx->evstun = event_new(ctx->base, ctx->fd, EV_READ|EV_TIMEOUT|EV_PERSIST, punch_event_cb, ctx); if (!inner_ctx->evstun) { DBG_LOG(DBG_ERR, "create evstun event, error\n"); return -1; } event_add(inner_ctx->evstun, &tv); int opt_ack = 0; stun_send_syn(ctx, opt_ack); stun_send_syn(ctx, opt_ack); inner_ctx->status = PUNCH_SYN_SENT; return 0; } int udp_punch_clear_internal(punch_ctx_t* ctx) { punch_inner_data_t* inner_ctx; if ( !ctx || !ctx->inner_data ) { return -1; } inner_ctx = ctx->inner_data; if ( inner_ctx->evstun ) { event_free(inner_ctx->evstun); } if ( inner_ctx->fd_udt != -1 ) { /* since UDP fd has been binded to UDT fd, we must avoid double closeing UDP fd */ udt_close(inner_ctx->fd_udt); } else if (ctx->fd != -1) { evutil_closesocket(ctx->fd); } if ( inner_ctx->evbuf_out ) { evbuffer_free(inner_ctx->evbuf_out); } free(ctx->inner_data); ctx->inner_data = NULL; return 0; } int udp_punch_init_internal(punch_ctx_t* ctx) { punch_inner_data_t* inner_ctx; if (!ctx || !ctx->base || ctx->fd < 0 ) { return -1; } udp_punch_clear_internal(ctx); ctx->inner_data = calloc(1, sizeof(punch_inner_data_t)); if (!ctx->inner_data) { return -1; } inner_ctx = ctx->inner_data; inner_ctx->fd_udt = -1; inner_ctx->udt_sndlowat = UDT_SNDLOWAT; inner_ctx->evstun = NULL; inner_ctx->evbuf_out = evbuffer_new(); if ( !inner_ctx->evbuf_out ) { DBG_LOG(DBG_ERR, "create evbuf_out, error\n"); udp_punch_clear_internal(ctx); return -1; } return 0; } int udp_punch_set_attr(punch_ctx_t* ctx, int attr, void* value) { punch_inner_data_t* inner_ctx; if( !ctx || !ctx->inner_data || !value ) { return -1; } inner_ctx = ctx->inner_data; switch(attr) { case PUNCHING_ATTR_UDT_SNDLOWAT: { unsigned int udt_sndlowat; udt_sndlowat = *(unsigned int*)value; if( udt_sndlowat <= 0 ) { return -1; } DBG_LOG(DBG_DBG, "udt_sndlowat changed: %u -> %u\n", inner_ctx->udt_sndlowat, udt_sndlowat); inner_ctx->udt_sndlowat = udt_sndlowat; return 0; }/* case of PUNCHING_ATTR_UDT_SNDLOWAT */ default: return -1; } return -1; }
最新发布
11-15
#include <ntifs.h> #include <ntddk.h> #include <intrin.h> #include "ptehook.h" #define CR0_WP (1 << 16) #define DRIVER_TAG 'HKOB' #define MAX_G_BIT_RECORDS 128 #define MAX_HOOK_COUNT 64 #define PAGE_ALIGN(va) ((PVOID)((ULONG_PTR)(va) & ~0xFFF)) #define PTE_NX_BIT (1ULL << 63) // 调试打印宏 #define LOG_ERROR(fmt, ...) \ DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_ERROR_LEVEL, "[PTE_HOOK] ERROR: " fmt "\n", ##__VA_ARGS__) #define LOG_INFO(fmt, ...) \ DbgPrintEx(DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, "[PTE_HOOK] INFO: " fmt "\n", ##__VA_ARGS__) #define LOG_TRACE(fmt, ...) \ DbgPrintEx(DPFLTR极IHVDRIVER_ID, DPFLTR_TRACE_LEVEL, "[PTE_HOOK] TRACE: " fmt "\n", ##__VA_ARGS__) // 修正的跳板指令结构 #pragma pack(push, 1) typedef struct _JMP_ABS { UINT8 opcode[6]; // FF 25 00 00 00 00 = jmp [rip+0] ULONG64 address; // 目标地址 (紧跟在指令后) } JMP_ABS, * PJMP_ABS; #pragma pack(pop) static_assert(sizeof(JMP_ABS) == 14, "JMP_ABS size must be 14 bytes"); // 声明PsGetProcessImageFileName extern "C" NTSYSAPI CHAR* NTAPI PsGetProcessImageFileName(PEPROCESS Process); // 页表结构定义 typedef struct _PAGE_TABLE { UINT64 LineAddress; union { struct { UINT64 present : 1; UINT64 write : 1; UINT64 user : 1; UINT64 write_through : 1; UINT64 cache_disable : 1; UINT64 accessed : 1; UINT64 dirty : 1; UINT64 pat : 1; UINT64 global : 1; UINT64 ignored_1 : 3; UINT64 page_frame_number : 36; UINT64 reserved_1 : 极; UINT64 ignored_2 : 7; UINT64 protection_key : 4; UINT64 execute_disable : 1; } flags; UINT64 value; }*PteAddress; union { struct { UINT64 present : 1; UINT64 write : 1; UINT64 user : 1; UINT64 write_through : 1; UINT64 cache_disable : 1; UINT64 accessed : 1; UINT64 dirty : 1; UINT64 large_page : 1; UINT64 global : 1; UINT64 ignored_2 : 3; UINT64 page_frame_number : 36; UINT64 reserved_1 : 4; UINT64 ignored_3 : 7; UINT64 protection_key : 4; UINT64 execute_disable : 1; } flags; UINT64 value; }*PdeAddress; union { struct { UINT64 present : 1; UINT64 write : 1; UINT64 user : 1; UINT64 write_through : 1; UINT64 cache_disable : 1; UINT64 accessed : 1; UINT64 ignored_1 : 1; UINT64 page_size : 1; UINT64 ignored_2 : 4; UINT64 page_frame_number : 36; UINT64 reserved_1 : 4; UINT64 ignored_3 : 7; UINT64 protection_key : 4; UINT64 execute_disable : 1; } flags; UINT64 value; }*PdpteAddress; UINT64* Pml4Address; BOOLEAN IsLargePage; BOOLEAN Is1GBPage; UINT64 OriginalPte; UINT64 OriginalPde; UINT64 OriginalPdpte; UINT64 OriginalPml4e; HANDLE ProcessId; } PAGE_TABLE, * PPAGE_TABLE; // G位信息记录结构体 typedef struct _G_BIT_INFO { void* AlignAddress; union { struct { UINT64 present : 1; UINT64 write : 1; UINT64 user : 1; UINT64 write_through : 1; UINT64 cache_disable : 1; UINT64 accessed : 1; UINT64 dirty : 1; UINT64 large_page : 1; UINT64 global : 1; UINT64 ignored_2 : 3; UINT64 page_frame_number : 36; UINT64 reserved_1 : 4; UINT64 ignored_3 : 7; UINT64 protection_key : 4; UINT64 execute_disable : 1; } flags; UINT64 value; }*PdeAddress; union { struct { UINT64 present : 1; UINT64 write : 1; UINT64 user : 1; UINT64 write_through : 1; UINT64 cache_disable : 1; UINT64 accessed : 1; UINT64 dirty : 1; UINT64 pat : 1; UINT64 global : 1; UINT64 ignored_1 : 3; UINT64 page_frame_number : 36; UINT64 reserved_1 : 4; UINT64 ignored_2 : 7; UINT64 protection_key : 4; UINT64 execute_disable : 1; } flags; UINT64 value; }*PteAddress; BOOLEAN IsLargePage; } G_BIT_INFO, * PG_BIT_INFO; typedef struct _HOOK_INFO { void* OriginalAddress; void* HookAddress; UINT8 OriginalBytes[20]; UINT8 HookBytes[20]; UINT32 HookLength; BOOLEAN IsHooked; HANDLE ProcessId; UINT64 OriginalPteValue; UINT64 HookedPteValue; } HOOK_INFO; class PteHookManager { public: bool fn_pte_inline_hook_bp_pg(HANDLE process_id, _Inout_ void** ori_addr, void* hk_addr); bool fn_remove_hook(HANDLE process_id, void* hook_addr); static PteHookManager* GetInstance(); HOOK_INFO* GetHookInfo() { return m_HookInfo; } char* GetTrampLinePool() { return m_TrampLinePool; } UINT32 GetHookCount() { return m_HookCount; } bool fn_resume_global_bits(void* align_addr); ~PteHookManager(); private: bool WriteTrampolineInstruction(void* trampoline, const JMP_ABS& jmpCmd); bool SetPageExecution(void* address, bool executable); void fn_add_g_bit_info(void* align_addr, void* pde_address, void* pte_address); bool fn_isolation_pagetable(UINT64 cr3_val, void* replace_align_addr, void* split_pde); bool fn_isolation_pages(HANDLE process_id, void* ori_addr); bool fn_split_large_pages(void* in_pde, void* out_pde); NTSTATUS get_page_table(UINT64 cr3, PAGE_TABLE& table); void* fn_pa_to_va(UINT64 pa); UINT64 fn_va_to_pa(void* va); __forceinline KIRQL DisableWriteProtection(); __forceinline void EnableWriteProtection(KIRQL oldIrql); void FlushCacheAndTlb(void* address); G_BIT_INFO m_GbitRecords[MAX_G_BIT_RECORDS]; UINT32 m_GbitCount = 0; void* m_PteBase = 0; HOOK_INFO m_HookInfo[MAX_HOOK_COUNT] = { 0 }; UINT32 m_HookCount = 0; char* m_TrampLinePool = nullptr; UINT32 m_PoolUsed = 0; static PteHookManager* m_Instance; }; PteHookManager* PteHookManager::m_Instance = nullptr; // 实现部分 __forceinline KIRQL P极HookManager::DisableWriteProtection() { KIRQL oldIrql = KeRaiseIrqlToDpcLevel(); UINT64 cr0 = __readcr0(); __writecr0(cr0 & ~CR0_WP); // 清除CR0.WP位 _mm_mfence(); return oldIrql; } __forceinline void PteHookManager::EnableWriteProtection(KIRQL oldIrql) { _mm_mfence(); UINT64 cr0 = __readcr0(); __writecr0(cr0 | CR0_WP); // 设置CR0.WP位 KeLowerIrql(oldIrql); } void* PteHookManager::fn_pa_to_va(UINT64 pa) { PHYSICAL_ADDRESS physAddr; physAddr.QuadPart = pa; return MmGetVirtualForPhysical(physAddr); } UINT64 PteHookManager::fn_va_to_pa(void* va) { PHYSICAL_ADDRESS physAddr = MmGetPhysicalAddress(va); return physAddr.QuadPart; } NTSTATUS PteHookManager::get_page_table(UINT64 cr3_val, PAGE_TABLE& table) { UINT64 va = table.LineAddress; UINT64 pml4e_index = (va >> 39) & 0x1FF; UINT64 pdpte_index = (va >> 30) & 0x1FF; UINT64 pde_index = (va >> 21) & 0x1FF; UINT64 pte_index = (va >> 12) & 0x1FF; // PML4 UINT64 pml4_pa = cr3_val & ~0xFFF; UINT64* pml4_va = (UINT64*)fn_pa_to_va(pml4_pa); if (!pml4_va) return STATUS_INVALID_ADDRESS; table.Pml4Address = &pml4_va[pml4e_index]; table.OriginalPml4e = *table.Pml4Address; if (!(table.OriginalPml4e & 1)) return STATUS_ACCESS_VIOLATION; // PDPTE UINT64 pdpte_pa = table.OriginalPml4e & ~0xFFF; UINT64* pdpte_va = (UINT64*)fn_pa_to_va(pdpte_pa); if (!pdpte_va) return STATUS_INVALID_ADDRESS; table.PdpteAddress = (decltype(table.PdpteAddress))&pdpte_va[pdpte_index]; table.OriginalPdpte = table.PdpteAddress->value; table.Is1GBPage = (table.PdpteAddress->flags.page_size) ? TRUE : FALSE; if (!(table.OriginalPdpte & 1)) return STATUS_ACCESS_VIOLATION; if (table.Is1GBPage) return STATUS_SUCCESS; // PDE UINT64 pde_pa = table.OriginalPdpte & ~0xFFF; UINT64* pde_va = (UINT64*)fn_pa_to_va(pde_pa); if (!pde_va) return STATUS_INVALID_ADDRESS; table.PdeAddress = (decltype(table.PdeAddress))&pde_va[pde_index]; table.OriginalPde = table.PdeAddress->value; table.IsLargePage = (table.PdeAddress->flags.large_page) ? TRUE : FALSE; if (!(table.OriginalPde & 1)) return STATUS_ACCESS_VIOLATION; if (table.IsLargePage) return STATUS_SUCCESS; // PTE UINT64 pte_pa = table.OriginalPde & ~0xFFF; UINT64* pte_va = (UINT64*)fn_pa_to_va(pte_pa); if (!pte_va) return STATUS_INVALID_ADDRESS; table.PteAddress = (decltype(table.PteAddress))&pte_va[pte_index]; table.OriginalPte = table.PteAddress->value; if (!(table.OriginalPte & 1)) return STATUS_ACCESS_VIOLATION; LOG_TRACE("Page Table Info: VA=0x%p, PTE=0x%llX", (void*)table.LineAddress, table.OriginalPte); return STATUS_SUCCESS; } bool PteHookManager::fn_split_large_pages(void* in_pde_ptr, void* out_pde_ptr) { auto in_pde = (decltype(PAGE_TABLE::PdeAddress))in_pde_ptr; auto out_pde = (decltype(PAGE_TABLE::PdeAddress))out_pde_ptr; LOG_INFO("Splitting large page: Input PDE=0x%llx", in_pde->value); PHYSICAL_ADDRESS LowAddr = { 0 }, HighAddr = { 0 }; HighAddr.QuadPart = MAXULONG64; auto pt = (decltype(PAGE_TABLE::PteAddress))MmAllocateContiguousMemorySpecifyCache( PAGE_SIZE, LowAddr, HighAddr, LowAddr, MmNonCached); if (!pt) { LOG_ERROR("Failed to allocate contiguous memory for page splitting"); return false; } UINT64 start_pfn = in_pde->flags.page_frame_number; for (int i = 0; i < 512; i++) { pt[i].value = 0; pt[i].flags.present = 1; pt[i].flags.write = in_pde->flags.write; pt[i].flags.user = in_pde->flags.user; pt[i].flags.write_through = in_pde->flags.write_through; pt[i].flags.cache_disable = in_pde->flags.cache_disable; pt[i].flags.accessed = in_pde->flags.accessed; pt[i].flags.dirty = in_pde->flags.dirty; pt[i].flags.global = 0; pt[i].flags.page_frame_number = start_pfn + i; } out_pde->value = in_pde->value; out_pde->flags.large_page = 0; out_pde->flags.page_frame_number = fn_va_to_pa(pt) >> 12; LOG_INFO("Large page split complete: New PTE table at PA=0x%llx", fn_va_to_pa(pt)); return true; } bool PteHookManager::SetPageExecution(void* address, bool executable) { // 使用页表遍历函数修改NX位 PAGE_TABLE table = { 0 }; table.LineAddress = (UINT64)address; NTSTATUS status = get_page_table(__readcr3(), table); if (!NT_SUCCESS(status)) { LOG_ERROR("Failed to get page table for address 0x%p", address); return false; } KIRQL oldIrql = DisableWriteProtection(); UINT64 oldPte = table.PteAddress->value; UINT64 newPte = oldPte; // 设置或清除NX位 if (executable) { newPte &= ~PTE_NX_BIT; } else { newPte |= PTE_NX_BIT; } table.PteAddress->value = newPte; // 刷新TLB __invlpg(address); _mm_mfence(); EnableWriteProtection(oldIrql); LOG_TRACE("PTE modified for 0x%p: Old=0x%llX, New=0x%llX", address, oldPte, newPte); return true; } bool PteHookManager::fn_isolation_pagetable(UINT64 cr3_val, void* replace_align_addr, void* split_pde_ptr) { PHYSICAL_ADDRESS LowAddr = { 0 }, HighAddr = { 0 }; HighAddr.QuadPart = MAXULONG64; LOG_INFO("Isolating page table: CR3=0x%llx, Address=0x%p", cr3_val, replace_align_addr); auto Va4kb = (UINT64*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddr, HighAddr, LowAddr, MmNonCached); auto VaPt = (UINT64*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddr, HighAddr, Low极, MmNonCached); auto VaPdt = (UINT64*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddr, HighAddr, LowAddr, MmNonCached); auto VaPdpt = (UINT64*)MmAllocateContiguousMemorySpecifyCache(PAGE_SIZE, LowAddr, HighAddr, LowAddr, MmNonCached); if (!VaPt || !Va4kb || !VaPdt || !VaPdpt) { if (VaPt) MmFreeContiguousMemory(VaPt); if (Va4kb) MmFreeContiguousMemory(Va4kb); if (VaPdt) MmFreeContiguousMemory(VaPdt); if (VaPdpt) MmFreeContiguousMemory(VaPd极); LOG_ERROR("Failed to allocate contiguous memory for isolation"); return false; } PAGE_TABLE Table = { 0 }; Table.LineAddress = (UINT64)replace_align_addr; NTSTATUS status = get_page_table(cr3_val, Table); if (!NT_SUCCESS(status)) { MmFreeContiguousMemory(VaPt); MmFreeContiguousMemory(Va4kb); MmFreeContiguousMemory(VaPdt); MmFreeContiguousMemory(VaPdpt); LOG_ERROR("get_page_table failed with status 0x%X", status); return false; } UINT64 pte_index = (Table.LineAddress >> 12) & 0x1FF; UINT64 pde_index = (Table.LineAddress >> 21) & 0x1FF; UINT64 pdpte_index = (Table.LineAddress >> 30) & 0x1FF; UINT64 pml4e_index = (Table.LineAddress >> 39) & 0x1FF; memcpy(Va4kb, replace_align_addr, PAGE_SIZE); if (Table.IsLargePage && split_pde_ptr) { auto split_pde = (decltype(PAGE_TABLE::PdeAddress))split_pde_ptr; memcpy(VaPt, (void*)(split_pde->flags.page_frame_number << 12), PAGE_SIZE); } else { memcpy(VaPt, (void*)(Table.PdeAddress->flags.page_frame_number << 12), PAGE_SIZE); } memcpy(VaPdt, (void*)(Table.PdpteAddress->flags.page_frame_number << 12), PAGE_SIZE); memcpy(VaPdpt, (void*)(Table.Pml4Address[pml4e_index] & ~0xFFF), PAGE_SIZE); auto new_pte = (decltype(PAGE_TABLE::PteAddress))VaPt; new_pte[pte_index].flags.page_frame_number = fn_va_to_pa(Va4kb) >> 12; auto new_pde = (decltype(PAGE_TABLE::PdeAddress))VaPdt; new_pde[pde_index].value = Table.OriginalPde; new_pde[pde_index].flags.large_page = 0; new_pde[pde_index].flags.page_frame_number = fn_va_to_pa(VaPt) >> 12; auto new_pdpte = (decltype(PAGE_TABLE::PdpteAddress))VaPdpt; new_pdpte[pdpte_index].flags.page_frame_number = fn_va_to_pa(VaPdt) >> 12; auto new_pml4 = (UINT64*)fn_pa_to_va(cr3_val & ~0xFFF); new_pml4[pml4e_index] = (new_pml4[pml4e_index] & 0xFFF) | (fn_va_to_pa(VaPdpt) & ~0xFFF); __invlpg(replace_align_addr); LOG_INFO("Page table isolation complete: New PFN=0x%llx", fn_va_to_pa(Va4kb) >> 12); return true; } bool PteHookManager::fn_isolation_pages(HANDLE process_id, void* ori_addr) { PEPROCESS Process; if (!NT_SUCCESS(PsLookupProcessByProcessId(process_id, &Process))) { LOG_ERROR("PsLookupProcessByProcessId failed"); return false; } LOG_INFO("Isolating pages: PID=%d, Address=0x%p", HandleToUlong(process_id), ori_addr); KAPC_STATE ApcState; KeStackAttachProcess(Process, &ApcState); void* AlignAddr = PAGE_ALIGN(ori_addr); PAGE_TABLE Table = { 0 }; Table.LineAddress = (UINT64)AlignAddr; UINT64 target_cr3 = *(UINT64*)((UCHAR*)Process + 0x28); if (!NT_SUCCESS(get_page_table(target_cr3, Table))) { KeUnstackDetachProcess(&ApcState); ObDereferenceObject(Process); LOG_ERROR("get_page_table failed"); return false; } bool success = false; decltype(PAGE_TABLE::PdeAddress) split_pde = nullptr; if (Table.IsLargePage) { split_pde = (decltype(PAGE_TABLE::PdeAddress))ExAllocatePoolWithTag(NonPagedPool, sizeof(*split_pde), 'pdeS'); if (!split_pde || !fn_split_large_pages(Table.PdeAddress, split_pde)) { if (split_pde) ExFreePoolWithTag(split_pde, 'pdeS'); KeUnstackDetachProcess(&ApcState); ObDereferenceObject(Process); LOG_ERROR("Splitting large page failed"); return false; } if (Table.PdeAddress->flags.global) { Table.PdeAddress->flags.global = 0; fn_add_g_bit_info(AlignAddr, Table.PdeAddress, nullptr); LOG_INFO("Cleared G-bit for large page PDE: 0x%llx", Table.PdeAddress->value); } } else if (Table.PteAddress && Table.PteAddress->flags.global) { Table.PteAddress->flags.global = 0; fn_add_g_bit_info(AlignAddr, nullptr, Table.PteAddress); LOG_INFO("Cleared G-bit for PTE: 0x%llx", Table.PteAddress->value); } success = fn_isolation_pagetable(__readcr3(), AlignAddr, split_pde); if (split_pde) ExFreePoolWithTag(split_pde, 'pdeS'); KeUnstackDetachProcess(&ApcState); ObDereferenceObject(Process); if (success) { LOG_INFO("Page isolation successful"); } else { LOG_ERROR("Page isolation failed"); } return success; } bool PteHookManager::WriteTrampolineInstruction(void* trampoline, const JMP_ABS& jmpCmd) { // 确保8字节对齐 if (reinterpret_cast<ULONG_PTR>(trampoline) & 0x7) { LOG_ERROR("Trampoline address not aligned: 0x%p", trampoline); return false; } // 禁用写保护 KIRQL oldIrql = DisableWriteProtection(); // 直接写入内存 RtlCopyMemory(trampoline, &jmpCmd, sizeof(JMP_ABS)); // 刷新缓存 __invlpg(trampoline); _mm_mfence(); // 恢复写保护 EnableWriteProtection(oldIrql); LOG_TRACE("Trampoline instruction written at 0x%p", trampoline); return true; } void PteHookManager::fn_add_g_bit_info(void* align_addr, void* pde_address, void* pte_address) { if (m_GbitCount >= MAX_G_BIT_RECORDS) { LOG_ERROR("Max G-bit records reached"); return; } PG_BIT_INFO record = &m_GbitRecords[m_GbitCount++]; record->AlignAddress = align_addr; record->PdeAddress = (decltype(G_BIT_INFO::PdeAddress))pde_address; record->PteAddress = (decltype(G_BIT_INFO::PteAddress))pte_address; record->IsLargePage = (pde_address != nullptr); LOG_TRACE("Added G-bit record: Address=0x%p, PDE=0x%p, PTE=0x%p", align_addr, pde_address, pte_address); } bool PteHookManager::fn_resume_global_bits(void* align_addr) { KIRQL oldIrql = DisableWriteProtection(); bool found = false; LOG_INFO("Restoring G-bits for address: 0x%p", align_addr); for (UINT32 i = 0; i < m_GbitCount; i++) { PG_BIT_INFO record = &m_GbitRecords[i]; if (align_addr && record->AlignAddress != align_addr) continue; if (record->PteAddress) { record->PteAddress->flags.global = 1; __invlpg(record->AlignAddress); LOG_TRACE("Restored G-bit for PTE: 0x%p", record->AlignAddress); } if (record->PdeAddress) { record->PdeAddress->flags.global = 1; if (record->IsLargePage) { __invlpg(record->AlignAddress); } LOG_TRACE("Restored G-bit for PDE: 0x%p", record->AlignAddress); } found = true; if (align_addr) break; } EnableWriteProtection(oldIrql); if (found) { LOG_INFO("G-bits restoration complete"); } else { LOG_ERROR("No matching G-bit record found"); } return found; } bool PteHookManager::fn_pte_inline_hook_bp_pg(HANDLE process_id, _Inout_ void** ori_addr, void* hk_addr) { if (!ori_addr || !hk_addr || !*ori_addr) { LOG_ERROR("Invalid parameters: ori_addr=%p, hk_addr=%p", ori_addr, hk_addr); return false; } // 分配跳板池(如果尚未分配) if (!m_TrampLinePool) { PHYSICAL_ADDRESS LowAddr; LowAddr.QuadPart = 0x100000; PHYSICAL_ADDRESS HighAddr; HighAddr.QuadPart = ~0ull; m_TrampLinePool = (char*)MmAllocateContiguousMemorySpecifyCache( PAGE_SIZE * 8, LowAddr, HighAddr, LowAddr, MmNonCached); if (!m_TrampLinePool) { LOG_ERROR("Failed to allocate trampoline pool"); return false; } // 设置跳板池可执行 if (!SetPageExecution(m_TrampLinePool, true)) { MmFreeContiguousMemory(m_TrampLinePool); m_TrampLinePool = nullptr; LOG_ERROR("Failed to set trampoline pool executable"); return false; } LOG_INFO("Trampoline pool allocated: Address=0x%p, Size=%d bytes", m_TrampLinePool, PAGE_SIZE * 8); } // 计算跳板位置(16字节对齐确保RIP相对寻址) void* trampoline = (void*)((ULONG_PTR)(m_TrampLinePool + m_PoolUsed + 15) & ~15); SIZE_T requiredSize = ((char*)trampoline - m_TrampLinePool) + sizeof(JMP_ABS); // 检查跳板池空间 if (requiredSize > PAGE_SIZE * 8) { LOG_ERROR("Trampoline pool out of space: Used=%d, Required=%d", m_PoolUsed, requiredSize); return false; } // 构造跳转指令 JMP_ABS jmpCmd; memcpy(jmpCmd.opcode, "\xFF\x25\x00\x00\x00\x00", 6); // jmp [rip+0] jmpCmd.address = reinterpret_cast<ULONG64>(hk_addr); LOG_INFO("Constructing jump instruction: Target=0x%p, Trampoline=0x%p", hk_addr, trampoline); // 写入跳板指令 if (!WriteTrampolineInstruction(trampoline, jmpCmd)) { return false; } // 设置目标函数不可执行(触发页错误) if (!SetPageExecution(*ori_addr, false)) { LOG_ERROR("Failed to set target function non-executable"); return false; } // 记录Hook信息 bool hookRecorded = false; for (UINT32 i = 0; i < MAX_HOOK_COUNT; i++) { if (!m_HookInfo[i].IsHooked) { m_HookInfo[i].OriginalAddress = *ori_addr; m_HookInfo[i].HookAddress = trampoline; m_HookInfo[i].ProcessId = process_id; m_HookInfo极i].IsHooked = TRUE; m_HookInfo[i].HookLength = sizeof(JMP_ABS); RtlCopyMemory(m_HookInfo[i].HookBytes, &jmpCmd, sizeof(jmpCmd)); // 保存原始PTE值 PAGE_TABLE table = { 0 }; table.LineAddress = (UINT64)*ori_addr; if (NT_SUCCESS(get_page_table(__readcr3(), table))) { m_HookInfo[i].OriginalPteValue = table.OriginalPte; } m_HookCount++; hookRecorded = true; LOG_INFO("Hook recorded #%d: Original=0x%p, Trampoline=0x%p", i, *ori_addr, trampoline); break; } } if (!hookRecorded) { LOG_ERROR("Exceeded max hook count (%d)", MAX_HOOK_COUNT); return false; } // 更新原始地址为跳板地址 *ori_addr = trampoline; m_PoolUsed = (UINT32)requiredSize; LOG_INFO("Hook installed successfully: Trampoline=0x%p -> Hook=0x%p", trampoline, hk_addr); return true; } bool PteHookManager::fn_remove_hook(HANDLE process_id, void* hook_addr) { LOG_INFO("Removing hook: HookAddr=0x%p", hook_addr); for (UINT32 i = 0; i < m_HookCount; i++) { if (m_HookInfo[i].HookAddress == hook_addr && m_HookInfo[i].IsHooked) { LOG_INFO("Found matching hook: OriginalAddr=0x%p", m_HookInfo[i].OriginalAddress); // 恢复目标函数执行权限 SetPageExecution(m_HookInfo[i].OriginalAddress, true); // 恢复原始PTE值 if (m_HookInfo[i].OriginalPteValue) { PAGE_TABLE table = { 0 }; table.LineAddress = (UINT64)m_HookInfo[i].OriginalAddress; if (NT_SUCCESS(get_page_table(__readcr3(), table))) { KIRQL oldIrql = KeRaiseIrqlToDpcLevel(); table.PteAddress->value = m_HookInfo[i].OriginalPteValue; __invlpg(m_HookInfo[i].OriginalAddress); KeLowerIrql(oldIrql); LOG_TRACE("Restored PTE for 0x%p: 0x%llX", m_HookInfo[i].OriginalAddress, m_HookInfo[i].OriginalPteValue); } } m_HookInfo[i].IsHooked = FALSE; LOG_INFO("Hook removed successfully"); return true; } } LOG_ERROR("No matching hook found"); return false; } PteHookManager::~PteHookManager() { if (m_TrampLinePool) { MmFreeContiguousMemory(m_TrampLinePool); m_TrampLinePool = nullptr; LOG_INFO("Trampoline pool freed"); } } PteHookManager* PteHookManager::GetInstance() { if (!m_Instance) { m_Instance = static_cast<PteHookManager*>( ExAllocatePoolWithTag(NonPagedPool, sizeof(PteHookManager), 'tpHk')); if (m_Instance) { RtlZeroMemory(m_Instance, sizeof(PteHookManager)); LOG_INFO("PTE Hook Manager instance created: 0x%p", m_Instance); } else { LOG_ERROR("Failed to create PTE Hook Manager instance"); } } return m_Instance; } // 全局PTE Hook管理器实例 PteHookManager* g_PteHookManager = nullptr; // 目标进程名称 const char target_process_name[] = "oxygen.exe"; // 辅助函数:检查是否为目标进程 BOOLEAN IsTargetProcess(CHAR* imageName) { CHAR currentName[16]; RtlZeroMemory(currentName, sizeof(currentName)); strncpy(currentName, imageName, sizeof(currentName) - 1); return (_stricmp(currentName, target_process_name) == 0); } // 函数类型定义 typedef NTSTATUS(*fn_ObReferenceObjectByHandleWithTag)( HANDLE Handle, ACCESS_MASK DesiredAccess, POBJECT_TYPE ObjectType, KPROCESSOR_MODE AccessMode, ULONG Tag, PVOID* Object, POBJECT_HANDLE_INFORMATION HandleInformation ); fn_ObReferenceObjectByHandleWithTag g_OriginalObReferenceObjectByHandleWithTag = nullptr; // Hook 函数 NTSTATUS MyObReferenceObjectByHandleWithTag( HANDLE Handle, ACCESS_MASK DesiredAccess, POBJECT_TYPE ObjectType, KPROCESSOR_MODE AccessMode, ULONG Tag, PVOID* Object, POBJECT_HANDLE_INFORMATION HandleInformation ) { PEPROCESS currentProcess = PsGetCurrentProcess(); CHAR* imageName = PsGetProcessImageFileName(currentProcess); LOG_INFO("Hook function called by process: %s", imageName); if (IsTargetProcess(imageName)) { LOG_INFO("Access denied for target process: %s", imageName); return STATUS_ACCESS_DENIED; } return g_OriginalObReferenceObjectByHandleWithTag( Handle, DesiredAccess, ObjectType, AccessMode, Tag, Object, HandleInformation ); } NTSTATUS InstallHook(HANDLE targetProcessId) { UNICODE_STRING funcName; RtlInitUnicodeString(&funcName, L"ObReferenceObjectByHandleWithTag"); g_OriginalObReferenceObjectByHandleWithTag = (fn_ObReferenceObjectByHandleWithTag)MmGetSystemRoutineAddress(&funcName); if (!g_OriginalObReferenceObjectByHandleWithTag) { LOG_ERROR("ObReferenceObjectByHandleWithTag not found"); return STATUS_NOT_FOUND; } LOG_INFO("Target function found: 0x%p", g_OriginalObReferenceObjectByHandleWithTag); void* targetFunc = (void*)g_OriginalObReferenceObjectByHandleWithTag; void* hookFunc = (void*)MyObReferenceObjectByHandleWithTag; if (!g_PteHookManager->fn_pte_inline_hook_bp_pg(targetProcessId, &targetFunc, hookFunc)) { LOG_ERROR("PTE Hook installation failed"); return STATUS_UNSUCCESSFUL; } g_OriginalObReferenceObjectByHandleWithTag = (fn_ObReferenceObjectByHandleWithTag)targetFunc; LOG_INFO("Hook installed successfully. Trampoline: 0x%p", targetFunc); return STATUS_SUCCESS; } // 移除 Hook VOID RemoveHook() { if (g_OriginalObReferenceObjectByHandleWithTag && g_PteHookManager) { g_PteHookManager->fn_remove_hook(PsGetCurrentProcessId(), (void*)MyObReferenceObjectByHandleWithTag); } } // 工作线程上下文 typedef struct _HOOK_THREAD_CONTEXT { HANDLE TargetProcessId; } HOOK_THREAD_CONTEXT, * PHOOK_THREAD_CONTEXT; // 工作线程函数 VOID InstallHookWorker(PVOID Context) { PHOOK_THREAD_CONTEXT ctx = (PHOOK_THREAD_CONTEXT)Context; if (ctx) { LOG_INFO("Installing hook for PID: %d", HandleToUlong(ctx->TargetProcessId)); InstallHook(ctx->TargetProcessId); ExFreePoolWithTag(ctx, 'CtxH'); } PsTerminateSystemThread(STATUS_SUCCESS); } // 进程创建回调 - 修正后的签名 VOID ProcessNotifyCallback( _In_ PEPROCESS Process, _In_ HANDLE ProcessId, _In_opt_ PPS_CREATE_NOTIFY_INFO CreateInfo ) { if (CreateInfo != NULL) { // 进程创建 CHAR* imageName = PsGetProcessImageFileName(Process); if (IsTargetProcess(imageName)) { LOG_INFO("Target process created: %s (PID: %d)", imageName, HandleToUlong(ProcessId)); // 创建工作线程上下文 PHOOK_THREAD_CONTEXT ctx = (PHOOK_THREAD_CONTEXT)ExAllocatePoolWithTag(NonPagedPool, sizeof(HOOK_THREAD_CONTEXT), 'CtxH'); if (ctx) { ctx->TargetProcessId = ProcessId; HANDLE threadHandle; NTSTATUS status = PsCreateSystemThread( &threadHandle, THREAD_ALL_ACCESS, NULL, NULL, NULL, InstallHookWorker, ctx ); if (NT_SUCCESS(status)) { ZwClose(threadHandle); LOG_INFO("Worker thread created for hook installation"); } else { ExFreePoolWithTag(ctx, 'CtxH'); LOG_ERROR("Failed to create worker thread: 0x%X", status); } } else { LOG_ERROR("Failed to allocate thread context"); } } } } // 驱动卸载函数 VOID DriverUnload(PDRIVER_OBJECT DriverObject) { UNREFERENCED_PARAMETER(DriverObject); LOG_INFO("Driver unloading..."); // 移除进程通知回调 PsSetCreateProcessNotifyRoutineEx(ProcessNotifyCallback, TRUE); // 移除Hook RemoveHook(); // 清理PTE Hook资源 if (g_PteHookManager) { LOG_INFO("Cleaning up PTE Hook Manager"); // 恢复所有被修改的G位 g_PteHookManager->fn_resume_global_bits(nullptr); // 移除所有活动的Hook HOOK_INFO* hookInfo = g_PteHookManager->GetHookInfo(); UINT32 hookCount = g_PteHookManager->GetHookCount(); for (UINT32 i = 0; i < hookCount; i++) { if (hookInfo[i].IsHooked) { g_PteHookManager->fn_remove_hook(PsGetCurrentProcessId(), hookInfo[i].HookAddress); } } // 释放管理器实例 ExFreePoolWithTag(g_PteHookManager, 'tpHk'); g_PteHookManager = nullptr; } LOG_INFO("Driver unloaded successfully"); } extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath) { UNREFERENCED_PARAMETER(RegistryPath); LOG_INFO("Driver loading..."); DriverObject->DriverUnload = DriverUnload; g_PteHookManager = PteHookManager::GetInstance(); if (!g_PteHookManager) { LOG_ERROR("Failed to initialize PteHookManager"); return STATUS_INSUFFICIENT_RESOURCES; } NTSTATUS status = PsSetCreateProcessNotifyRoutineEx(ProcessNotifyCallback, FALSE); if (!NT_SUCCESS(status)) { LOG_ERROR("Failed to register process callback: 0x%X", status); return status; } LOG_INFO("Driver loaded successfully"); return STATUS_SUCCESS; } 严重性 代码 说明 项目 文件 行 禁止显示状态 详细信息 错误(活动) E0020 未定义标识符 "极" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 51 错误(活动) E0276 后面有“::”的名称一定是类名或命名空间名 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 203 错误(活动) E0020 未定义标识符 "DPFLTR极IHVDRIVER_ID" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 276 错误(活动) E0020 未定义标识符 "DPFLTR极IHVDRIVER_ID" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 349 错误(活动) E0020 未定义标识符 "Low极" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 360 错误(活动) E0020 未定义标识符 "VaPd极" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 368 错误(活动) E0020 未定义标识符 "DPFLTR极IHVDRIVER_ID" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 508 错误(活动) E0020 未定义标识符 "DPFLTR极IHVDRIVER_ID" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 524 错误(活动) E0020 未定义标识符 "DPFLTR极IHVDRIVER_ID" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 541 错误(活动) E0020 未定义标识符 "DPFLTR极IHVDRIVER_ID" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 549 错误(活动) E0020 未定义标识符 "m_HookInfo极i" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 636 错误(活动) E0065 应输入“;” obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 636 错误(活动) E0020 未定义标识符 "DPFLTR极IHVDRIVER_ID" obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 687 错误 C2065 “极”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 51 错误 C2653 “P极HookManager”: 不是类或命名空间名称 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 203 错误 C2065 “DPFLTR极IHVDRIVER_ID”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 276 错误 C2065 “DPFLTR极IHVDRIVER_ID”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 349 错误 C2065 “Low极”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 360 错误 C3536 “VaPt”: 初始化之前无法使用 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 364 错误 C2664 “void MmFreeContiguousMemory(PVOID): 无法将参数 1 从“int”转换为“PVOID” obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 365 错误 C2065 “VaPd极”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 368 错误 C2664 “void MmFreeContiguousMemory(PVOID): 无法将参数 1 从“int”转换为“PVOID” obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 377 错误 C2664 “void *memcpy(void *,const void *,size_t): 无法将参数 1 从“int”转换为“void *” obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 394 错误 C2664 “void *memcpy(void *,const void *,size_t): 无法将参数 1 从“int”转换为“void *” obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 397 错误 C2664 “UINT64 PteHookManager::fn_va_to_pa(void *): 无法将参数 1 从“int”转换为“void *” obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 409 错误 C2065 “DPFLTR极IHVDRIVER_ID”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 508 错误 C2065 “DPFLTR极IHVDRIVER_ID”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 524 错误 C2065 “DPFLTR极IHVDRIVER_ID”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 541 错误 C2065 “DPFLTR极IHVDRIVER_ID”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 549 错误 C2065 “m_HookInfo极i”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 636 错误 C2143 语法错误: 缺少“;”(在“]”的前面) obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 636 错误 C2059 语法错误:“]” obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 636 错误 C2065 “DPFLTR极IHVDRIVER_ID”: 未声明的标识符 obpcallback C:\Users\17116\source\repos\obpcallback\obpcallback\源.cpp 687
06-26
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值