開源專案之防火牆 tdifw
tdifw是windows防火牆軟體(TDI層驅動過濾),負責監控網路監聽與連線、以及過濾資訊。
原始碼在src目錄, 程式在Bin目錄,執行根目錄下的批處理檔案也可以,
具體步驟如下:
1. 執行install.bat
2. 根據你機器的配置情況,編輯%SystemRoot%\system32\drivers\etc\tdifw.conf配置檔案
3. 重新啟動計算機
主程式原始碼是win32的,就9個目標檔案,不包含驅動部分,專案如圖:
程式主要原始碼分析:
int main(int argc, char **argv) { static SERVICE_TABLE_ENTRY dispatch_table[] = { {"tdifw", service_main}, {NULL, NULL} }; _LEAK_CHECK; //模擬引數 argc = 3; argv[0]="tdifw"; argv[1]="install"; argv[2]="tdifw_drv.sys"; if (argc >= 2) { const char *param = argv[1]; if (strcmp(param, "install") == 0) { if (argc < 3) { fprintf(stderr, "Use: tdifw install <config>\n"); return -1; } //載入驅動服務 install_service(argv[2]); } else if (strcmp(param, "remove") == 0) { //移除驅動服務 remove_service(); } else if (strcmp(param, "debug") == 0) { if (argc < 3) { fprintf(stderr, "Use: tdifw debug <config>\n"); return -1; } if (start(argv[2])) { printf("press enter to exit...\n"); getchar(); printf("exiting...\n"); //停止 釋放資源 stop(); } } else if (strcmp(param, "listen") == 0) { // tdifw specific //列舉監聽 enum_listen(); } else if (strcmp(param, "conn") == 0) { // tdifw specific //列舉連線 enum_connect(); } else { fprintf(stderr, "Use: tdifw install|remove|debug|listen|conn\n"); } } else { g_console = FALSE; // 連線程式主執行緒到服務控制管理程式 if (!StartServiceCtrlDispatcher(dispatch_table)) winerr("main: StartServiceCtrlDispatcher"); } return 0; }
//獲得驅動檔案所在路徑 則開啟 否則退出 void install_service(const char *config) { SC_HANDLE schService; SC_HANDLE schSCManager; CHAR szPath[MAX_PATH]; //從登錄檔中獲得資訊 AddEventSource("tdifw"); if (GetModuleFileName(NULL, szPath, sizeof(szPath)) == 0) { winerr("install_service: GetModuleFileName"); return; } //建立了一個連線到服務控制管理器,並開啟指定的資料庫。 schSCManager = OpenSCManager( NULL, // machine (NULL == local) NULL, // database (NULL == default) SC_MANAGER_ALL_ACCESS); // access required if (schSCManager != NULL) { //建立一個服務物件並且把它加入到服務管理資料庫中 schService = CreateService( schSCManager, // SCManager database "tdifw", // name of service "TDI-based open source personal firewall", // name to display SERVICE_ALL_ACCESS, // desired access SERVICE_WIN32_OWN_PROCESS, // service type SERVICE_AUTO_START, // start type SERVICE_ERROR_NORMAL, // error control type szPath, // service's binary NULL, // no load ordering group NULL, // no tag identifier NULL, // dependencies NULL, // LocalSystem account NULL); // no password if (schService != NULL) { printf("tdifw service has been installed\n"); if (!add_config_info(schService, config)) fprintf(stderr, "Can't store config info! Service will use defaults.\n"); CloseServiceHandle(schService); } else winerr("install_service: CreateService"); CloseServiceHandle(schSCManager); } else winerr("install_service: OpenSCManager"); }
//移除服務 關閉驅動 void remove_service(void) { SC_HANDLE schService; SC_HANDLE schSCManager; schSCManager = OpenSCManager( NULL, // machine (NULL == local) NULL, // database (NULL == default) SC_MANAGER_ALL_ACCESS); // access required if (schSCManager != NULL) { schService = OpenService(schSCManager, "tdifw", SERVICE_ALL_ACCESS); if (schService != NULL) { // try to stop the service if (ControlService(schService, SERVICE_CONTROL_STOP, &ssStatus)) { printf("stopping..."); Sleep(1000); while(QueryServiceStatus( schService, &ssStatus)) { if (ssStatus.dwCurrentState == SERVICE_STOP_PENDING) { printf("."); Sleep( 1000 ); } else break; } printf("\n"); if (ssStatus.dwCurrentState == SERVICE_STOPPED) printf("stopped\n"); else printf("failed to stop\n"); } // now remove the service if (DeleteService(schService)) printf("service has been removed\n"); else winerr("install_service: DeleteService"); CloseServiceHandle(schService); } else winerr("install_service: OpenService"); CloseServiceHandle(schSCManager); } else winerr("install_service: OpenSCManager"); }
// 從驅動程式中獲得網路監聽物件
void enum_listen(void)
{
ULONG size;
struct listen_nfo *ln = NULL;
int i, n;
// 從 psapi.dll 中獲得連結EnumProcesses、EnumProcessModules、GetModuleFileNameExW函式地址
link_psapi();
/* connect with driver */
g_device = CreateFile(g_nfo_device_name, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (g_device == INVALID_HANDLE_VALUE) {
winerr(g_nfo_device_name);
goto done;
}
/* get list of listening objects */
size = sizeof(*ln) * 0x10000 * 3; // this size is good enough :-)
ln = (struct listen_nfo *)malloc(size);
if (ln == NULL) {
perror("malloc");
goto done;
}
//與驅動交流 列舉監聽操作 獲取監聽資訊
if (!DeviceIoControl(g_device, IOCTL_CMD_ENUM_LISTEN, NULL, 0,
ln, size, &size, NULL)) {
winerr("DeviceIoControl");
goto done;
}
n = size / sizeof(*ln);
// sort this list!
qsort(ln, n, sizeof(*ln), compare_ln);
printf("IPProto\tAddress:Port\tProcess (pid)\n");
printf("-------\t------------\t---------------------------------------------\n");
//顯示
for (i = 0; i < n ; i++) {
char *proto, pname[MAX_PATH];
if (ln[i].ipproto == IPPROTO_TCP)
proto = "TCP";
else if (ln[i].ipproto == IPPROTO_UDP)
proto = "UDP";
else if (ln[i].ipproto == IPPROTO_IP)
proto = "RawIP";
else
proto = "?";
// resolve pid!
if (!get_pname_by_pid(ln[i].pid, pname, sizeof(pname)))
pname[0] = '\0';
printf("%s\t%d.%d.%d.%d:%d\t%s (%d)\n",
proto, PRINT_IP_ADDR(ln[i].addr), ntohs(ln[i].port), pname, ln[i].pid);
}
done:
free(ln);
if (g_device != INVALID_HANDLE_VALUE)
CloseHandle(g_device);
}
// 從驅動程式中獲得網路連線資訊
void enum_connect(void)
{
ULONG size;
struct tcp_conn_nfo *tn = NULL;
int i, n;
unsigned __int64 traffic[TRAFFIC_MAX];
// 從 psapi.dll 中獲得連結EnumProcesses、EnumProcessModules、GetModuleFileNameExW函式地址
link_psapi();
/* connect with driver */
g_device = CreateFile(g_nfo_device_name, GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
if (g_device == INVALID_HANDLE_VALUE) {
winerr(g_nfo_device_name);
goto done;
}
/* get list of listening objects */
size = sizeof(*tn) * 0x10000 * 3; // this size is good enough :-)
tn = (struct tcp_conn_nfo *)malloc(size);
if (tn == NULL) {
perror("malloc");
goto done;
}
//與驅動交流 列舉監聽操作 獲取連線資訊
if (!DeviceIoControl(g_device, IOCTL_CMD_ENUM_TCP_CONN, NULL, 0,
tn, size, &size, NULL)) {
winerr("DeviceIoControl");
goto done;
}
n = size / sizeof(*tn);
// sort this list!
qsort(tn, n, sizeof(*tn), compare_tn);
//順序輸出
for (i = 0; i < n ; i++) {
char pname[MAX_PATH];
if (tn[i].state >= TCP_STATE_MAX)
tn[i].state = 0;
// resolve pid!
if (!get_pname_by_pid(tn[i].pid, pname, sizeof(pname)))
pname[0] = '\0';
printf("%s\t%d.%d.%d.%d:%d\t%d.%d.%d.%d:%d\t%s (%d)\t%u/%u\n",
g_tcp_states[tn[i].state],
PRINT_IP_ADDR(tn[i].laddr), ntohs(tn[i].lport),
PRINT_IP_ADDR(tn[i].raddr), ntohs(tn[i].rport),
pname, tn[i].pid,
tn[i].bytes_out, tn[i].bytes_in);
}
// output traffic counters
get_traffic_stats(traffic);
printf(
"\n"
"Traffic counters (out/in):\n"
" Total: %I64u/%I64u\n"
" Counted: %I64u/%I64u\n",
traffic[TRAFFIC_TOTAL_OUT], traffic[TRAFFIC_TOTAL_IN],
traffic[TRAFFIC_COUNTED_OUT], traffic[TRAFFIC_COUNTED_IN]);
done:
free(tn);
if (g_device != INVALID_HANDLE_VALUE)
CloseHandle(g_device);
}
以上是主要程式的原始碼,驅動部分共有32個目標檔案,如圖:
/* 驅動入口 */
NTSTATUS DriverEntry(IN PDRIVER_OBJECT theDriverObject,
IN PUNICODE_STRING theRegistryPath)
{
NTSTATUS status = STATUS_SUCCESS;
int i;
UNICODE_STRING name, linkname;
//記憶體跟蹤初始化 呼叫了KeInitializeSpinLock(&guard);
memtrack_init();
//初始化鎖
KeInitializeSpinLock(&g_traffic_guard);
#ifdef USE_TDI_HOOKING
KdPrint(("[tdi_fw] WARNING! Using unstable working mode: TDI hooking!\n"));
#endif
status = ot_init();
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: ot_init: 0x%x\n", status));
goto done;
}
//過濾器初始化
status = filter_init();
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: filter_init: 0x%x\n", status));
goto done;
}
//連線狀態初始化
status = conn_state_init();
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: conn_state_init: 0x%x\n", status));
goto done;
}
//分發函式
for (i = 0; i < IRP_MJ_MAXIMUM_FUNCTION; i++)
theDriverObject->MajorFunction[i] = DeviceDispatch;
#if DBG
// register UnLoad procedure
theDriverObject->DriverUnload = OnUnload;
#endif
/* create control device and symbolic link */
RtlInitUnicodeString(&name, L"\\Device\\tdifw");
status = IoCreateDevice(theDriverObject,
0,
&name,
0,
0,
TRUE, // exclusive!
&g_devcontrol);
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: IoCreateDevice(control): 0x%x!\n", status));
goto done;
}
RtlInitUnicodeString(&linkname, L"\\??\\tdifw");
//建立了一個符號連線
status = IoCreateSymbolicLink(&linkname, &name);
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: IoCreateSymbolicLink: 0x%x!\n", status));
goto done;
}
RtlInitUnicodeString(&name, L"\\Device\\tdifw_nfo");
//建立裝置物件
status = IoCreateDevice(theDriverObject,
0,
&name,
0,
0,
FALSE, // not exclusive!
&g_devnfo);
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: IoCreateDevice(nfo): 0x%x!\n", status));
goto done;
}
RtlInitUnicodeString(&linkname, L"\\??\\tdifw_nfo");
//建立了一個符號連線
status = IoCreateSymbolicLink(&linkname, &name);
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: IoCreateSymbolicLink: 0x%x!\n", status));
goto done;
}
#ifndef USE_TDI_HOOKING
//繫結裝置
status = c_n_a_device(theDriverObject, &g_tcpfltobj, &g_tcpoldobj, L"\\Device\\Tcp");
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x\n", status));
goto done;
}
//繫結裝置
status = c_n_a_device(theDriverObject, &g_udpfltobj, &g_udpoldobj, L"\\Device\\Udp");
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x\n", status));
goto done;
}
//繫結裝置
status = c_n_a_device(theDriverObject, &g_ipfltobj, &g_ipoldobj, L"\\Device\\RawIp");
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: c_n_a_device: 0x%x\n", status));
goto done;
}
#else /* USE_TDI_HOOKING */
/* get device objects for tcp/udp/ip */
//獲得tcp裝置物件
status = get_device_object(L"\\Device\\Tcp", &g_tcpfltobj);
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: get_device_object(tcp): 0x%x\n", status));
goto done;
}
//獲得Udp裝置物件
status = get_device_object(L"\\Device\\Udp", &g_udpfltobj);
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: get_device_object(udp): 0x%x\n", status));
goto done;
}
//獲得RawIp裝置物件
status = get_device_object(L"\\Device\\RawIp", &g_ipfltobj);
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: get_device_object(ip): 0x%x\n", status));
goto done;
}
/* hook tcpip */
//針對tcp下鉤子
status = hook_tcpip(&g_old_DriverObject, TRUE);
if (status != STATUS_SUCCESS) {
KdPrint(("[tdi_fw] DriverEntry: hook_driver: 0x%x\n", status));
goto done;
}
g_hooked = TRUE;
#endif /* USE_TDI_HOOKING */
status = STATUS_SUCCESS;
done:
if (status != STATUS_SUCCESS) {
// cleanup
OnUnload(theDriverObject);
}
return status;
}
針對tcp或udp、ip等裝置物件時的操作,操作分兩種:裝置過濾模式 和 TDI Hook模式。原始碼如下:
//TDI Hook過濾模式
case IRP_MJ_CREATE: /* create fileobject */
result = tdi_create(irp, irps, &completion);
status = tdi_dispatch_complete(DeviceObject, irp, result,
completion.routine, completion.context);
break;
case IRP_MJ_DEVICE_CONTROL:
KdPrint(("[tdi_fw] DeviceDispatch: IRP_MJ_DEVICE_CONTROL, control 0x%x for 0x%08X\n",
irps->Parameters.DeviceIoControl.IoControlCode, irps->FileObject));
if (KeGetCurrentIrql() == PASSIVE_LEVEL) {
/*
* try to convert it to IRP_MJ_INTERNAL_DEVICE_CONTROL
* (works on PASSIVE_LEVEL only!)
*/
status = TdiMapUserRequest(DeviceObject, irp, irps);
} else
status = STATUS_NOT_IMPLEMENTED; // set fake status
if (status != STATUS_SUCCESS) {
void *buf = (irps->Parameters.DeviceIoControl.IoControlCode == IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER) ?
irps->Parameters.DeviceIoControl.Type3InputBuffer : NULL;
// send IRP to original driver
status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, NULL, NULL);
if (buf != NULL && status == STATUS_SUCCESS) {
g_TCPSendData = *(TCPSendData_t **)buf;
KdPrint(("[tdi_fw] DeviceDispatch: IOCTL_TDI_QUERY_DIRECT_SEND_HANDLER: TCPSendData = 0x%x\n",
g_TCPSendData));
*(TCPSendData_t **)buf = new_TCPSendData;
}
break;
}
// don't break! go to internal device control!
case IRP_MJ_INTERNAL_DEVICE_CONTROL: {
/*
* Analyze ioctl for TDI driver
*/
int i;
for (i = 0; g_tdi_ioctls[i].MinorFunction != 0; i++)
if (g_tdi_ioctls[i].MinorFunction == irps->MinorFunction) {
#if DBG
// print description
KdPrint(("[tdi_fw] DeviceDispatch: %s (0x%x) for 0x%x\n",
g_tdi_ioctls[i].desc,
irps->MinorFunction,
irps->FileObject));
#endif
if (g_tdi_ioctls[i].fn == NULL) {
// send IRP to original driver
status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW,
NULL, NULL);
break;
}
// call dispatch function
result = g_tdi_ioctls[i].fn(irp, irps, &completion);
// complete request
status = tdi_dispatch_complete(DeviceObject, irp, result,
completion.routine, completion.context);
break;
}
// if dispatch function hasn't been found
if (g_tdi_ioctls[i].MinorFunction == 0) {
// send IRP to original driver
status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW, NULL, NULL);
}
break;
}
case IRP_MJ_CLEANUP: /* cleanup fileobject */
result = tdi_cleanup(irp, irps, &completion);
status = tdi_dispatch_complete(DeviceObject, irp, result,
completion.routine, completion.context);
break;
case IRP_MJ_CLOSE:
KdPrint(("[tdi_fw] DeviceDispatch: IRP_MJ_CLOSE fileobj 0x%x\n", irps->FileObject));
// passthrough IRP
status = tdi_dispatch_complete(DeviceObject, irp, FILTER_ALLOW,
completion.routine, completion.context);
break;
//裝置過濾操作模式
if (irps->MajorFunction == IRP_MJ_CREATE) {
// initialize for user-mode part (exclusive access - 1 user-mode logging part)
filter_init_2();
g_got_log = TRUE;
} else if (irps->MajorFunction == IRP_MJ_CLOSE) {
// cleanup for user-mode logging part
filter_free_2();
g_got_log = FALSE;
} if (irps->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
/*
* control request
*/
ULONG ioctl = irps->Parameters.DeviceIoControl.IoControlCode,
len = irps->Parameters.DeviceIoControl.InputBufferLength,
size = irps->Parameters.DeviceIoControl.OutputBufferLength;
char *out_buf;
if (IOCTL_TRANSFER_TYPE(ioctl) == METHOD_NEITHER) {
// this type of transfer unsupported
out_buf = NULL;
} else
out_buf = (char *)irp->AssociatedIrp.SystemBuffer;
// process control request
status = process_request(ioctl, out_buf, &len, size);
irp->IoStatus.Information = len;
}
irp->IoStatus.Status = status;
IoCompleteRequest(irp, IO_NO_INCREMENT);
學習的目的是成熟!~
相關推薦
開源專案之防火牆 tdifw
tdifw是windows防火牆軟體(TDI層驅動過濾),負責監控網路監聽與連線、以及過濾資訊。 原始碼在src目錄, 程式在Bin目錄,執行根目錄下的批處理檔案也可以, 具體步驟如下: 1. 執行install.bat 2. 根據你機器的配置情況,編輯%SystemR
開源專案之Android imifirewall(防火牆)
imifirewall(艾米防火牆)是一款Android下的防火牆軟體,它主要用來幫助手機使用者解決日常使用中遇到的問題:電話騷擾,釣魚簡訊,手機垃圾,系統變慢等眾多問題,艾米防火牆暫時只支援簡訊過濾,來電攔截,程式聯網行為攔截和流量檢視等功能,以後會陸續新增更多實用的功能
開源專案之架構分享
此次分享是我當初在開發某個系統時,參考的一些開源專案架構的思路和風格。 第一個是Jeesite,它的架構風格如下: 大家如果對jeesite感興趣的話,可以百度搜索找到,不過那已經是半年多以前的事情,jeesite目前也發生較大的變化。 當初我在參考jessite
[譯] 開源專案之 Nginx
原文地址:The Open Source Project Nginx 原文作者:Andrew Alexeev 譯文出自:掘金翻譯計劃 本文永久連結:github.com/xitu/gold-m… 譯者:razertory 校對者:yqian1991 ng
Android入門開源專案之仿開眼視訊APP
開眼短視訊(OpenEyes) 仿照(開眼視訊)Android端(舊版UI,新版UI已改變)做的一個App,每天更新一個精美短視訊應用,一個非常美的短視訊應用,UI介面基本上是參照開眼視訊Android端來做的。 在該專案中,我採用的是Vitamio的視訊播放器框架
開源專案之GIF Animation Control(顯示 GIF 動畫的 MFC 元件)
GIF Animation Control 是用來在介面上顯示 GIF 動畫的 MFC 元件。 基於MFC的簡單程式設計模型,您可以使用 GIF Animation Control 來在介面上顯示 GIF 動畫的 MFC 元件,來建立具有動畫
Docker 開源專案之 registry
原文地址 在部署 registry 之前需要現在主機上安裝 Docker。registry 實際上就是執行在 Docker 中的 registry 映象的例項。 本主題提供關於部署和配置 registry 的基本資訊。要檢視配置選項列表,請參考 配置手冊。
深入解析開源專案之Universal-Image-Loader(二)快取篇
Universal-Image-Loader 是一個優秀的圖片載入開源專案,Github地址在 (Github地址) ,很多童鞋都在自己的專案中用到了。優秀的專案從來都是把簡單留給開發者,把複雜封裝在框架內部。ImageLoader作為Github上S
開源專案之跨平臺的詞典軟體 GoldenDict
GoldenDict 是一款不錯的、與 StarDict(星際譯王)類似的詞典軟體。它使用 WebKit 作為渲染核心,格式化、顏色、影象、連結等支援一應俱全;支援多種詞典檔案格式,包括 Babylo
BaseRecyclerViewAdapterHelper開源專案之BaseMultiItemQuickAdapter 實現多型別原始碼學習
version:2.8.5 今天我們來看下BaseRecyclerViewAdapterHelper是如何實現多佈局的。 首先我們要實現多型別佈局,我們的adapter不再是繼承自BaseQuickAdapter類,而是繼承自其的子類 BaseMultiItemQui
Android開源專案之Music — Android的MediaPlayer架構介紹 + 補充
3.4 media服務libmediaservice.so frameworks/base/media\libmediaplayerservice目錄中的MediaPlayerService.h和MediaPlayerService.cpp用於實現一個 servers/media/的服務,MediaPla
個人python練手專案之微報命令列版V1.0開源
前言 還記得不久之前,寫過一篇關於團隊管理工具的調研文章傳送門,當時調研了一大圈發現對於管理層來說最大的痛點就是沒有一個現成的工具支援word週報的匯出,傳統企業還是比較偏向紙質檔案的。再加上想練練python說動手就動手,於是就有了這個專案。 專案定位算是練練手+解決自身痛點。現
開源專案學習之------AG-Admin環境搭建
一、框架簡介 AG-Admin是基於Spring Cloud微服務化開發平臺,具有統一授權、認證後臺管理系統,其中包含具備使用者管理、資源許可權管理、閘道器API管理等多個模組,支援多業務系統並行開發,可以作為後端服務的開發腳手架。程式碼簡潔,架構清晰,適合學習和直接專
【大牛之路】大牛指導,報酬豐厚的開源專案---“谷歌程式設計之夏”
正文 What 這個問題,我們先來看看 Google 官方是怎麼說的:(出自 What is Google Summer of Code?) Google Summer of Code (GSoC) is a global progr
Github使用之Pull Request的正確開啟方式(如何在GitHub上貢獻開源專案)
GitHub的官方幫助如下: 立topic branch),還有PullRequest的運作細節也沒有提到。寫個簡單的總結補充一下。 Step 1: Fork原專案 這個不解釋了,單擊一下滑鼠就能做到的事
Android 之 Telegram 開源專案
使用 Android Studio 來編譯執行(第一次使用 Android Studio 來執行正式專案) 先按指引上 Telegram 官網申請 app id 等資訊。 然後,就可以正常啟動了。 對於我們來說,裡面太多可以學習的東西了: 基礎元件:Android
.NET平臺開源專案速覽-最快的物件對映元件Tiny Mapper之專案實踐
心情小札:近期換了工作,苦逼於22:00後下班,房間一篇狼藉~ 小翠鄙視到:"你就適合生活在垃圾堆中!!!" 看評論也是挺有價值,同時也看到許多新手同學問道在實際專案中使用的情況。 下面就原作者的程式碼的基礎上略作調整,闡述一下在實際專案場景中的使用: 第一步:瞭解類庫方法:TinyMapper 主
.NET Core微服務之開源專案CAP的初步使用
一、CAP簡介 CAP 是一個在分散式系統中(SOA,MicroService)實現事件匯流排及最終一致性(分散式事務)的一個開源的 C# 庫,她具有輕量級,高效能,易使用等特點。我們可以輕鬆的在基於 .NET Core 技術的分散式系統中引入CAP,包括但限於 ASP.NET Core 和
Android專案:第三方Jar包proguard混淆之常見開源專案混淆配置
9、pinyin4j -dontwarn net.soureceforge.pinyin4j.** -dontwarn demo.** -libraryjars libs/pinyin4j-2.5.0.jar -keep class net.sourceforge.piny
Android開源專案推薦之「最好用的Log」
1 Android Log 今天在微信公眾號,看到了張神的推文,受益不少,所以把他分享到這裡,若要看原文,可以到該網站: http://mp.weixin.qq.com/s?__biz=MzA4NTQwNDcyMA==&mid=26